@jonnin,
I think you misunderstood the context of that quote. I say that because in your discussion, you don't cover a union of doubles and longs.
Did you, for example, see the union Pixel example? It does what you're describing here.
What I was pointing out is that examining the
bits of a long has no utility when cast to a double, or the reverse, though I go on to point out that the one thing examining the bits of a double cast as a long (well, 64 bit integer, longs are not universally the same size) could be used to use "bit fiddling" to determine if the double is a NAN or INF - which is actually the kind of thing you're saying is done.
I'm not actually clear that we disagree at all.
Unions are used, as I put it earlier, to "pre-cast" multiple "views". I'm not sure how that disagrees.
My point is that something like the old variant union Microsoft provided for Visual Basic interfacing offered several useless "views" given certain contexts. Variant, for those who don't remember, was a huge union with every basic type included. VB used a type of variable storage that could be just about anything - a single variable that didn't really have a type per se, but could be any type. The variant union had "views" for everything from integers to doubles to string pointers. The programmer merely had to pick the right view.
So, if the variant offered a "double" view, but the underlying value was actually an int, looking at the variable as an "int" made no sense.
As @JLBorges, technically some unions provided undefined behavior. The Pixel union above, for example, works as expected, because there is no expectation that the 32 bit value has any meaning as an integer, but as a package of 4 color channels which can be moved as a unit. C++ does nothing to guarantee what the integer would look like, so there is "undefined" behavior where that integer is used as an integer and not 4 color channels. Indeed, the value of that integer for a given pixel would change on different platforms over endianness.
The problem with many unions is exactly that point. There are some few arrangements, as @JLBorges points out, that exhibit fully defined behavior. Many (perhaps most) unions exhibit undefined behaviors, like those I point out where the union provides incompatible or unstable views of the same bits.
@ne555's point, which started this segment of the thread, was to suggest the
reason for undefined behavior was merely the order of access. From that post
is undefined behaviour, as you are not accessing the last set member, but another one. |
So, this suggests that unions can only be used, with defined behavior, if the last set "view" is the one read later.
This isn't accurate because that isn't the reason for the undefined behavior. The undefined behavior must be understood in the context of what those two different views are, not merely that they are not the same view.
My counterpoint (not really yet posted after @JLBorges) is that while there are technical merits to calling some union usage as "undefined" behavior, it isn't in that class of "undefined" where expect serious crashes, like deleting on a pointer twice, or running off the end of an array.
Like you said, sometimes the union expresses an intent, which ignores the well defined behaviors of C/C++, precisely because of the nature of the goal, like touching various bits that are otherwise even more work (with casting and pointer 'magic') to accomplish.
The Overlapped structure at the top of this discussion was Microsoft specific notion, as was the "large integer" union they used for years are an example of both points at once, the notion of utility of this thing and of undefined behavior associated with it.
The large integer union I posted above is a bit clearer example on the point. Microsoft has been implemented mostly on x86 platforms, but there was a PowerPC version for a time, which has opposing endianness. Some code using the large integer union would likely have different behavior on those PowerPC versions, due to the fact C/C++ does not define the order of the bits in 64 vs 32 vs 16 vs 8 bit components. Code which built up a 64 bit integer using that union would work find on any x86 platform, but when switching to a target of opposing endianness, the code wouldn't make much sense.