For some godawful reason, people like to explain object oriented concepts via analogy to biological taxonomies. I suppose it's easy to understand an "is-a" relationship (a cat is a mammal), but the down side is you get a complex and increasingly leaky analogy that over-emphasizes the importance of inheritance.
That's not to slight Alan's choice of using it to anoymize today's code. It's just a broad frustration on my part.
In any case, Alan has been working in a C++ codebase that's written by people who maybe don't fully understand all the key object oriented concepts. That's why you get code like this:
/*
* If possible, steal tentacle from the given animal.
*/
void Animal::StealTentacle(Animal *animal) {
// only Octopus and Squid can have tentacles
if (!IS_INSTANCE_OF(this, Octopus) && !IS_INSTANCE_OF(this, Squid)) {
return;
}
Limb *limb = animal->mLimbs->GetFirstTentacle();
if (limb) {
limb->AttachToAnimal(this);
// . . .
This function takes a raw pointer to an Animal object. If that pointer is not an Octopus or a Squid, we bail. Otherwise, we're going to take its tentacle, and do stuff with it.
Now, this is a pretty clear example of the type switch anti-pattern. Instead of using the type system and polymorphism to enforce restrictions, we instead do runtime type checks.
But wait a second, what's going on in that IS_INSTANCE_OF?
// I've noticed that dynamic_cast always returns a valid result when
// casting an "objectPointer" of type "className".
// Try casting it to a [Organism *] to force it to work. (?)
#define IS_INSTANCE_OF(objectPointer, className) ((bool) (dynamic_cast<className *>(((Organism *) objectPointer)) != NULL))
#define IS_MEMBER_OF(objectPointer, className) IS_INSTANCE_OF(objectPointer, className) // temporary macro alias
This code attempts to take an objectPointer and first, force it into a pointer to our base type (Organism), using a C-style cast. Now, in C++, C-style casts are a shorthand, and the compiler will, based on the context, attempt to do the best cast which fits the situation, starting with a const_cast (which only alters the constness of the type), and failing all the way up to a reinterpret_cast- which just ignores type information entirely and assumes you know what you're doing.
Which this developer clearly doesn't.
In any case, we pass the C-style cast result into the dynamic_cast function, which attempts to cast that pointer into an instance of className. If no such conversion is possible (because they're not part of the same inheritance hiercharcy), it returns NULL, which is what they're checking.
Their comment highlights their misunderstanding of the function; dynamic_cast can be used to crosscast. In other words, dynamic_cast<Dog*>(my_cat) will actually work, because (in this example) Dog* and Cat* share a common base.
From the docs:
Otherwise, if expression points/refers to a public base class subobject of the most derived object, and the type of the most derived object has an unambiguous and public base class of type Target, the result points/refers to the Target subobject of the most derived object. In other words, dynamic_cast can be used to crosscast (or side-cast) pointers/references, between two types derived from the same base.
You shouldn't be using dynamic cast to solve this problem. Hell, you sohuldn't be doing any of this in the first place!
In any case, in actual usage, the developers responsible do stuff like this:
if (IS_INSTANCE_OF(animal, Felid)) {
Felid *cat = (Felid *) animal; // Yes another C-style cast - this time (hopefully) devolving to the same dynamic_cast hidden inside the expansion of the previous macro.
I love when a comment says "hopefully". "This hopefully does what I think it should do".
I'll let Alan summarize the problems:
Used prudently, the dynamic dispatch facility that object oriented languages provide their users can be a valuable tool for capturing the domain model in clean, elegant code. However, as even C++'s designer Bjarne Stroustrup acknowledged, in "The Design and Evolution of C++", the introduction of support for Run-Time Type Information makes it possible to spurn such assistance and write RTTI-based code for which a verdict of "combines the syntactic elegance of C with the run-time efficiency of Smalltalk" is just "too kind".