Иногда может возникнуть необходимость в создании собственной информационной системы. Это может быть полезно, если вы хотите сохранить больше информации о типах (положение, размер типа, указатели на общие функции ...) или если у вас есть представление о более компактных представлениях типов.
В приведенном ниже примере показано, как можно создать и использовать определенную пользователем информацию типа_info. Пример работает с RTTI и без него.
Рассмотрим ситуацию, когда пользователь использует только эти типы в<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;
}
В этом случае пользователь может захотеть сэкономить пространство в двоичном коде и создать собственную систему типов. В этом случае<detail::typenum<>
>добавляется метафункция. В зависимости от типа ввода T эта функция будет возвращать различные числовые значения.
#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}; };
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_type_index
>— созданный пользователем класс type_index. Если вы сомневаетесь на этом этапе, вы всегда можете взглянуть на файлы<<boost/type_index/ctti_type_index.hpp>
>или<<boost/type_index/stl_type_index.hpp>
>. Документация для<type_index_facade
>также может быть полезной.
См. осуществление<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;
};
}
Обратите внимание, что в качестве основы мы использовали класс boost::typeindex::type_index_facade. Этот класс заботился обо всех вспомогательных функциях и операторах (сравнение, хеширование, омтрийинг и другие).
Наконец, мы можем использовать класс my_type_index для получения индексов типов:
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");
Обычно для того, чтобы предоставить информацию о типе среды выполнения, нам нужно зарегистрировать класс с помощью макроса. Давайте посмотрим, как макрос<MY_TYPEINDEX_REGISTER_CLASS
>может быть реализован для нашего класса<my_type_index
>:
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); \
}
}}
Теперь, когда у нас есть MY_TYPEINDEX_REGISTER_CLASS, давайте реализуем метод<my_type_index::type_id_runtime
>:
namespace my_namespace {
template <class T>
my_type_index my_type_index::type_id_runtime(const T& variable) BOOST_NOEXCEPT {
return variable.type_id_runtime();
}
}
Рассмотрим ситуацию, когда<my_class
>и<my_struct
>являются полиморфными классами:
namespace my_namespace {
class my_class {
public:
MY_TYPEINDEX_REGISTER_CLASS
virtual ~my_class() {}
};
struct my_struct: public my_class {
MY_TYPEINDEX_REGISTER_CLASS
};
}
Теперь следующий пример будет компилироваться и работать.
my_struct str;
my_class& reference = str;
assert(my_type_index::type_id<my_struct>() == my_type_index::type_id_runtime(reference));
Существует простой способ заставить<boost::typeindex::type_id
>использовать свой собственный класс type_index.
Все, что нам нужно сделать, это просто определить<BOOST_TYPE_INDEX_USER_TYPEINDEX
>полный путь к файлу заголовка вашего класса индекса типа:
#define BOOST_TYPE_INDEX_USER_TYPEINDEX <boost/../libs/type_index/examples/user_defined_typeinfo.hpp>
#include <boost/type_index.hpp>
Вам также нужно будет добавить несколько типдефов и макросов в файл заголовка «user_defined_typeinfo.hpp»:
#define BOOST_TYPE_INDEX_REGISTER_CLASS MY_TYPEINDEX_REGISTER_CLASS
namespace boost { namespace typeindex {
typedef my_namespace::my_type_index type_index;
}}
Вот так! Теперь все глобальные методы и типдефы TypeIndex будут использовать ваш класс:
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>());