Runtime Type Checking in C++ without RTTI

A technique I always seem to forget is how to map C++ types to an integer without relying upon RTTI. A variation on this is used in <locale> in standard library, for std::use_facet<>. But let’s take a much simpler, and highly contrived, example.

Let’s say we’ve got some values of different types, and we want to give those types to a library to store somewhere, and then we later want to get them back again. Crucially, the library itself doesn’t know anything about the types in question. So, for a very simple case:

This works, but has a major flaw: if you get the types wrong when calling Something.as<>, you’ll get a segfault or something similarly horrible. We’d like to replace that with something safer.

One way to do it is to use runtime type information. The simplest variation on this is to replace the static_cast with a dynamic_cast. However, we can only do this if SomethingValueBase is a polymorphic type, which it isn’t. We can make it so by adding in a virtual destructor:

Here we make use of typeid explicitly, which is widely considered to be about on par with use of goto. However, it paves the way for our next step. Can we replace typeid(SomethingValueType<T_>()).name() with a different, non-evil expression? Let’s think about what properties the result of that expression must have:

We must be able to store it, so it needs to be a regular type.

We must be able to compare values of it, and be guaranteed true if and only if the two types used to create the value are the same, and false if and only if they are different. (Note that RTTI doesn’t even provide this guarantee.)

No RTTI at all there, and it is type safe, but it relies upon a lot of boilerplate from the library user, and that boilerplate is very easy to screw up. So, we’ll allocate magic numbers automatically instead:

How does this work? Each instantiation of the magic_number_for<T_> function needs to return the same magic number every time it is called. The first time any particular instantiation is called, its static int result requests the next magic number. On subsequent calls, the allocated number is remembered. (Note that static values inside a template are not shared between different instantiations of that template.) Finally, next_magic_number just returns a new magic number every time it is called.

And there we have it: fast runtime type checking with no boilerplate and no RTTI. What we’ve done here is more or less useless, but the techniques do have other applications. For the curious, std::use_facet<> is probably the most common, and anyone brave enough to delve into its design will eventually see why this isn’t either pointless wankery or reinventing the wheel. For the rest, if you think that using RTTI can solve your problem adequately, then it probably can, and you don’t need to go into the kind of devious trickery the standard library uses internally.

Related

10 responses to “Runtime Type Checking in C++ without RTTI”

Since you seem concerned with performance, you should strongly consider using make_shared instead of initializing your shared_ptr with a “new” expression. You avoid a superfluous hit to the heap if you use make_shared, because make_shared can package the ref count with the object being ref counted. Also note that your “as” function will almost never be inlined, because it contains a throw statement.

I don’t think this solution is thread safe, as your static “magic” isn’t protected in any way. Also, the numbers you are producing are probably not useful for serialization, as the order of object construction could change which magic number each class gets.

If you want fast and safe type comparisons, you should probably use type_info::operator==. You have an example where you are comparing the type_info names, but that will generally be slower than a direct comparison. Comparing a typeid is roughly on par with a pointer comparison.

Just the same, you should really profile to see if dynamic_cast is as evil as you seem to think. It’s speed is proportional to the size of your inheritance hierarchy, and since your hierarchy is small, it should go pretty quick.

I was under the impression that packaging the ref count with the object was done using std::enable_shared_from_this. Is there something else clever that I’ve missed? For that matter, for real code I’d probably not use a shared value at all, and copy the value holder when the owning object was copied instead.

In C++0x static initialisations are required to be done in a thread safe manner. For the current standard there’s no such thing as threads, and cluttering up the example with pthread wrappers or something similarly horrible would be counterproductive.

I don’t really want to use type_info at all, since it opens a huge can of worms. std::type_info isn’t a regular type, which makes working with it a nuisance.

dynamic_cast is a lot slower than static_cast. Whether or not that matters is down to the application.

Opps. It appears I don’t know how to deal with angle brackets when commenting. Lets see if I get this right this time. It should have read:

Thus simultaneous calls to magic_number_for<std::string>() would be guaranteed to be thread safe, but a call to magic_number_for<std::string>() and magic_number_for<int>() would be allowed to run in parallel and could cause problems since they both rely on next_magic_number().

Mm, yes. Unfortunately, the example’s stuck between keeping things simple enough that it’s not distracting, and giving a real working example that makes sense. Guess I should update that one since it’s a correctness thing rather than a purpose thing.

(Note that static values inside a template are not shared between different instantiations of that template.)
A small note, but because of this fact you solution is useless.
Or do you create you applications in one big module?

I separate my projects into several modules, which communicate with clear interfaces.
So when I create a “Something” instance in Module A and extract its value in Module B I might get a crash. Just think about it.