[cppx] Unique identifier values via std::type_info

Like my posting about cloning this posting about unique identifier values is in preparation for discussing the cppx library’s exception translation support. In short the aspect discussed here is how to let the calling code choose an id for a set of installed translators, so that it can remove them all in one operation (specifying that id). And the simplest id you have in C++ is a type with external linkage. You can obtain a unique-wrt.-comparisions id for a class from the typeid operator. The only problem is that that id, of type std::type_info, is not copyable and not generally comparable, so it can’t be used directly as a key in a std::map, say, and the standard fails to guarantee that you will always obtain the same std::type_info instance for the same type, so it’s a bit risky to use a pointer to that instance…

Andrei’s solution: a wrapper class for std::type_info

If there is some general problem that primarily involves C++ types, then Andrei Alexandrescu will probably have solved it already, and such a solution will probably also be present in the Boost library. And yes, a copyable, generally comparable wrapper for std::type_info was one of the first things that Andrei discussed in his “Modern C++ Design” book (most of the discussion in that book concerned the techniques employed in Andrei’s Loki library). If you don’t have that book, get it! 🙂

Andrei called his class TypeInfo, and that’s also what I call the cppx library’s version – it is of course in a different namespace…

In short, the only purpose of this class is to avoid a library dependency, where if this class hadn’t been defined by cppx itself then cppx would have needed to rely on the Loki library, and/or the Boost library, or some such.

The cppx TypeInfoclass

File [progrock/cppx/generic/TypeInfo.h]:

// Copyright (c) Alf P. Steinbach, 2010.
// A copyable and comparable type info class, inspired by Andrei's almost identical
// class in the Loki library. I just left out the "compatibility" functions 'before'.
// Essentially this provides internal-to-the-process unique identifier values.
// #include <progrock/cppx/generic/typeinfo.h>

#include <progrock/cppx/devsupport/better_experience.h>

//------------------------------------- Dependencies:

#include <typeinfo>

//------------------------------------- Interface:

namespace progrock{ namespace cppx{

    class TypeInfo
        std::type_info const*   pStdInfo_;

        virtual ~TypeInfo() {}

        TypeInfo( std::type_info const& stdInfo )
            : pStdInfo_( &stdInfo )

        char const* name() const { return pStdInfo_->name(); }

        bool operator!=( TypeInfo const& other ) const
            return !!(*pStdInfo_ != *other.pStdInfo_);      // "!!" for MSVC non-std ops.

        bool operator<( TypeInfo const& other ) const
            return !!pStdInfo_->before( *other.pStdInfo_ ); // "!!" for MSVC non-std ops.

        bool operator<=( TypeInfo const& other ) const
            return !other.pStdInfo_->before( *pStdInfo_ );

        bool operator==( TypeInfo const& other ) const
            return !!(*pStdInfo_ == *other.pStdInfo_);      // "!!" for MSVC non-std ops.

        bool operator>=( TypeInfo const& other ) const
            return !pStdInfo_->before( *other.pStdInfo_ );

        bool operator>( TypeInfo const& other ) const
            return !!other.pStdInfo_->before( *pStdInfo_ ); // "!!" for MSVC non-std ops.

}}  // namespace progrock::cppx


How this class differs from Andrei’s

I just typed in and tested the above, and then I looked in “Modern C++ Design” to see what Andrei did differently, if anything. Well there was not much. For some reason he put the comparison operators outside the class, as free-standing functions, and he included a before member function like the one in std::type_info.

Those differences are probably just about style.

And I guess, but I don’t know, that Andrei did not add those “!!” bang-bangs. I added them because MSVC 7.1 has non-standard int result type for the bool functions in std::type_info. And due to its own lack of standard-conformance wrt. to the result types, without the bang-bangs it issues a stream of what in this context are pure sillywarnings about allegedly inefficient conversion from int to bool!

How unique is a TypeInfo instance as an id?

With respect to comparison to other TypeInfo instances, a TypeInfo instance is almost as unique as a class.

Hey, “almost”? What do you mean? Why the weasel word?

Well, the C++ standard does not address the issue of dynamic libraries. And in Windows a dynamic library, a Windows DLL, is almost like a program on its own, unlike a *nix dynamic library which is more like a part of the client program. So if you pass a TypeInfo instance to a Windows DLL then there is no formal guarantee, and not even an in-practice guarantee, that the DLL won’t be able to produce a TypeInfo instance that compares as equal. In Windows terminology the uniqueness (with respect to comparisons) is only within a module. But happily that’s good enough for most C++ programming.

Usage considerations

With a type as argument the typeid operator requires that type to be complete (a complete type is one that you can give to sizeof). And so to avoid imposing needless verbosity on client code, when a TypeInfo instance is used as an id it may be a good idea to form a pointer type. For even when T is incomplete, T* is complete.


4 comments on “[cppx] Unique identifier values via std::type_info

  1. AFAIK, MSVC compares the type_infos by its internal string representation, so they are comparable even across DLL borders, but I don’t know if it really works or if there are some more pitfalls.

    Besides, this would have been a good class to introduce/discuss the Barton-Nackman-trick. 😉

    Thomas aka Phygon

    PS: Nice blog!

    • Hm. I would not recommend comparing type_infos across DLL borders even if MSVC supports that (but your description sounds as if type_infos for class Foo in module 1 and Foo in module 2 might compare equal with MSVC). It’s like all undefined behavior: it’s undefined.

      Re the Barton-Nackman trick, thanks! I only remembered that it had something to do with the Curiously Recurring Template Pattern, and had to look it up in Wikipedia. It wouldn’t have saved anything here, though, but it’s interesting on its own. One day I’m going to buy that book, seriously!

      The Barton-Nackman technique that I do remember (because it’s more commonly useful) is the Barton-Nackman Fallible class, which I think is called Optional in Boost. It enables calling code to do simple checking rather than dealing with an exception when a result-producing function fails to produce a result. But throwing an exception, or aborting, if an attempt is made to actually use a non-existent result.


      – Alf

  2. Making the comparison operators global rather than class members makes them more usable as conversions work as expected for both left and right argument rather than only for the right argument.

    First note that since TypeInfo( std::type_info const& stdInfo ) is not explicit std::type_info object can be implicitly converted to TypeInfo. So with global operators it would be possible to write

    typeof( MyClass ) == myClassTypeInfo

    where myClassTypeInfo is of TypeInfo. While the same is not possible with operators being members. Making the comparison the other way works in both cases.

    I think in general comparison operators should always be global (same as general arithmetic operators).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s