Making a custom type_index

| Home | Libraries | People | FAQ | More

## Making a custom type_index

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.

### Basics

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}; };

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

        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.

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");

### Getting type infos at runtime

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));

### Using new type infos all around the code

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>());