Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Making a custom type_index

Basics
Getting type infos at runtime
Using new type infos all around the code

Sometimes there may be a need to create your own type info system. This may be useful if you wish to store some more info about types (PODness, size of a type, pointers to common functions...) or if you have an idea of a more compact types representations.

The following example shows how a user defined type_info can be created and used. Example works with and without RTTI.

Consider situation when user uses only those types in typeid():

#include <vector>
#include <string>

namespace my_namespace {

class my_class;
struct my_struct;

typedef std::vector<my_class> my_classes;
typedef std::string my_string;

} // namespace my_namespace

In that case user may wish to save space in binary and create it's own type system. For that case detail::typenum<> meta function is added. Depending on the input type T this function will return different numeric values.

#include <boost/type_index/type_index_facade.hpp>

namespace my_namespace { namespace detail {
    template <class T> struct typenum;
    template <> struct typenum<void>{       enum {value = 0}; };
    template <> struct typenum<my_class>{   enum {value = 1}; };
    template <> struct typenum<my_struct>{  enum {value = 2}; };
    template <> struct typenum<my_classes>{ enum {value = 3}; };
    template <> struct typenum<my_string>{  enum {value = 4}; };

#ifdef BOOST_MSVC
#pragma warning(push)
#pragma warning(disable: 4510 4512 4610) // non-copyable non-constructable type
#endif

    // my_typeinfo structure is used to save type number
    struct my_typeinfo {
        const char* const type_;
    };

#ifdef BOOST_MSVC
#pragma warning(pop)
#endif

    const my_typeinfo infos[5] = {
        {"void"}, {"my_class"}, {"my_struct"}, {"my_classes"}, {"my_string"}
    };

    template <class T>
    inline const my_typeinfo& my_typeinfo_construct() {
        return infos[typenum<T>::value];
    }
}} // my_namespace::detail

my_type_index is a user created type_index class. If in doubt during this phase, you can always take a look at the <boost/type_index/ctti_type_index.hpp> or <boost/type_index/stl_type_index.hpp> files. Documentation for type_index_facade could be also useful.

Since we are not going to override type_index_facade::hash_code() we must additionally include <boost/container_hash/hash.hpp>.

#include <boost/container_hash/hash.hpp>

See implementation of my_type_index:

namespace my_namespace {

class my_type_index: public boost::typeindex::type_index_facade<my_type_index, detail::my_typeinfo> {
    const detail::my_typeinfo* data_;

public:
    typedef detail::my_typeinfo type_info_t;

    inline my_type_index() BOOST_NOEXCEPT
        : data_(&detail::my_typeinfo_construct<void>())
    {}

    inline my_type_index(const type_info_t& data) BOOST_NOEXCEPT
        : data_(&data)
    {}

    inline const type_info_t&  type_info() const BOOST_NOEXCEPT {
        return *data_;
    }

    inline const char*  raw_name() const BOOST_NOEXCEPT {
        return data_->type_;
    }

    inline std::string  pretty_name() const {
        return data_->type_;
    }

    template <class T>
    inline static my_type_index type_id() BOOST_NOEXCEPT {
        return detail::my_typeinfo_construct<T>();
    }

    template <class T>
    inline static my_type_index type_id_with_cvr() BOOST_NOEXCEPT {
        return detail::my_typeinfo_construct<T>();
    }

    template <class T>
    inline static my_type_index type_id_runtime(const T& variable) BOOST_NOEXCEPT;
};

} // namespace my_namespace

Note that we have used the boost::typeindex::type_index_facade class as base. That class took care about all the helper function and operators (comparison, hashing, ostreaming and others).

Finally we can use the my_type_index class for getting type indexes:

my_type_index
    cl1 = my_type_index::type_id<my_class>(),
    st1 = my_type_index::type_id<my_struct>(),
    st2 = my_type_index::type_id<my_struct>(),
    vec = my_type_index::type_id<my_classes>()
;

assert(cl1 != st1);
assert(st2 == st1);
assert(vec.pretty_name() == "my_classes");
assert(cl1.pretty_name() == "my_class");

Usually to allow runtime type info we need to register class with some macro. Let's see how a MY_TYPEINDEX_REGISTER_CLASS macro could be implemented for our my_type_index class:

namespace my_namespace { namespace detail {

    template <class T>
    inline const my_typeinfo& my_typeinfo_construct_ref(const T*) {
        return my_typeinfo_construct<T>();
    }

#define MY_TYPEINDEX_REGISTER_CLASS                                             \
    virtual const my_namespace::detail::my_typeinfo& type_id_runtime() const {  \
        return my_namespace::detail::my_typeinfo_construct_ref(this);           \
    }

}} // namespace my_namespace::detail

Now when we have a MY_TYPEINDEX_REGISTER_CLASS, let's implement a my_type_index::type_id_runtime method:

namespace my_namespace {
    template <class T>
    my_type_index my_type_index::type_id_runtime(const T& variable) BOOST_NOEXCEPT {
        // Classes that were marked with `MY_TYPEINDEX_REGISTER_CLASS` will have a
        // `type_id_runtime()` method.
        return variable.type_id_runtime();
    }
}

Consider the situation, when my_class and my_struct are polymorphic classes:

namespace my_namespace {

class my_class {
public:
    MY_TYPEINDEX_REGISTER_CLASS
    virtual ~my_class() {}
};

struct my_struct: public my_class {
    MY_TYPEINDEX_REGISTER_CLASS
};

} // namespace my_namespace

Now the following example will compile and work.

my_struct str;
my_class& reference = str;
assert(my_type_index::type_id<my_struct>() == my_type_index::type_id_runtime(reference));

There is an easy way to force boost::typeindex::type_id to use your own type_index class.

All we need to do is just define BOOST_TYPE_INDEX_USER_TYPEINDEX to the full path to header file of your type index class:

// BOOST_TYPE_INDEX_USER_TYPEINDEX must be defined *BEFORE* first inclusion of <boost/type_index.hpp>
#define BOOST_TYPE_INDEX_USER_TYPEINDEX <boost/../libs/type_index/examples/user_defined_typeinfo.hpp>
#include <boost/type_index.hpp>

You'll also need to add some typedefs and macro to your "user_defined_typeinfo.hpp" header file:

#define BOOST_TYPE_INDEX_REGISTER_CLASS MY_TYPEINDEX_REGISTER_CLASS
namespace boost { namespace typeindex {
    typedef my_namespace::my_type_index type_index;
}}

That's it! Now all TypeIndex global methods and typedefs will be using your class:

boost::typeindex::type_index worldwide = boost::typeindex::type_id<my_classes>();
assert(worldwide.pretty_name() == "my_classes");
assert(worldwide == my_type_index::type_id<my_classes>());

PrevUpHomeNext