Reputation: 16499
Is there a standard (or at least safe) way to compare unions for equality in C and/or C++? I expect that bitwise comparison would be useful in a number of scenarios regardless of the last-assigned member in each union; for instance, a particular bit-pattern could be reserved to mean "value is uninitialized", and it would be useful to be able to check if the union is uninitialized without needing to specify an "active" member.
An example in C++ (though I think the concept extends to C using non-member functions):
union MyData
{
public:
// Assume I'm compiling this on a platform where the size of `int`
// exceeds the size of `char*`, or that I'm using some combination
// if `#ifdef`s or similar to ensure that my numeric type is sufficiently
// large, or that I include an extra member that is known to be
// exactly the size of the larger member, just for the sake of
// comparison.
int int_member;
char* ptr_member;
bool isInitialized() const
{
return (*this != INVALID_VAL);
}
bool operator==(MyData const& rhs)
{
return / * ??? */;
}
private:
constexpr MyData INVALID_VAL { /* ??? */ };
}
// ... later, in client code...
MyData d;
bool initialized{d.isInitialized()}; // false
d.ptr_member = new char[32];
bool initialized{d.isInitialized()}; // true
Here, INVALID_VAL
could probably be defined by setting int_member
to the max negative int value, because that's an uneven value, so it won't be on a word boundary and therefore is highly unlikely to ever be assigned to the char*
member (assuming that assignments typically come directly from new
).
One possible implementation of operator==
would be simply:
return int_member == rhs.int_member;
Even though it's not known whether int_member
is the "active" member, I expect this to be safe, because I see no reason why a static cast from char*
to int
should fail or be problematic. Is that correct?
If this implementation is unsafe, something like the following should be possible (using C-style casts in C, of course):
return static_cast<void*>(*this) == static_cast<void*>(rhs);
...though of course if MyData
is larger than the size of a pointer, you'd have to start messing around with sizeof
to make this work.
Does anyone do this? Is the first (simpler) implementation safe? Is there any reason not to do it?
Upvotes: 2
Views: 2209
Reputation: 362
I think a better approach would be to wrap your union inside a class or struct with an enum field storing which was the last member accessed e.g.
class MyData {
enum {
unintialized, int_member, ptr_member
} last_member = unintialized;
union {
int int_member;
char* ptr_member;
} union_fields;
public:
bool isInitialized() const
{
return last_member != unintialized;
}
};
The in class initialization of last_member works if you have C++11 otherwise just initialize it in the default constructor.
Create accessors for the two fields and set last_member
accordingly, it would also be good to add checks in the accessor methods making sure only the "active member" can be accessed.
Upvotes: 3
Reputation: 399949
Of course it's unsafe.
You can't assume that an int
has the same size as a char *
, for instance. There might also be padding, which is often random in content.
Upvotes: 1