Tricking a C++ Union into Having Methods
I've got an interesting problem that I sure wish I'd checking into the linux C libraries a lot more before getting too far into this, but so it goes... It's the old UUID problem. It's a 16-byte, unsigned number, but it's far too big to hold in one atomic data element, so it's often a union:
union UUID { uint8_t bytes[16]; uint16_t words[8]; uint64_t blocks[2]; };
but the problem with this union is that you can't do something like this:
UUID a; UUID b; a = b;
or:
UUID a; UUID b; // ... if (a = b) { // do something... }
C++ just doesn't allow for the default operator=() or operator==() overloading like it does the default constructor, the default (shallow) copy constructor, etc. You have to build these things into a class.
However, there is one nice thing that these unions allow: it's the un-biased use of the union as the start of it's member data. For example, if we have:
UUID a; // ... memcpy(&a, src, 16);
we can write the 16-bytes of data into the union's elements by simply referencing the UUID itself. This is not the case with a class. There's the vtable, and that ends up throwing off all the offsets for the ivar data.
But what can a guy do to get these methods on a union?
The Answer is really the anonymous union:
struct UUID { union { uint8_t bytes[16]; uint16_t words[8]; uint64_t blocks[2]; }; // the constructor/destructor set... UUID(); UUID( const UUID & anOther ); UUID & operator=()( UUID & anOther ); UUID & operator=()( const UUID & anOther ); // the equality/inequality operators... bool operator==( const UUID & anOther ) const; bool operator!=( const UUID & anOther ) const; bool operator<( const UUID & anOther ) const; bool operator>( const UUID & anOther ) const; bool operator<=( const UUID & anOther ) const; bool operator>=( const UUID & anOther ) const;
where I added in the inequality operators so the UUID could be used in a std::map as a key. The problem now is that I'd really love to have the following still work:
UUID a; // ... memcpy(&a, src, 16);
but it won't. I have to reference the first part of the data to get past the vtable, etc.
UUID a; // ... memcpy(&a.blocks[0], src, 16);
It's not horrible, but it's no better than really building a class. I got the syntax I wanted, but the consequences weren't without their costs. Kind of bumming to get it all going and then remember the vtable, but that's life.