...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
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>());