Storing multiple types for a smartpointer
I've had a pretty good smart pointer going for a while in my C++ projects. The pointer consists of two main classes. The template class that you assign your objects to and a baseclass that this template derives from. The baseclass stores a static multimap of all the objects and which pointers are pointing to them (object->pointer).
It stored the objects simply as a void* and compared the addresses of the objects to decide if they were in fact the same object. By doing this, the pointers could drop an object and let every other pointer pointing to the same object know if they still pointed to something valid or not. It could also just let all the pointers go out of scope and automatically clean up the dangling object when the last pointer was destroyed.
Oh it was green hills and lush forests until the beast that is multiple inheritance came along. Using the pointers on base classes hadn't been a problem before. It would still realize that it was the same object. After a good debug run, I realized that the same object gave off two different addresses!
Subobjects
C++ figures out that these guys in fact belong to the same object even though they can have a different address, as long as you keep the type pointer. If you for some reason, like casting to a void*, lose the type, it can no longer relate a subobject's address to it's actual full object.
My instinct was to not let the type go away. Templates must be the solution I though. I made a baseclass that would virtually pass along the comparison needed to a templated subclass where the type was stored. I tangled with the code for hours trying to get the templates to compare directly to each other. Unfortunately, templating doesn't work on virtual functions, neither at the base nor the derived. So I was at a loss I thought, and started considering living with it and simply avoid using multiple inheritance.
I took a break and relaxed, which I should've done hours earlier. Things have a tendency of clearing up when you stop thinking about them so intensely.
The wrong Solution
So it suddenly struck me! I actually only need ONE of the types directly to figure it out. To sort the multimap, the operator < is used. I was stuck with using void* < void* or just static_cast<T*>(voidPointer) < voidPointer2, which gave the same result. But naturally, the objects does have a size, and I know that size if I know it's type and can then check if an address lies within the object! If it does, it's the same object.
So the sollution in short: (char*)voidPointer+sizeof(T) <= voidPointer2
Casting to a char* gives the object a size of basic machine units, which is 8bits, or 1 byte. A void* has no size and sizeof returns the size in machine units. This result in the address past the end of the object. So to compare an object as being greater than this object, it must be allowed to begin at the past end address, hence the less or equal operator. I also defined the operator > which just tests the addresses plainly. When the actual comparison is made by the baseclass, it uses both comparisons, since it will give different results if the subobject or the "root" address is on the left side. So the return looks like: Address1 < Address2 && Address2 > Address1
The final solution (a month later)
There was one single case where the old solution didn't work. Comparing two subobjects of the same complete object didn't result in equality, since the two subobjects didn't overlap (address+size).
I once again went on to find a better solution. I knew that the vtable of a subobject kept an offset to the complete object, but there was no obvious way of reaching it. Hacking the vtable is a bad idea and is highly implementation dependent.
I finally gave up in figuring it out myself and consulted the good people at stack overflow. The answer was so simple that I could barely believe it.
dynamic_cast can actually give you the address to the most derived, the complete object, by using it with void*: dynamic_cast<void*>(mySubObject)
This simple realization let me clean out my complex templated address class and simply cast to void* and make a straight comparison to establish object identity.
I must say that this could have been mentioned with an extra sentence on cplusplus.com.
dynamic_cast can also cast null pointers even between pointers to unrelated classes, and can also cast pointers of any type to void pointers (void*).











