Allow runtime type info even when RTTI is off. Improve tests and examples

This commit is contained in:
Antony Polukhin
2014-02-19 13:03:12 +04:00
parent c99b7b26da
commit ea2e5beabb
8 changed files with 120 additions and 98 deletions

View File

@ -9,16 +9,18 @@
The following example shows that `boost::type_info` is able to store the real type, successfully getting through
all the inheritances.
Example works with RTTI only. Without RTTI support it won't compile, producing a compile-time error with message:
"boost::type_id_rtti_only(T&) requires RTTI"
Example works with and without RTTI."
*/
#include <boost/type_index.hpp>
#include <iostream>
struct A { virtual ~A(){} };
struct B: public A {};
struct C: public B {};
struct A {
BOOST_TYPE_INDEX_REGISTER_CLASS
virtual ~A(){}
};
struct B: public A { BOOST_TYPE_INDEX_REGISTER_CLASS };
struct C: public B { BOOST_TYPE_INDEX_REGISTER_CLASS };
void print_real_type(const A& a) {
std::cout << boost::typeind::type_id_runtime(a).pretty_name() << '\n';

View File

@ -21,12 +21,13 @@
#include <boost/config.hpp>
#if defined(BOOST_TYPE_INDEX_USER_TYPEINDEX) && defined(BOOST_TYPE_INDEX_USER_TYPEINDEX_NAME)
#if defined(BOOST_TYPE_INDEX_USER_TYPEINDEX)
# include BOOST_TYPE_INDEX_USER_TYPEINDEX
#elif (!defined(BOOST_NO_RTTI) && !defined(BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY)) || defined(BOOST_MSVC)
# include <boost/type_index/stl_type_index.hpp>
#else
#else
# include <boost/type_index/ctti_type_index.hpp>
# include <boost/type_index/ctti_register_class.hpp>
#endif
namespace boost { namespace typeind {
@ -38,12 +39,14 @@ namespace boost { namespace typeind {
/// Could be a boost::typeind::stl_type_index, boost::typeind::ctti_type_index or
/// user defined type_index class.
typedef platform-specific type_index;
#elif defined(BOOST_TYPE_INDEX_USER_TYPEINDEX) && defined(BOOST_TYPE_INDEX_USER_TYPEINDEX_NAME)
typedef BOOST_TYPE_INDEX_USER_TYPEINDEX_NAME type_index;
#elif defined(BOOST_TYPE_INDEX_USER_TYPEINDEX)
// Nothing to do
#elif (!defined(BOOST_NO_RTTI) && !defined(BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY)) || defined(BOOST_MSVC)
typedef boost::typeind::stl_type_index type_index;
# define BOOST_TYPE_INDEX_REGISTER_CLASS
#else
typedef boost::typeind::ctti_type_index type_index;
# define BOOST_TYPE_INDEX_REGISTER_CLASS BOOST_TYPE_INDEX_REGISTER_CTTI_CLASS
#endif
/// Depending on a compiler flags, optimal implementation of type_info will be used
@ -58,17 +61,35 @@ typedef type_index::type_info_t type_info;
/// \def BOOST_TYPE_INDEX_USER_TYPEINFO
/// BOOST_TYPE_INDEX_USER_TYPEINFO can be defined to the path to header file
/// with user provided implementation of type_index.
///
/// BOOST_TYPE_INDEX_USER_TYPEINDEX_NAME must be also defined!
#define BOOST_TYPE_INDEX_USER_TYPEINDEX <full/absolute/path/to/header/with/type_index>
/// \def BOOST_TYPE_INDEX_USER_TYPEINDEX_NAME
/// BOOST_TYPE_INDEX_USER_TYPEINDEX_NAME can be defined to the fullty
/// qualified name of the user's type_index implementation.
/// \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.
/// \b Example:
/// \code
/// class A {
/// public:
/// BOOST_TYPE_INDEX_REGISTER_CLASS
/// virtual ~A(){}
/// };
///
/// BOOST_TYPE_INDEX_USER_TYPEINDEX must be also defined!
#define BOOST_TYPE_INDEX_USER_TYPEINDEX_NAME my_namespace::my_type_index
/// struct B: public A {
/// BOOST_TYPE_INDEX_REGISTER_CLASS
/// };
///
/// struct C: public B {
/// BOOST_TYPE_INDEX_REGISTER_CLASS
/// };
///
/// ...
///
/// C c1;
/// A* pc1 = &c1;
/// assert(boost::typeind::type_id<C>() == boost::typeind::type_id_runtime(*pc1));
/// \endcode
#define BOOST_TYPE_INDEX_REGISTER_CLASS nothing-or-some-virtual-functions
#endif // defined(BOOST_TYPE_INDEX_DOXYGEN_INVOKED)
@ -136,31 +157,6 @@ inline type_index type_id_runtime(const T& runtime_val) BOOST_NOEXCEPT {
return type_index::type_id_runtime(runtime_val);
}
/// Function that works exactly like C++ typeid(rtti_val) call, but returns boost::type_index.
///
/// Retunrs runtime information about specified type.
///
/// \b Requirements: RTTI available or specially designed user type_info class must be provided
/// via BOOST_TYPE_INDEX_USER_TYPEINDEX and BOOST_TYPE_INDEX_USER_TYPEINDEX_NAME macro.
///
/// \b Example:
/// \code
/// struct Base { virtual ~Base(){} };
/// struct Derived: public Base {};
/// ...
/// Base* b = new Derived();
/// type_index ti = type_id_runtime(b);
/// std::cout << ti.pretty_name(); // Outputs 'Derived*'
/// \endcode
///
/// \param runtime_val Varaible which runtime type must be returned.
/// \throw Nothing.
/// \return boost::typeind::type_index with information about the specified variable.
template <class T>
inline type_index type_id_runtime(const T* runtime_val) {
return type_index::type_id_runtime(runtime_val);
}
}} // namespace boost::typeind

View File

@ -0,0 +1,39 @@
//
// Copyright (c) Antony Polukhin, 2013-2014.
//
//
// 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)
//
#ifndef BOOST_TYPE_INDEX_CTTI_REGISTER_CLASS_HPP
#define BOOST_TYPE_INDEX_CTTI_REGISTER_CLASS_HPP
// MS compatible compilers support #pragma once
#if defined(_MSC_VER)
# pragma once
#endif
/// \file ctti_register_class.hpp
/// \brief Contains BOOST_TYPE_INDEX_REGISTER_CTTI_CLASS macro.
#include <boost/type_index/ctti_type_index.hpp>
namespace boost { namespace typeind { namespace detail {
template <class T>
inline const ctti_data& ctti_construct_typeid_ref(const T*) BOOST_NOEXCEPT {
return ctti_construct<T>();
}
}}} // 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); \
} \
/**/
#endif // BOOST_TYPE_INDEX_CTTI_REGISTER_CLASS_HPP

View File

@ -39,14 +39,15 @@ struct ctti_data {
const char* typename_;
};
} // namespace detail
/// Helper method for getting detail::ctti_data of a tempalte patameter T.
template <class T>
inline const ctti_data& ctti_construct() BOOST_NOEXCEPT {
static const ctti_data result = { boost::detail::ctti<T>::n() };
inline const detail::ctti_data& ctti_construct() BOOST_NOEXCEPT {
static const detail::ctti_data result = { boost::detail::ctti<T>::n() };
return result;
}
} // namespace detail
/// \class ctti_type_index
/// This class is a wrapper that pretends to work exactly like stl_type_info, but does
/// not require RTTI support. For description of functions see type_index_facade.
@ -60,7 +61,7 @@ public:
typedef detail::ctti_data type_info_t;
inline ctti_type_index() BOOST_NOEXCEPT
: data_(&detail::ctti_construct<void>())
: data_(&ctti_construct<void>())
{}
inline ctti_type_index(const type_info_t& data) BOOST_NOEXCEPT
@ -78,9 +79,6 @@ public:
template <class T>
inline static ctti_type_index type_id_with_cvr() BOOST_NOEXCEPT;
template <class T>
inline static ctti_type_index type_id_runtime(const T* variable) BOOST_NOEXCEPT;
template <class T>
inline static ctti_type_index type_id_runtime(const T& variable) BOOST_NOEXCEPT;
};
@ -90,31 +88,20 @@ template <class T>
inline ctti_type_index ctti_type_index::type_id() BOOST_NOEXCEPT {
typedef BOOST_DEDUCED_TYPENAME boost::remove_reference<T>::type no_ref_t;
typedef BOOST_DEDUCED_TYPENAME boost::remove_cv<no_ref_t>::type no_cvr_t;
return detail::ctti_construct<no_cvr_t>();
return ctti_construct<no_cvr_t>();
}
template <class T>
inline ctti_type_index ctti_type_index::type_id_with_cvr() BOOST_NOEXCEPT {
return detail::ctti_construct<T>();
return ctti_construct<T>();
}
template <class T>
inline ctti_type_index ctti_type_index::type_id_runtime(const T* rtti_val) BOOST_NOEXCEPT {
BOOST_STATIC_ASSERT_MSG(sizeof(T) && false,
"type_id_runtime(const T*) and type_index::construct_runtime(const T*) require RTTI");
return detail::ctti_construct<T>();
}
template <class T>
inline ctti_type_index ctti_type_index::type_id_runtime(const T& rtti_val) BOOST_NOEXCEPT {
BOOST_STATIC_ASSERT_MSG(sizeof(T) && false,
"type_id_runtime(const T&) and type_index::construct_runtime(const T&) require RTTI");
return detail::ctti_construct<T>();
inline ctti_type_index ctti_type_index::type_id_runtime(const T& variable) BOOST_NOEXCEPT {
return variable.type_id_ref();
}
@ -137,6 +124,5 @@ inline std::size_t ctti_type_index::hash_code() const BOOST_NOEXCEPT {
}} // namespace boost::typeind
#endif // BOOST_TYPE_INDEX_CTTI_TYPE_INDEX_IPP
#endif // BOOST_TYPE_INDEX_CTTI_TYPE_INDEX_HPP

