Part 1 of this series looked at how some of the core features of C++ work in open source implementations. In this article, we'll continue our exploration with some of the more dynamic features of the language.
As in the previous article, everything discussed here is specific to the Itanium C++ ABI used by most *NIX systems, unless otherwise stated.
Runtime Type Information
A few of the features of C++, including exception handling, depend on the existence of runtime type information (RTTI), which means being able to interrogate an object for its type, and manipulate types as if they were objects.
This is one of the interesting places where the divide between the compiler and the standard library isn't quite clear. When you use the typeid operator, or use RTTI in some other way, the compiler will be generating instances of std::type_info, or a (typically hidden) subclass. These subclasses are usually defined by the ABI specification, if one exists, or by the toolchain vendor otherwise.
Runtime type information is also used with the dynamic_cast<> template. This uses the real type of the class to find out whether it can be cast, letting you safely cast from a superclass pointer to the subclass, for example.
The type_info structure typically includes just one field, which is a pointer to a (C) string containing the type encoding. The interesting behavior is typically implemented in private subclasses, which add some other data. For example, the type info class used for pointer types will hold a pointer to the type info class for the pointee, typically along with some flags indicating the kind of pointer (const, volatile, and so on). For classes, these structures will contain pointers to the superclass, and typically offsets for casting. They typically provide virtual functions for performing casting, although these virtual functions may not be exposed in the header.
When you use a dynamic_cast, the compiler will emit a call to the function that performs the pointer manipulation. The Itanium ABI takes the original pointer, the source and destination type infos, and a hint. The compiler then compares the two type infos, typically by calling a virtual function like __cast_to() on one, with the other as an argument, returning the new pointer if the cast is possible.
The dynamic cast is one of the features of C++ that falls into the general category of things that make it difficult to judge the performance of a piece of code. If you see something like a = dynamic_cast<B>(c), how fast will it run? Well, if the compiler knows the type of c, it will probably emit a simple pointer arithmetic sequence for this. In some cases (a cast to a single-inheritance superclass, for example), this is a no-op. In other cases, the compiler will emit a call to a runtime function that then has to walk a hierarchy of data structures via several virtual function calls. The exact complexity depends on the type hierarchy.