blob: c890d1caec20a58af394e240a469ed02b1bedc48 [file] [log] [blame]
[library Boost.TypeIndex
[quickbook 1.6]
[version 4.0]
[copyright 2012-2014 Antony Polukhin]
[category Language Features Emulation]
[license
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
[@http://www.boost.org/LICENSE_1_0.txt])
]
]
[section Motivation]
Sometimes getting and storing information about a type at runtime is required. For such cases a construction like `&typeid(T)` or C++11 class `std::type_index` is usually used, which is where problems start:
* `typeid(T)` and `std::type_index` require Run Time Type Info (RTTI)
* some implementations of `typeid(T)` erroneously do not strip const, volatile and references from type
* some compilers have bugs and do not correctly compare `std::type_info` objects across shared libraries
* only a few implementations of Standard Library currently provide `std::type_index`
* no easy way to store type info without stripping const, volatile and references
* no nice and portable way to get human readable type names
* no way to easily make your own type info class
Boost.TypeIndex library was designed to work around all those issues.
[note `T` means type here. Think of it as of `T` in `template <class T>` ]
[endsect]
[section Getting started]
[classref boost::typeindex::type_info boost::typeindex::type_info] is a drop-in replacement for `std::type_info` and
[classref boost::typeindex::type_index boost::typeindex::type_index]
is a drop-in replacement for `std::type_index`. Unlike Standard Library versions those classes can work without RTTI.
`type_index` provides the full set of comparison operators, hashing functions and ostream
operators, so it can be used with any container class.
[section How to use]
To start using Boost.TypeIndex:
[table:porting
[[Replace this:][With the following:][More Info]]
[[``
#include <typeinfo>
#include <typeindex>
``][``
#include <boost/type_index.hpp>
``][
[headerref boost/type_index.hpp more... ]
]]
[[``
std::type_index
``][``
boost::typeindex::type_index
``][
[classref boost::typeindex::type_index more... ]
]]
[[``
typeid(T)
typeid(T).name() // not human readable
typeid(variable)
``][``
boost::typeindex::type_id<T>()
boost::typeindex::type_id<T>().pretty_name() // human readable
boost::typeindex::type_id_runtime(variable)
``][
[funcref boost::typeindex::type_id more... ]
[classref boost::typeindex::type_index more... ]
[funcref boost::typeindex::type_id_runtime more... ]
]]
[[``
// attempt to save const, volatile, reference
typeid(please_save_modifiers<T>)
``][``
// cvr = const, volatile, reference
boost::typeindex::type_id_with_cvr<T>()
``][
[funcref boost::typeindex::type_id_with_cvr more... ]
]]
[[``
// when reference to `std::type_info` is required
const std::type_info& v1 = typeid(int);
// other cases
const std::type_info* v2 = &typeid(int);
``][``
const boost::typeindex::type_info& v1
= boost::typeindex::type_id<int>().type_info();
boost::typeindex::type_index v2
= boost::typeindex::type_id<int>();
``][
[classref boost::typeindex::type_index more... ]
[funcref boost::typeindex::type_id more... ]
]]
]
If you are using [funcref boost::typeindex::type_id_runtime type_id_runtime()] methods
and RTTI is disabled, make sure that classes that are passed to
`type_id_runtime()` are marked with
[macroref BOOST_TYPE_INDEX_REGISTER_CLASS BOOST_TYPE_INDEX_REGISTER_CLASS] macro.
[endsect]
[section Example with Boost.Any]
Here is how TypeIndex could be used in `boost/any.hpp`:
[table:any
[[Before] [With TypeIndex]]
[[``#include <typeinfo>``][``#include <boost/type_index.hpp>``]]
[[``
virtual const std::type_info & type() const BOOST_NOEXCEPT
{
// requires RTTI
return typeid(ValueType);
}
``] [``
virtual const boost::typeindex::type_info & type() const BOOST_NOEXCEPT
{
// now works even with RTTI disabled
return boost::typeindex::type_id<ValueType>().type_info();
}
``]]
]
[endsect]
[section Example with Boost.Variant]
Here is how TypeIndex could be used in `boost/variant/variant.hpp`:
[table:variant
[[Before] [With TypeIndex]]
[[``
#if !defined(BOOST_NO_TYPEID)
#include <typeinfo> // for typeid, std::type_info
#endif // BOOST_NO_TYPEID
``][``
#include <boost/type_index.hpp>
``]]
[[``
#if !defined(BOOST_NO_TYPEID)
class reflect
: public static_visitor<const std::type_info&>
{
public: // visitor interfaces
template <typename T>
const std::type_info& operator()(const T&) const BOOST_NOEXCEPT
{
return typeid(T);
}
};
#endif // BOOST_NO_TYPEID
``][``
class reflect
: public static_visitor<const boost::typeindex::type_info&>
{
public: // visitor interfaces
template <typename T>
const boost::typeindex::type_info& operator()(const T&) const BOOST_NOEXCEPT
{
return boost::typeindex::type_id<T>().type_info();
}
};
``]]
[[``
#if !defined(BOOST_NO_TYPEID)
const std::type_info& type() const
{
detail::variant::reflect visitor;
return this->apply_visitor(visitor);
}
#endif
``] [``
const boost::typeindex::type_info& type() const
{
detail::variant::reflect visitor;
return this->apply_visitor(visitor);
}
``]]
]
[endsect]
[endsect]
[section:config Configuring and building the library]
TypeIndex is a header only library and it does not use Boost libraries that require
building. You just need to `#include <boost/type_index.hpp>` to start using it.
The library supports a number of configuration macros, defining which require full
rebuild of all the projects that use TypeIndex:
[table Configuration macros
[[Macro name] [Short description]]
[[[macroref BOOST_TYPE_INDEX_USER_TYPEINDEX]] [ Macro that allows you to use your
own implementation of TypeIndex instead of the default all around the projects and libraries.]]
[[[macroref BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY]] [ Macro that must be defined
if you are mixing RTTI-on and RTTI-off.]]
[[[macroref BOOST_TYPE_INDEX_CTTI_USER_DEFINED_PARSING] and
[macroref BOOST_TYPE_INDEX_FUNCTION_SIGNATURE]] [ Macros that allow you to specify
parsing options and type name generating macro for RTTI-off cases. ]]
]
You can define configuration macros in the `bjam` command line using one of the following
approaches:
[pre
b2 variant=release define=BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY stage
]
[pre
b2 variant=release "define=BOOST_TYPE_INDEX_CTTI_USER_DEFINED_PARSING='(39, 1, true, \\"T = \\")'" stage
]
However, it may be more convenient to define configuration macros in the "boost/config/user.hpp"
file in order to automatically define them both for the library and user's projects.
[endsect]
[section How it works]
`type_index` is just a typedef for [classref boost::typeindex::stl_type_index]
or [classref boost::typeindex::ctti_type_index].
Depending on the `typeid()` availability TypeIndex library will choose an optimal class for
`type_index`. In cases when at least basic support for `typeid()` is available `stl_type_index` will be used.
[macroref BOOST_TYPE_INDEX_REGISTER_CLASS] macro is a helper macro that places some virtual helper functions or
expands to nothing.
Issues with cross module type comparison on a bugged compilers are bypassed by directly comparing strings with type
(latest versions of those compilers resolved that issue using exactly the same approach).
[endsect]
[section Examples]
[import ../examples/demangled_names.cpp]
[section Getting human readable and mangled type names] [type_index_names_example] [endsect]
[import ../examples/registry.cpp]
[section Storing information about a type in container ] [type_index_registry_example] [endsect]
[import ../examples/inheritance.cpp]
[section Getting through the inheritance to receive a real type name ] [type_index_derived_example] [endsect]
[import ../examples/exact_types_match.cpp]
[section Exact type matching: storing type with const, volatile and reference qualifiers] [type_index_exact_type_match_example] [endsect]
[import ../examples/table_of_names.cpp]
[section Table of raw_name() and pretty_name() outputs with and without RTTI ] [type_index_names_table] [endsect]
[endsect]
[xinclude autodoc.xml]
[section 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.
[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]
* `ctti_type_index` uses macro for getting full text representation of function name which could lead to code bloat,
so prefer using `stl_type_index` type when possible.
* All the type_index classes hold a single pointer and are fast to copy.
* Calls to `const char* raw_name()` do not require dynamic memory allocation and usually just return a pointer to an array of chars in a read-only section of the binary image.
* Comparison operators are optimized as much as possible and execute a single `std::strcmp` in worst case.
* Calls to `std::string pretty_name()` usually require dynamic memory allocation and some computations, so they are not recommended for usage in performance critical sections.
[endsect]
[section Code bloat]
Without RTTI TypeIndex library will switch from using `boost::typeindex::stl_type_index` class to
`boost::typeindex::ctti_type_index`. `boost::typeindex::ctti_type_index` uses macro for getting full
text representation of function name for each type that is passed to `type_id()` and
`type_id_with_cvr()` functions.
This leads to big strings in binary file:
```
static const char* boost::detail::ctti<T>::n() [with T = int]
static const char* boost::detail::ctti<T>::n() [with T = user_defined_type]
```
While using RTTI, you'll get the following (more compact) string in binary file:
```
i
17user_defined_type
```
[endsect]
[section RTTI emulation limitations]
TypeIndex has been tested and successfully work on many compilers.
[warning
With RTTI off classes with exactly the same names defined in different modules in anonymous namespaces may collapse:
```
// In A.cpp
namespace { struct user_defined{}; }
type_index foo_a() { return type_id<user_defined>(); }
// In B.cpp
namespace { struct user_defined{}; }
type_index foo_b() { return type_id<user_defined>(); }
// In main.cpp
assert(foo_a() != foo_b()); // will fail on some compilers
```
*Compilers that have that limitation:* GCC, CLANG, Intel.
*Test:* you can test this issue by runing the `testing_crossmodule_anonymous_no_rtti` that can be build if you run `../../../b2` in `type_index/test/` folder.
]
[section Define the BOOST_TYPE_INDEX_FUNCTION_SIGNATURE macro]
If you get the following error during compilation
``
TypeIndex library could not detect your compiler.
Please make the BOOST_TYPE_INDEX_FUNCTION_SIGNATURE macro use
correct compiler macro for getting the whole function name.
Define BOOST_TYPE_INDEX_CTTI_USER_DEFINED_PARSING to correct value after that.
``
then you are using a compiler that was not tested with this library.
[macroref BOOST_TYPE_INDEX_FUNCTION_SIGNATURE] must be defined to a compiler specific macro, that outputs the *whole*
function signature including template parameters.
[endsect]
[section Fixing pretty_name() output]
If the output of `boost::typeindex::ctti_type_index::type_id<int>().name()`
* returns not just `int` but also a lot of text around the `int`
* or does not return type at all
then you are using a compiler that was not tested with this library and you need to setup the
[macroref BOOST_TYPE_INDEX_CTTI_USER_DEFINED_PARSING] macro.
Here is a short instruction:
# get the output of `boost::typeindex::ctti_type_index::type_id<int>().name()`
# define [macroref BOOST_TYPE_INDEX_CTTI_USER_DEFINED_PARSING] to
`(skip_at_begin, skip_at_end, false, "")`, where
* `skip_at_begin` is equal to characters count before the first occurrence of `int` in output
* `skip_at_end` is equal to characters count after last occurrence of `int` in output
# check that `boost::typeindex::ctti_type_index::type_id<int>().name_demangled()` returns "int"
# if it does not return `int`, then define BOOST_TYPE_INDEX_CTTI_USER_DEFINED_PARSING to
`(skip_at_begin, skip_at_end, true, "T = ")`, where
* `skip_at_begin` is equal to `skip_at_begin` at step 2
* `skip_at_end` is equal to `skip_at_end` at step 2
* `"T = "` is equal to characters that are right before the `int` in output
# (optional, but highly recommended) [@http://www.boost.org/support/bugs.html create ticket] with
feature request to add your compiler to supported compilers list. Include
parameters provided to `BOOST_TYPE_INDEX_CTTI_USER_DEFINED_PARSING` macro.
Consider the following example:
`boost::typeindex::ctti_type_index::type_id<int>().raw_name()` returns
"const char *__cdecl boost::detail::ctti<int>::n(void)". Then you shall set
`skip_at_begin` to `sizeof("const char *__cdecl boost::detail::ctti<") - 1`
and `skip_at_end` to `sizeof(">::n(void)") - 1`.
``
#define BOOST_TYPE_INDEX_CTTI_USER_DEFINED_PARSING (39, 6, false, "")
``
Another example:
`boost::typeindex::ctti_type_index::type_id<int>().raw_name()` returns
"static const char *boost::detail::ctti<int>::n() [T = int]"". Then you shall set
`skip_at_begin` to `sizeof("static const char *boost::detail::ctti<") - 1`
and `skip_at_end` to `sizeof("]") - 1` and last parameter of macro to "T = ".
``
#define BOOST_TYPE_INDEX_CTTI_USER_DEFINED_PARSING (39, 1, true, "T = ")
``
[endsect]
[endsect]
[section Mixing sources with RTTI on and RTTI off]
Linking a binary from source files that were compiled with different RTTI flags is not a very good
idea and may lead to a lot of surprises. However if there is a very strong need, TypeIndex library
provides a solution for mixing sources: just define [macroref BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY]
macro. This would lead to usage of same type_index class (`boost::typeindex::ctti_type_index` or
`boost::typeindex::stl_type_index`) all around the project.
[note Do not forget to rebuild *all* the projects with `BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY` macro defined ]
You must know that linking RTTI on and RTTI off binaries may succeed even without defining the
`BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY` macro, but that does not mean that you'll get a
working binary. Such actions may break the One Definition Rule. Take a look at the table below,
that shows how the `boost::type_index get_integer();` function will look like with different
RTTI flags:
[table:diffs
[[RTTI on] [RTTI off]]
[[`boost::typeindex::stl_type_index get_integer();`] [`boost::typeindex::ctti_type_index get_integer();`]]
]
Such differences are usually not detected by linker and lead to errors at runtime.
[warning
Even with `BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY` defined there is no guarantee
that everything will be OK. Libraries that use their own workarounds for disabled RTTI
may fail to link or to work correctly.
]
[endsect]
[section Acknowledgements]
In order of helping and advising:
* Peter Dimov for writing source codes in late 2007, that gave me an idea of how to emulate RTTI.
* Agustín Bergé K-ballo for helping with docs and fixing a lot of typos.
* Niall Douglas for generating a lot of great ideas, reviewing the sources and being the review manager for the library.
* All the library reviewers, especially Andrey Semashev, for making good notes and proposing improvements to the library.
[endsect]