View File

@ -110,9 +110,6 @@ public:
template <class T>
inline static stl_type_index type_id_with_cvr() BOOST_NOEXCEPT;
template <class T>
inline static stl_type_index type_id_runtime(const T* variable);
template <class T>
inline static stl_type_index type_id_runtime(const T& value) BOOST_NOEXCEPT;
};
@ -248,20 +245,12 @@ inline stl_type_index stl_type_index::type_id_with_cvr() BOOST_NOEXCEPT {
return typeid(type);
}
template <class T>
inline stl_type_index stl_type_index::type_id_runtime(const T* rtti_val) {
#ifdef BOOST_NO_RTTI
BOOST_STATIC_ASSERT_MSG(sizeof(T) && false,
"type_id_runtime(const T*) and type_index::construct_runtime(const T*) require RTTI");
#endif
return typeid(rtti_val);
}
template <class T>
inline stl_type_index stl_type_index::type_id_runtime(const T& value) BOOST_NOEXCEPT {
#ifdef BOOST_NO_RTTI
BOOST_STATIC_ASSERT_MSG(sizeof(T) && false,
"type_id_runtime(const T&) and type_index::construct_runtime(const T&) require RTTI");
"stl_type_index::type_id_runtime(const T&) require RTTI");
#endif
return typeid(value);
}

