From 7488e75a9cb32d8f9b33496c4580d0d0d11ca628 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Thu, 20 Feb 2014 15:36:23 +0400 Subject: [PATCH] Finished example with user defined type_index, improved doocs and refactored some of the functions --- doc/type_index.qbk | 21 ++ examples/user_defined_typeinfo.cpp | 156 +++----------- examples/user_defined_typeinfo.hpp | 201 ++++++++++++++++++ include/boost/type_index.hpp | 4 + .../boost/type_index/ctti_register_class.hpp | 8 +- include/boost/type_index/ctti_type_index.hpp | 2 +- test/type_index_test.cpp | 2 +- 7 files changed, 267 insertions(+), 127 deletions(-) create mode 100644 examples/user_defined_typeinfo.hpp diff --git a/doc/type_index.qbk b/doc/type_index.qbk index 8556fca..a1223f6 100644 --- a/doc/type_index.qbk +++ b/doc/type_index.qbk @@ -203,13 +203,34 @@ expands to nothing. [xinclude autodoc.xml] [section Making own type_index] + +Sometimes there may be a need to create your own type info system. This may be usefull 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. + +[import ../examples/user_defined_typeinfo.hpp] [import ../examples/user_defined_typeinfo.cpp] + +[section Basics] [type_index_userdefined_usertypes] [type_index_userdefined_enum] [type_index_my_type_index] [type_index_my_type_index_usage] [endsect] +[section Getting type infos at runtime] +[type_index_my_type_index_register_class] +[type_index_my_type_index_type_id_runtime_implmentation] +[type_index_my_type_index_type_id_runtime_classes] +[type_index_my_type_index_type_id_runtime_test] +[endsect] + +[section Using new type infos all around the code] +[type_index_my_type_index_worldwide_macro] +[type_index_my_type_index_worldwide_typedefs] +[type_index_my_type_index_worldwide_usage] +[endsect] + +[endsect] + [section Space and Performance] diff --git a/examples/user_defined_typeinfo.cpp b/examples/user_defined_typeinfo.cpp index ddd7741..60ec712 100644 --- a/examples/user_defined_typeinfo.cpp +++ b/examples/user_defined_typeinfo.cpp @@ -4,136 +4,31 @@ // (See the accompanying file LICENSE_1_0.txt // or a copy at .) -//[type_index_userdefined_usertypes -/*` - 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()`: +//[type_index_my_type_index_worldwide_macro +/*` + There is an easy way to force `boost::typeind::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: */ -#include -#include - -namespace my_namespace { - -class my_class; -struct my_struct; - -typedef std::vector my_classes; -typedef std::string my_string; - -} // namespace my_namespace - -//] [/type_index_userdefined_usertypes] +// BOOST_TYPE_INDEX_USER_TYPEINDEX must be defined *BEFORE* first inclusion of +#define BOOST_TYPE_INDEX_USER_TYPEINDEX +#include +//] [/type_index_my_type_index_worldwide_macro] -//[type_index_userdefined_enum -/*` - 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 - -namespace my_namespace { namespace detail { - template struct typenum; - template <> struct typenum{ enum {value = 0}; }; - template <> struct typenum{ enum {value = 1}; }; - template <> struct typenum{ enum {value = 2}; }; - template <> struct typenum{ enum {value = 3}; }; - template <> struct typenum{ enum {value = 4}; }; - - // my_typeinfo structure is used to save type number - struct my_typeinfo { - // type_[0] will hold a type number - // type_[1] will be '\0', to have a zero terminated raw type name - char type_[2]; - }; - - template - inline const my_typeinfo& my_typeinfo_construct() { - static const my_typeinfo ret = {{ static_cast(typenum::value), '\0' }}; - return ret; - } -}} // my_namespace::detail - -//] [/type_index_userdefined_usertypes] - - -//[type_index_my_type_index -/*` - `my_type_index` is a user created type_index class. If in doubt during this phase, you can always - take a look at the `` or `` - 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::typeind::type_index_facade { - const detail::my_typeinfo* data_; - -public: - typedef detail::my_typeinfo type_info_t; - - inline my_type_index() BOOST_NOEXCEPT - : data_(&detail::my_typeinfo_construct()) - {} - - 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 { - // Must be in sync with detail::typenum::value - static const char* names[] = { - "void", "my_class", "my_struct", "my_classes", "my_string" - }; - - const std::size_t indx = static_cast(data_->type_[0]); - return names[indx]; - } - - template - inline static my_type_index type_id() BOOST_NOEXCEPT { - return detail::my_typeinfo_construct(); - } -}; - -} // namespace my_namespace - -/*` - Note that we have used the boost::typeind::type_index_facade class as base. - That class took care about all the helper function and operators (comparison, hashing, ostreaming and others). -*/ - -//] [/type_index_my_type_index] - -namespace my_namespace { - -class my_class{}; -struct my_struct{}; - -} // namespace my_namespace - -//[type_index_my_type_index_usage -/*` - Finally we can use the my_type_index class for getting type indexes: -*/ using namespace my_namespace; #include int main() { +//[type_index_my_type_index_usage +/*` + Finally we can use the my_type_index class for getting type indexes: +*/ + my_type_index cl1 = my_type_index::type_id(), st1 = my_type_index::type_id(), @@ -145,7 +40,26 @@ int main() { assert(st2 == st1); assert(vec.pretty_name() == "my_classes"); assert(cl1.pretty_name() == "my_class"); -} //] [/type_index_my_type_index_usage] +//[type_index_my_type_index_type_id_runtime_test +/*` + Now the follwoing example will compile and work. +*/ + my_struct str; + my_class& reference = str; + assert(my_type_index::type_id() == my_type_index::type_id_runtime(reference)); +//][/type_index_my_type_index_type_id_runtime_test] + +//[type_index_my_type_index_worldwide_usage +/*` + That's it! Now all TypeIndex global methods and typedefs will be using your class: +*/ + boost::typeind::type_index worldwide = boost::typeind::type_id(); + assert(worldwide.pretty_name() == "my_classes"); + assert(worldwide == my_type_index::type_id()); +//][/type_index_my_type_index_worldwide_usage] +} + + diff --git a/examples/user_defined_typeinfo.hpp b/examples/user_defined_typeinfo.hpp new file mode 100644 index 0000000..6e1e250 --- /dev/null +++ b/examples/user_defined_typeinfo.hpp @@ -0,0 +1,201 @@ +// Copyright 2013-2014 Antony Polukhin + +// Distributed under the Boost Software License, Version 1.0. +// (See the accompanying file LICENSE_1_0.txt +// or a copy at .) + +#ifndef USER_DEFINED_TYPEINFO_HPP +#define USER_DEFINED_TYPEINFO_HPP + +//[type_index_userdefined_usertypes +/*` + 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 +#include + +namespace my_namespace { + +class my_class; +struct my_struct; + +typedef std::vector my_classes; +typedef std::string my_string; + +} // namespace my_namespace + +//] [/type_index_userdefined_usertypes] + + +//[type_index_userdefined_enum +/*` + 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 + +namespace my_namespace { namespace detail { + template struct typenum; + template <> struct typenum{ enum {value = 0}; }; + template <> struct typenum{ enum {value = 1}; }; + template <> struct typenum{ enum {value = 2}; }; + template <> struct typenum{ enum {value = 3}; }; + template <> struct typenum{ enum {value = 4}; }; + + // my_typeinfo structure is used to save type number + struct my_typeinfo { + // type_[0] will hold a type number + // type_[1] will be '\0', to have a zero terminated raw type name + char type_[2]; + }; + + template + inline const my_typeinfo& my_typeinfo_construct() { + static const my_typeinfo ret = {{ static_cast(typenum::value), '\0' }}; + return ret; + } +}} // my_namespace::detail + +//] [/type_index_userdefined_usertypes] + + +//[type_index_my_type_index +/*` + `my_type_index` is a user created type_index class. If in doubt during this phase, you can always + take a look at the `` or `` + 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::typeind::type_index_facade { + const detail::my_typeinfo* data_; + +public: + typedef detail::my_typeinfo type_info_t; + + inline my_type_index() BOOST_NOEXCEPT + : data_(&detail::my_typeinfo_construct()) + {} + + 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 { + // Must be in sync with detail::typenum::value + static const char* names[] = { + "void", "my_class", "my_struct", "my_classes", "my_string" + }; + + const std::size_t indx = static_cast(data_->type_[0]); + return names[indx]; + } + + template + inline static my_type_index type_id() BOOST_NOEXCEPT { + return detail::my_typeinfo_construct(); + } + + template + inline static my_type_index type_id_with_cvr() BOOST_NOEXCEPT { + return detail::my_typeinfo_construct(); + } + + template + inline static my_type_index type_id_runtime(const T& variable) BOOST_NOEXCEPT; +}; + +} // namespace my_namespace + +/*` + Note that we have used the boost::typeind::type_index_facade class as base. + That class took care about all the helper function and operators (comparison, hashing, ostreaming and others). +*/ + +//] [/type_index_my_type_index] + +//[type_index_my_type_index_register_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 + inline const my_typeinfo& my_typeinfo_construct_ref(const T*) { + return my_typeinfo_construct(); + } + +#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 + +//] [/type_index_my_type_index_register_class] + +//[type_index_my_type_index_type_id_runtime_implmentation +/*` + Now when we have a MY_TYPEINDEX_REGISTER_CLASS, let's implement a `my_type_index::type_id_runtime` method: +*/ +namespace my_namespace { + template + 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(); + } +} +//] [/type_index_my_type_index_type_id_runtime_implmentation] + +//[type_index_my_type_index_type_id_runtime_classes +/*` + 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 + +//] [/type_index_my_type_index_type_id_runtime_classes] + + +//[type_index_my_type_index_worldwide_typedefs +/*` + 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 typeind { + typedef my_namespace::my_type_index type_index; +}} +//] [/type_index_my_type_index_worldwide_typedefs] + + +#endif // USER_DEFINED_TYPEINFO_HPP + diff --git a/include/boost/type_index.hpp b/include/boost/type_index.hpp index 601534c..036fa88 100644 --- a/include/boost/type_index.hpp +++ b/include/boost/type_index.hpp @@ -67,6 +67,10 @@ typedef type_index::type_info_t type_info; /// \def BOOST_TYPE_INDEX_REGISTER_CLASS /// BOOST_TYPE_INDEX_REGISTER_CLASS is a helper macro that is used to help to emulate RTTI. /// Put this macro into the public section of polymorphic class to allow runtime type detection. +/// +/// Depending on the typeid() availability this macro will expand to nothing or to virtual helper function +/// `virtual const type_info& type_id_runtime() const`. +/// /// \b Example: /// \code /// class A { diff --git a/include/boost/type_index/ctti_register_class.hpp b/include/boost/type_index/ctti_register_class.hpp index 23ac6e8..7356a7a 100644 --- a/include/boost/type_index/ctti_register_class.hpp +++ b/include/boost/type_index/ctti_register_class.hpp @@ -29,10 +29,10 @@ inline const ctti_data& ctti_construct_typeid_ref(const T*) BOOST_NOEXCEPT { }}} // namespace boost::typeind::detail -#define BOOST_TYPE_INDEX_REGISTER_CTTI_CLASS \ - virtual const boost::typeind::detail::ctti_data& type_id_ref() const BOOST_NOEXCEPT { \ - return boost::typeind::detail::ctti_construct_typeid_ref(this); \ - } \ +#define BOOST_TYPE_INDEX_REGISTER_CTTI_CLASS \ + virtual const boost::typeind::detail::ctti_data& type_id_runtime() const BOOST_NOEXCEPT { \ + return boost::typeind::detail::ctti_construct_typeid_ref(this); \ + } \ /**/ #endif // BOOST_TYPE_INDEX_CTTI_REGISTER_CLASS_HPP diff --git a/include/boost/type_index/ctti_type_index.hpp b/include/boost/type_index/ctti_type_index.hpp index b7110b5..07006e2 100644 --- a/include/boost/type_index/ctti_type_index.hpp +++ b/include/boost/type_index/ctti_type_index.hpp @@ -101,7 +101,7 @@ inline ctti_type_index ctti_type_index::type_id_with_cvr() BOOST_NOEXCEPT { template inline ctti_type_index ctti_type_index::type_id_runtime(const T& variable) BOOST_NOEXCEPT { - return variable.type_id_ref(); + return variable.type_id_runtime(); } diff --git a/test/type_index_test.cpp b/test/type_index_test.cpp index 95a55d9..8618f60 100644 --- a/test/type_index_test.cpp +++ b/test/type_index_test.cpp @@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(comparators_type_id_runtime) BOOST_CHECK(typeid(&rc1) == typeid(pb1)); BOOST_CHECK(typeid(&rb1) == typeid(pc1)); #else - BOOST_CHECK(boost::typeind::type_index(pc1->type_id_ref()).raw_name()); + BOOST_CHECK(boost::typeind::type_index(pc1->type_id_runtime()).raw_name()); #endif BOOST_CHECK_EQUAL(boost::typeind::type_id_runtime(rc1), boost::typeind::type_id_runtime(*pc1));