Re: Type-punning / casting problem
Ian Collins wrote:
Phil Endecott wrote:
Dear Experts,
I need a function that takes a float, swaps its endianness (htonl) in
place, and returns a char* pointer to its first byte. This is one of a
family of functions that prepare different data types for passing to
another process.
I have got confused by the rules about what won't work, what will work,
and what might work, when casting. Specifically, I have an
implementation that works until I remove my debugging, at which point
the compiler seems to decide that it can optimise away the writes to the
bytes other than the first, or something like that. Here it is:
template <typename T>
inline const char* encode_arg(T& t); // linker error if you try to
// encode a type for which there
// is no implementation
// This one works:
template <>
inline const char* encode_pq_arg<int>(int& i) {
Typo? ^^
Yes, copy&pastism, sorry!
i = htonl(i);
return reinterpret_cast<const char*>(&i);
}
Did you intend the bytes of the passed value to be swapped?
Yes; the caller doesn't need the value any more, so I swap it in-place
rather than making a copy. If I made a copy then I would have to
allocate memory for it and free it later.
// This one doesn't:
template <>
inline const char* encode_arg<float>(float& f) {
uint32_t* ptr = reinterpret_cast<uint32_t*>(&f);
*ptr = htonl(*ptr);
const char* cptr = reinterpret_cast<const char*>(ptr);
return cptr;
}
When I dump cptr[0] to cptr[3] before the return, it works. Without the
debug, it fails; it's obviously hard to see what ends up in the result
in that case, but it looks undefined.
What doesn't work?
The four bytes pointed to by the const char* that this function returns
eventually get sent to a socket, and I can observe them at the other
end. They are wrong, i.e. if I pass 2.0 then the value at the other end
might be 8.923461290e-44. There is some non-determinism in the values I
see, which makes me think that I'm looking at uninitialised memory
rather than a systematic corruption. My first thought was to add
somethng like this:
std::cout << "cptr[0] = " << static_cast<int>(cptr[0]) << ....etc for
[1] to [3] ... << "\n";
However, as soon as I add this (just before the return statement), it
works: the correct value is seen in the other process. My feeling is
that the cptr[n] expressions in the debugging tell the compiler that
these bytes are needed; when the debuging is not there, it thinks that
they are not used, and optimises them away. Can you think of any other
explanation?
Regards,
Phil.