View File

@ -129,15 +129,6 @@ protected:
template <class T>
static Derived type_id_with_cvr() BOOST_NOEXCEPT;
/// This is a factory method that is used to create instances of Derived classes.
/// boost::typeind::type_id_runtime(const T*) will call this method, if Derived has same type as boost::typeind::type_index.
///
/// \b Override: This function \b may be redefined and made public in Derived class.
/// \param variable Variable which runtime type will be stored in type_index.
/// \return type_index with runtime type of variable.
template <class T>
static Derived type_id_runtime(const T* variable);
/// This is a factory method that is used to create instances of Derived classes.
/// boost::typeind::type_id_runtime(const T&) will call this method, if Derived has same type as boost::typeind::type_index.
///

View File

@ -6,7 +6,7 @@
import testing ;
import feature ;
import os ;
# Variable that contains all the stuff required for linking against -lboost_unit_test
tlib = /boost/test//boost_unit_test_framework/<link>static ;
@ -45,17 +45,16 @@ test-suite type_index
#[ link-fail testing_crossmodule.cpp $(tlib) test_lib_nortti : : link_fail_rtti_nortti ]
[ run testing_crossmodule.cpp $(tlib) test_lib_rtti_compat : : : $(nortti) $(compat) : testing_crossmodule_nortti_rtti_compat ]
[ run testing_crossmodule.cpp $(tlib) test_lib_nortti_compat : : : $(compat) : testing_crossmodule_rtti_nortti_compat ]
# Examples that must work even with RTTI disabled
[ run ../examples/registry.cpp : : : <rtti>off : registry_no_rtti ]
[ run ../examples/exact_types_match.cpp : : : <rtti>off : exact_types_match_no_rtti ]
[ run ../examples/demangled_names.cpp : : : <rtti>off : demangled_names_no_rtti ]
[ compile-fail ../examples/inheritance.cpp : <rtti>off : failing_inheritance_example ]
;
# Assuring that examples compile and run. Adding sources from `examples` directory to the `type_index` test suite.
for local p in [ glob ../examples/*.cpp ]
{
# RTTI on
type_index += [ run $(p) ] ;
# RTTI off
local target_name = $(p[1]:B)_no_rtti ;
type_index += [ run $(p) : : : <rtti>off : $(target_name) ] ;
}

View File

@ -245,11 +245,19 @@ BOOST_AUTO_TEST_CASE(type_index_user_defined_class_test)
}
#ifndef BOOST_NO_RTTI
struct A {
public:
BOOST_TYPE_INDEX_REGISTER_CLASS
virtual ~A(){}
};
class A { public: virtual ~A(){} };
class B: public A{};
class C: public B {};
struct B: public A {
BOOST_TYPE_INDEX_REGISTER_CLASS
};
struct C: public B {
BOOST_TYPE_INDEX_REGISTER_CLASS
};
BOOST_AUTO_TEST_CASE(comparators_type_id_runtime)
{
@ -259,6 +267,8 @@ BOOST_AUTO_TEST_CASE(comparators_type_id_runtime)
A& rc1 = c1;
A* pb1 = &b1;
A& rb1 = b1;
#ifndef BOOST_NO_RTTI
BOOST_CHECK(typeid(rc1) == typeid(*pc1));
BOOST_CHECK(typeid(rb1) == typeid(*pb1));
@ -267,12 +277,19 @@ 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());
#endif
BOOST_CHECK_EQUAL(boost::typeind::type_id_runtime(rc1), boost::typeind::type_id_runtime(*pc1));
BOOST_CHECK_EQUAL(boost::typeind::type_id<C>(), boost::typeind::type_id_runtime(*pc1));
BOOST_CHECK_EQUAL(boost::typeind::type_id_runtime(rb1), boost::typeind::type_id_runtime(*pb1));
BOOST_CHECK_EQUAL(boost::typeind::type_id<B>(), boost::typeind::type_id_runtime(*pb1));
BOOST_CHECK_NE(boost::typeind::type_id_runtime(rc1), boost::typeind::type_id_runtime(*pb1));
BOOST_CHECK_NE(boost::typeind::type_id_runtime(rb1), boost::typeind::type_id_runtime(*pc1));
#ifndef BOOST_NO_RTTI
BOOST_CHECK_EQUAL(boost::typeind::type_id_runtime(&rc1), boost::typeind::type_id_runtime(pb1));
BOOST_CHECK_EQUAL(boost::typeind::type_id_runtime(&rb1), boost::typeind::type_id_runtime(pc1));
@ -283,9 +300,12 @@ BOOST_AUTO_TEST_CASE(comparators_type_id_runtime)
BOOST_CHECK(boost::typeind::type_id_runtime(rb1) != typeid(*pc1));
BOOST_CHECK(boost::typeind::type_id_runtime(&rc1) == typeid(pb1));
BOOST_CHECK(boost::typeind::type_id_runtime(&rb1) == typeid(pc1));
#endif
}
#ifndef BOOST_NO_RTTI
BOOST_AUTO_TEST_CASE(comparators_type_id_vs_type_info)
{
using namespace boost::typeind;