From ad26256d0989b11604c1e97fb2d7a4ee4c82cad1 Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Thu, 11 Aug 2016 10:31:16 -0400 Subject: [PATCH] Initial documentation. --- doc/Jamfile.v2 | 1 + doc/type_index.qbk | 7 ++ examples/inheritance.cpp | 10 +++ examples/runtime_cast.cpp | 78 +++++++++++++++++++ include/boost/type_index/runtime_cast.hpp | 4 + .../runtime_cast/boost_shared_ptr_cast.hpp | 10 +++ .../type_index/runtime_cast/pointer_cast.hpp | 31 ++++++-- .../runtime_cast/reference_cast.hpp | 16 +++- .../runtime_cast/register_runtime_class.hpp | 55 +++++++++++-- .../runtime_cast/std_shared_ptr_cast.hpp | 14 +++- test/type_index_runtime_cast_test.cpp | 8 +- 11 files changed, 211 insertions(+), 23 deletions(-) create mode 100644 examples/runtime_cast.cpp diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index 53d6d7d..c788a1b 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -11,6 +11,7 @@ doxygen autodoc : [ glob ../../../boost/type_index.hpp ] [ glob ../../../boost/type_index/*.hpp ] + [ glob ../../../boost/type_index/runtime_cast/*.hpp ] : EXTRACT_ALL=NO HIDE_UNDOC_MEMBERS=YES diff --git a/doc/type_index.qbk b/doc/type_index.qbk index 27d8777..d985c21 100644 --- a/doc/type_index.qbk +++ b/doc/type_index.qbk @@ -246,6 +246,10 @@ Depending on the `typeid()` availability TypeIndex library will choose an optima [macroref BOOST_TYPE_INDEX_REGISTER_CLASS] macro is a helper macro that places some virtual helper functions or expands to nothing. +[macroref BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS] macro is a helper macro that places the same +helpers as BOOST_TYPE_INDEX_REGISTER_CLASS plus some additional helpers for boost::typeindex::runtime_cast +to function. + 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). @@ -262,6 +266,9 @@ Issues with cross module type comparison on a bugged compilers are bypassed by d [import ../examples/inheritance.cpp] [section Getting through the inheritance to receive a real type name ] [type_index_derived_example] [endsect] +[import ../examples/runtime_cast.cpp] +[section Using runtime_cast where RTTI is unavailable or undesirable ] [type_index_runtime_cast_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] diff --git a/examples/inheritance.cpp b/examples/inheritance.cpp index c263439..8bcdbf2 100644 --- a/examples/inheritance.cpp +++ b/examples/inheritance.cpp @@ -21,6 +21,7 @@ struct A { }; struct B: public A { BOOST_TYPE_INDEX_REGISTER_CLASS }; struct C: public B { BOOST_TYPE_INDEX_REGISTER_CLASS }; +struct D: public C { BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) }; void print_real_type(const A& a) { std::cout << boost::typeindex::type_id_runtime(a).pretty_name() << '\n'; @@ -31,6 +32,15 @@ int main() { const A& c_as_a = c; print_real_type(c_as_a); // Outputs `struct C` print_real_type(B()); // Outputs `struct B` + +/*` + It's also possible to use type_id_runtime with the BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS, which adds additional + information for runtime_cast to work. +*/ + D c; + const A& d_as_a = d; + print_real_type(dc_as_a); // Outputs `struct D` + } //] [/type_index_derived_example] diff --git a/examples/runtime_cast.cpp b/examples/runtime_cast.cpp new file mode 100644 index 0000000..f359d51 --- /dev/null +++ b/examples/runtime_cast.cpp @@ -0,0 +1,78 @@ +// +// Copyright (c) Chris Glover, 2016. +// +// +// 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) +// + +//[type_index_runtime_cast_example +/*` + The following example shows that `runtime_cast` is able to find a valid pointer + in various class hierarchies regardless of inheritance or type relation. + + Example works with and without RTTI." +*/ + +#include +#include + +struct A { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) + virtual ~A() + {} +}; + +struct B { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) + virtual ~B() + {} +}; + +struct C : A { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((A)) +}; + +struct D : B { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((B)) +}; + +struct E : C, D { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((C)(D)) +}; + +int main() { + C c; + A* a = &c; + + if(C* cp = boost::typeindex::runtime_cast(a)) + std::cout << "Yes, a points to a C." << '\n'; + else + std::cout << "Error: Expected a to point to a C" << '\n'; + + if(E* ce = boost::typeindex::runtime_cast(a)) + std::cout << "Error: Expected a to not points to an E." << '\n'; + else + std::cout << "But, a does not point to an E" << '\n'; + + E e; + C* cp = &e; + if(D* dp = boost::typeindex::runtime_cast(cp)) + std::cout << "Yes, we can cross-cast from a C* to a D* when we actually have an E." << '\n'; + else + std::cout << "Error: Expected cp to point to a D" << '\n'; + +/*` + Alternatively, we can use runtime_pointer_cast so we don't need to specity the target as a pointer. + This works for smart_ptr types too. +*/ + A* ap = &e; + if(B* bp = boost::typeindex::runtime_pointer_cast(ap)) + std::cout << "Yes, we can cross-cast and up-cast at the same time." << '\n'; + else + std::cout << "Error: Expected ap to point to a B" << '\n'; + + return 0; +} + +//] [/type_index_runtime_cast_example] diff --git a/include/boost/type_index/runtime_cast.hpp b/include/boost/type_index/runtime_cast.hpp index fa62a68..692cd1b 100644 --- a/include/boost/type_index/runtime_cast.hpp +++ b/include/boost/type_index/runtime_cast.hpp @@ -12,6 +12,10 @@ /// \file runtime_cast.hpp /// \brief Contains the basic utilities necessary to fully emulate /// dynamic_cast for language level constructs (raw pointers and references). +/// +/// boost::typeindex::runtime_cast is a drop in replacement for dynamic_cast +/// that can be used in situations where traditional rtti is either unavailable +/// or undesirable. #include #include diff --git a/include/boost/type_index/runtime_cast/boost_shared_ptr_cast.hpp b/include/boost/type_index/runtime_cast/boost_shared_ptr_cast.hpp index 0dc9b0c..cdde1fb 100644 --- a/include/boost/type_index/runtime_cast/boost_shared_ptr_cast.hpp +++ b/include/boost/type_index/runtime_cast/boost_shared_ptr_cast.hpp @@ -23,6 +23,16 @@ namespace boost { namespace typeindex { +/// \brief Creates a new instance of std::shared_ptr whose stored pointer is obtained from u's +/// stored pointer using a runtime_cast. +/// +/// The new shared_ptr will share ownership with u, except that it is empty if the runtime_cast +/// performed by runtime_pointer_cast returns a null pointer. +/// \tparam T The desired target type to return a pointer of. +/// \tparam U A complete class type of the source instance pointed to from u. +/// \return If there exists a valid conversion from U* to T*, returns a boost::shared_ptr +/// that points to an address suitably offset from u. +/// If no such conversion exists, returns boost::shared_ptr(); template boost::shared_ptr runtime_pointer_cast(boost::shared_ptr const& u) { T* value = detail::runtime_cast_impl(u.get(), boost::is_base_and_derived()); diff --git a/include/boost/type_index/runtime_cast/pointer_cast.hpp b/include/boost/type_index/runtime_cast/pointer_cast.hpp index f76b0ba..8f6c3d4 100644 --- a/include/boost/type_index/runtime_cast/pointer_cast.hpp +++ b/include/boost/type_index/runtime_cast/pointer_cast.hpp @@ -10,13 +10,8 @@ #define BOOST_TYPE_INDEX_RUNTIME_CAST_POINTER_CAST_HPP /// \file pointer_class.hpp -/// \brief Contains the overload of boost::typeindex::runtime_cast for +/// \brief Contains the function overloads of boost::typeindex::runtime_cast for /// pointer types. -/// -/// boost::typeindex::runtime_cast can be used to emulate dynamic_cast -/// functionality on platorms that don't provide it or should the user -/// desire opt in functionality instead of enabling it system wide. - #include #include #include @@ -28,23 +23,47 @@ namespace boost { namespace typeindex { +/// \brief Safely converts pointers to classes up, down, and sideways along the inheritance hierarchy. +/// \tparam T The desired target type. Like dynamic_cast, must be a pointer to complete class type. +/// \tparam U A complete class type of the source instance, u. +/// \return If there exists a valid conversion from U* to T, returns a T that points to +/// an address suitably offset from u. If no such conversion exists, returns NULL. template T runtime_cast(U* u) BOOST_NOEXCEPT { typedef typename boost::remove_pointer::type impl_type; return detail::runtime_cast_impl(u, boost::is_base_and_derived()); } +/// \brief Safely converts pointers to classes up, down, and sideways along the inheritance hierarchy. +/// \tparam T The desired target type. Like dynamic_cast, must be a pointer to complete class type. +/// \tparam U A complete class type of the source instance, u. +/// \return If there exists a valid conversion from U* to T, returns a T that points to +/// an address suitably offset from u. If no such conversion exists, returns NULL. template T runtime_cast(U const* u) BOOST_NOEXCEPT { typedef typename boost::remove_pointer::type impl_type; return detail::runtime_cast_impl(u, boost::is_base_and_derived()); } +/// \brief Safely converts pointers to classes up, down, and sideways along the inheritance +/// hierarchy. +/// \tparam T The desired target type to return a pointer to. +/// \tparam U A complete class type of the source instance, u. +/// \return If there exists a valid conversion from U const* to T*, returns a T* +/// that points to an address suitably offset from u. +/// If no such conversion exists, returns NULL. template T* runtime_pointer_cast(U* u) BOOST_NOEXCEPT { return detail::runtime_cast_impl(u, boost::is_base_and_derived()); } +/// \brief Safely converts pointers to classes up, down, and sideways along the inheritance +/// hierarchy. +/// \tparam T The desired target type to return a pointer to. +/// \tparam U A complete class type of the source instance, u. +/// \return If there exists a valid conversion from U const* to T const*, returns a T const* +/// that points to an address suitably offset from u. +/// If no such conversion exists, returns NULL. template T const* runtime_pointer_cast(U const* u) BOOST_NOEXCEPT { return detail::runtime_cast_impl(u, boost::is_base_and_derived()); diff --git a/include/boost/type_index/runtime_cast/reference_cast.hpp b/include/boost/type_index/runtime_cast/reference_cast.hpp index 9815832..f814547 100644 --- a/include/boost/type_index/runtime_cast/reference_cast.hpp +++ b/include/boost/type_index/runtime_cast/reference_cast.hpp @@ -6,8 +6,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_TYPE_INDEX_RUNTIME_REFERENCE_CAST_HPP -#define BOOST_TYPE_INDEX_RUNTIME_REFERENCE_CAST_HPP +#ifndef BOOST_TYPE_INDEX_RUNTIME_CAST_REFERENCE_CAST_HPP +#define BOOST_TYPE_INDEX_RUNTIME_CAST_REFERENCE_CAST_HPP /// \file reference_cast.hpp /// \brief Contains the overload of boost::typeindex::runtime_cast for @@ -25,6 +25,11 @@ namespace boost { namespace typeindex { +/// \brief Safely converts references to classes up, down, and sideways along the inheritance hierarchy. +/// \tparam T The desired target type. Like dynamic_cast, must be a pointer to complete class type. +/// \tparam U A complete class type of the source instance, u. +/// \return If there exists a valid conversion from U& to T, returns a T that references an address +/// suitably offset from u. If no such conversion exists, throws std::bad_cast() template T& runtime_cast(U& u) { typedef typename boost::remove_reference::type impl_type; @@ -34,6 +39,11 @@ T& runtime_cast(U& u) { return *value; } +/// \brief Safely converts references to classes up, down, and sideways along the inheritance hierarchy. +/// \tparam T The desired target type. Like dynamic_cast, must be a pointer to complete class type. +/// \tparam U A complete class type of the source instance, u. +/// \return If there exists a valid conversion from U const& to T const, returns a T const that references an address +/// suitably offset from u. If no such conversion exists, throws std::bad_cast() template T const& runtime_cast(U const& u) { typedef typename boost::remove_reference::type impl_type; @@ -45,4 +55,4 @@ T const& runtime_cast(U const& u) { }} // namespace boost::typeindex -#endif // BOOST_TYPE_INDEX_RUNTIME_REFERENCE_CAST_HPP \ No newline at end of file +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_REFERENCE_CAST_HPP \ No newline at end of file diff --git a/include/boost/type_index/runtime_cast/register_runtime_class.hpp b/include/boost/type_index/runtime_cast/register_runtime_class.hpp index 49883a0..cd0e8dd 100644 --- a/include/boost/type_index/runtime_cast/register_runtime_class.hpp +++ b/include/boost/type_index/runtime_cast/register_runtime_class.hpp @@ -11,11 +11,6 @@ /// \file register_runtime_class.hpp /// \brief Contains the macro BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS -/// -/// BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS is used to provide -/// boost::typeindex::runtime_cast with the information necessary to perform -/// the dynamic_cast emulation it was designed to do. - #include #include @@ -34,16 +29,60 @@ inline type_index runtime_class_construct_type_id(T const*) { } // namespace detail +/// @cond + #define BOOST_TYPE_INDEX_CHECK_BASE_(r, data, Base) \ if(void const* ret_val = this->Base::boost_type_index_find_instance_(idx)) return ret_val; -#define BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(base_list) \ +/// @endcond + +/// \def BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS +/// \brief Macro used to make a class compatible with boost::typeindex::runtime_cast +/// +/// BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS generates a virtual function +/// in the current class that, when combined with the supplied base class information, allows +/// boost::typeindex::runtime_cast to accurately convert between dynamic types of instances of +/// the current class. +/// +/// \b Example: +/// \code +/// struct base1 { +/// BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) +/// virtual ~base1(); +/// }; +/// +/// struct base2 { +/// BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) +/// virtual ~base2(); +/// }; +/// +/// struct derived1 : base1 { +/// BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base1)) +/// }; +/// +/// struct derived2 : base1, base2 { +/// BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base1)(base2)) +/// }; +/// +/// ... +/// +/// base1* pb1 = get_object(); +/// if(derived2* pb2 = boost::typeindex::runtime_cast(pb1)) +/// { /* ... */ } +/// \endcode +/// +/// \param base_class_seq A Boost.Preprocessor sequence of the current class' direct bases, or +/// BOOST_PP_SEQ_NIL if this class has no direct base classes. +#define BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(base_class_seq) \ + BOOST_TYPE_INDEX_REGISTER_CLASS \ virtual void const* boost_type_index_find_instance_(boost::typeindex::type_index const& idx) const BOOST_NOEXCEPT { \ if(idx == boost::typeindex::detail::runtime_class_construct_type_id(this)) \ return this; \ - BOOST_PP_SEQ_FOR_EACH(BOOST_TYPE_INDEX_CHECK_BASE_, _, base_list) \ + BOOST_PP_SEQ_FOR_EACH(BOOST_TYPE_INDEX_CHECK_BASE_, _, base_class_seq) \ return NULL; \ } }} // namespace boost::typeindex -#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_HPP \ No newline at end of file +#define BOOST_TYPE_INDEX_NO_BASE_CLASS BOOST_PP_SEQ_NIL + +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_REGISTER_RUNTIME_CLASS_HPP \ No newline at end of file diff --git a/include/boost/type_index/runtime_cast/std_shared_ptr_cast.hpp b/include/boost/type_index/runtime_cast/std_shared_ptr_cast.hpp index e826640..12e8f1c 100644 --- a/include/boost/type_index/runtime_cast/std_shared_ptr_cast.hpp +++ b/include/boost/type_index/runtime_cast/std_shared_ptr_cast.hpp @@ -9,7 +9,7 @@ #ifndef BOOST_TYPE_INDEX_RUNTIME_CAST_STD_SHARED_PTR_CAST_HPP #define BOOST_TYPE_INDEX_RUNTIME_CAST_STD_SHARED_PTR_CAST_HPP -/// \file boost_shared_ptr_cast.hpp +/// \file std_shared_ptr_cast.hpp /// \brief Contains the overload of boost::typeindex::runtime_pointer_cast for /// std::shared_ptr types. @@ -23,6 +23,16 @@ namespace boost { namespace typeindex { +/// \brief Creates a new instance of std::shared_ptr whose stored pointer is obtained from u's +/// stored pointer using a runtime_cast. +/// +/// The new shared_ptr will share ownership with u, except that it is empty if the runtime_cast +/// performed by runtime_pointer_cast returns a null pointer. +/// \tparam T The desired target type to return a pointer of. +/// \tparam U A complete class type of the source instance pointed to from u. +/// \return If there exists a valid conversion from U* to T*, returns a std::shared_ptr +/// that points to an address suitably offset from u. +/// If no such conversion exists, returns std::shared_ptr(); template std::shared_ptr runtime_pointer_cast(std::shared_ptr const& u) { T* value = detail::runtime_cast_impl(u.get(), boost::is_base_and_derived()); @@ -33,4 +43,4 @@ std::shared_ptr runtime_pointer_cast(std::shared_ptr const& u) { }} // namespace boost::typeindex -#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_BOOST_SHARED_PTR_CAST_HPP \ No newline at end of file +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_STD_SHARED_PTR_CAST_HPP \ No newline at end of file diff --git a/test/type_index_runtime_cast_test.cpp b/test/type_index_runtime_cast_test.cpp index 152a4b2..2bf5b9d 100644 --- a/test/type_index_runtime_cast_test.cpp +++ b/test/type_index_runtime_cast_test.cpp @@ -26,7 +26,7 @@ std::string name; struct base { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_PP_SEQ_NIL) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) IMPLEMENT_CLASS(base) }; @@ -36,12 +36,12 @@ struct single_derived : base { }; struct base1 { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_PP_SEQ_NIL) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) IMPLEMENT_CLASS(base1) }; struct base2 { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_PP_SEQ_NIL) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) IMPLEMENT_CLASS(base2) }; @@ -66,7 +66,7 @@ struct multiple_virtual_derived : baseV1, baseV2 { }; struct unrelated { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_PP_SEQ_NIL) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) IMPLEMENT_CLASS(unrelated) };