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..539fc83 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/exact_types_match.cpp b/examples/exact_types_match.cpp index f245066..498d662 100644 --- a/examples/exact_types_match.cpp +++ b/examples/exact_types_match.cpp @@ -17,6 +17,8 @@ #include #include #include +#include + //<- // Making `#include ` visible in docs, while actually using hand-made check // instead of `assert`. This is required to verify correct behavior even if NDEBUG diff --git a/examples/inheritance.cpp b/examples/inheritance.cpp index c263439..3da7527 100644 --- a/examples/inheritance.cpp +++ b/examples/inheritance.cpp @@ -13,6 +13,7 @@ */ #include +#include #include struct A { @@ -21,6 +22,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 +33,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 d; + const A& d_as_a = d; + print_real_type(d_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..43e7aa7 --- /dev/null +++ b/examples/runtime_cast.cpp @@ -0,0 +1,90 @@ +// +// 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: " + << a << "->" << cp << '\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: " + << a << "->" << ce << '\n'; + } + else { + std::cout << "But, a does not point to an E" << '\n'; + } + + E e; + C* cp2 = &e; + if(D* dp = boost::typeindex::runtime_cast(cp2)) { + std::cout << "Yes, we can cross-cast from a C* to a D* when we actually have an E: " + << cp2 << "->" << dp << '\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." + << ap << "->" << bp << '\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 new file mode 100644 index 0000000..c72b119 --- /dev/null +++ b/include/boost/type_index/runtime_cast.hpp @@ -0,0 +1,28 @@ +// +// 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) +// + +#ifndef BOOST_TYPE_INDEX_RUNTIME_CAST_HPP +#define BOOST_TYPE_INDEX_RUNTIME_CAST_HPP + +/// \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 +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_HPP 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 new file mode 100644 index 0000000..e31f19d --- /dev/null +++ b/include/boost/type_index/runtime_cast/boost_shared_ptr_cast.hpp @@ -0,0 +1,46 @@ +// +// 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) +// + +#ifndef BOOST_TYPE_INDEX_RUNTIME_CAST_BOOST_SHARED_PTR_CAST_HPP +#define BOOST_TYPE_INDEX_RUNTIME_CAST_BOOST_SHARED_PTR_CAST_HPP + +/// \file boost_shared_ptr_cast.hpp +/// \brief Contains the overload of boost::typeindex::runtime_pointer_cast for +/// boost::shared_ptr types. + +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +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()); + if(value) + return boost::shared_ptr(u, value); + return boost::shared_ptr(); +} + +}} // namespace boost::typeindex + +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_BOOST_SHARED_PTR_CAST_HPP diff --git a/include/boost/type_index/runtime_cast/detail/runtime_cast_impl.hpp b/include/boost/type_index/runtime_cast/detail/runtime_cast_impl.hpp new file mode 100644 index 0000000..8164eda --- /dev/null +++ b/include/boost/type_index/runtime_cast/detail/runtime_cast_impl.hpp @@ -0,0 +1,57 @@ +// +// 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) +// + +#ifndef BOOST_TYPE_INDEX_RUNTIME_CAST_DETAIL_RUNTIME_CAST_IMPL_HPP +#define BOOST_TYPE_INDEX_RUNTIME_CAST_DETAIL_RUNTIME_CAST_IMPL_HPP + +/// \file pointer_class.hpp +/// \brief Contains the overload 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 + +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +namespace boost { namespace typeindex { + +namespace detail { + +template +T* runtime_cast_impl(U* u, boost::true_type) BOOST_NOEXCEPT { + return u; +} + +template +T const* runtime_cast_impl(U const* u, boost::true_type) BOOST_NOEXCEPT { + return u; +} + +template +T* runtime_cast_impl(U* u, boost::false_type) BOOST_NOEXCEPT { + return const_cast(static_cast( + u->boost_type_index_find_instance_(boost::typeindex::type_id()) + )); +} + +template +T const* runtime_cast_impl(U const* u, boost::false_type) BOOST_NOEXCEPT { + return static_cast(u->boost_type_index_find_instance_(boost::typeindex::type_id())); +} + +} // namespace detail + +}} // namespace boost::typeindex + +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_DETAIL_RUNTIME_CAST_IMPL_HPP diff --git a/include/boost/type_index/runtime_cast/pointer_cast.hpp b/include/boost/type_index/runtime_cast/pointer_cast.hpp new file mode 100644 index 0000000..49a7613 --- /dev/null +++ b/include/boost/type_index/runtime_cast/pointer_cast.hpp @@ -0,0 +1,74 @@ +// +// 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) +// + +#ifndef BOOST_TYPE_INDEX_RUNTIME_CAST_POINTER_CAST_HPP +#define BOOST_TYPE_INDEX_RUNTIME_CAST_POINTER_CAST_HPP + +/// \file pointer_class.hpp +/// \brief Contains the function overloads of boost::typeindex::runtime_cast for +/// pointer types. +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +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()); +} + +}} // namespace boost::typeindex + +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_POINTER_CAST_HPP diff --git a/include/boost/type_index/runtime_cast/reference_cast.hpp b/include/boost/type_index/runtime_cast/reference_cast.hpp new file mode 100644 index 0000000..10b5dc1 --- /dev/null +++ b/include/boost/type_index/runtime_cast/reference_cast.hpp @@ -0,0 +1,65 @@ +// +// 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) +// + +#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 +/// reference types. + +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +namespace boost { namespace typeindex { + +/// \brief Indicates that runtime_cast was unable to perform the desired cast operation +/// because the source instance was not also an instance of the target type. +struct bad_runtime_cast : std::exception +{}; + +/// \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 boost::typeindex::bad_runtime_cast. +template +T& runtime_cast(U& u) { + typedef typename boost::remove_reference::type impl_type; + impl_type* value = detail::runtime_cast_impl( + boost::addressof(u), boost::is_base_and_derived()); + if(!value) + BOOST_THROW_EXCEPTION(bad_runtime_cast()); + 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 boost::typeindex::bad_runtime_cast. +template +T const& runtime_cast(U const& u) { + typedef typename boost::remove_reference::type impl_type; + impl_type* value = detail::runtime_cast_impl( + boost::addressof(u), boost::is_base_and_derived()); + if(!value) + BOOST_THROW_EXCEPTION(bad_runtime_cast()); + return *value; +} + +}} // namespace boost::typeindex + +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_REFERENCE_CAST_HPP diff --git a/include/boost/type_index/runtime_cast/register_runtime_class.hpp b/include/boost/type_index/runtime_cast/register_runtime_class.hpp new file mode 100644 index 0000000..37ce686 --- /dev/null +++ b/include/boost/type_index/runtime_cast/register_runtime_class.hpp @@ -0,0 +1,88 @@ +// +// 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) +// + +#ifndef BOOST_TYPE_INDEX_RUNTIME_CAST_REGISTER_RUNTIME_CLASS_HPP +#define BOOST_TYPE_INDEX_RUNTIME_CAST_REGISTER_RUNTIME_CLASS_HPP + +/// \file register_runtime_class.hpp +/// \brief Contains the macro BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +namespace boost { namespace typeindex { + +namespace detail { + +template +inline type_index runtime_class_construct_type_id(T const*) { + return type_id(); +} + +} // 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; + +/// @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_class_seq) \ + return NULL; \ + } +}} // namespace boost::typeindex + +#define BOOST_TYPE_INDEX_NO_BASE_CLASS BOOST_PP_SEQ_NIL + +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_REGISTER_RUNTIME_CLASS_HPP 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 new file mode 100644 index 0000000..277a524 --- /dev/null +++ b/include/boost/type_index/runtime_cast/std_shared_ptr_cast.hpp @@ -0,0 +1,46 @@ +// +// 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) +// + +#ifndef BOOST_TYPE_INDEX_RUNTIME_CAST_STD_SHARED_PTR_CAST_HPP +#define BOOST_TYPE_INDEX_RUNTIME_CAST_STD_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. + +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +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()); + if(value) + return std::shared_ptr(u, value); + return std::shared_ptr(); +} + +}} // namespace boost::typeindex + +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_STD_SHARED_PTR_CAST_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6986c96..8a1d3a4 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -40,6 +40,7 @@ exe testing_crossmodule_anonymous_no_rtti : testing_crossmodule_anonymous.cpp te test-suite type_index : [ run type_index_test.cpp ] + [ run type_index_runtime_cast_test.cpp ] [ run type_index_constexpr_test.cpp ] [ run type_index_test.cpp : : : off $(norttidefines) : type_index_test_no_rtti ] [ run ctti_print_name.cpp : : : always_show_run_output ] diff --git a/test/type_index_runtime_cast_test.cpp b/test/type_index_runtime_cast_test.cpp new file mode 100644 index 0000000..c64222a --- /dev/null +++ b/test/type_index_runtime_cast_test.cpp @@ -0,0 +1,273 @@ +// +// Copyright 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) + +// #include +// #include + +#include +#include +#include +#include + +#include + + +// Classes include a member variable "name" with the +// name of the class hard coded so we can be sure that +// the pointer offsets are all working, since we're doing +// a cast from void* at some point. + +#define IMPLEMENT_CLASS(type_name) \ + type_name() : name( #type_name ) {} \ + std::string name; + +struct base { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) + IMPLEMENT_CLASS(base) +}; + +struct single_derived : base { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) + IMPLEMENT_CLASS(single_derived) +}; + +struct base1 { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) + IMPLEMENT_CLASS(base1) +}; + +struct base2 { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) + IMPLEMENT_CLASS(base2) +}; + +struct multiple_derived : base1, base2 { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base1)(base2)) + IMPLEMENT_CLASS(multiple_derived) +}; + +struct baseV1 : virtual base { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) + IMPLEMENT_CLASS(baseV1) +}; + +struct baseV2 : virtual base { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) + IMPLEMENT_CLASS(baseV2) +}; + +struct multiple_virtual_derived : baseV1, baseV2 { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((baseV1)(baseV2)) + IMPLEMENT_CLASS(multiple_virtual_derived) +}; + +struct unrelated { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) + IMPLEMENT_CLASS(unrelated) +}; + +struct unrelated_with_base : base { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) + IMPLEMENT_CLASS(unrelated_with_base) +}; + +struct unrelatedV1 : virtual base { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) + IMPLEMENT_CLASS(unrelatedV1) +}; + +struct level1_a : base { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) + IMPLEMENT_CLASS(level1_a) +}; + +struct level1_b : base { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) + IMPLEMENT_CLASS(level1_b) +}; + +struct level2 : level1_a, level1_b { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((level1_a)(level1_b)) + IMPLEMENT_CLASS(level2) +}; + +void no_base() +{ + using namespace boost::typeindex; + base b; + base* b2 = runtime_pointer_cast(&b); + BOOST_TEST_NE(b2, (base*)NULL); + BOOST_TEST_EQ(b2->name, "base"); + + BOOST_TEST_EQ(runtime_pointer_cast(&b), (unrelated*)NULL); + BOOST_TEST_EQ(runtime_pointer_cast(&b), (single_derived*)NULL); + BOOST_TEST_EQ(runtime_pointer_cast(&b), (unrelatedV1*)NULL); + BOOST_TEST_EQ(runtime_pointer_cast(&b), (unrelated_with_base*)NULL); +} + +void single_base() +{ + using namespace boost::typeindex; + single_derived d; + base* b = &d; + single_derived* d2 = runtime_pointer_cast(b); + BOOST_TEST_NE(d2, (single_derived*)NULL); + BOOST_TEST_EQ(d2->name, "single_derived"); + + BOOST_TEST_EQ(runtime_pointer_cast(&d), (unrelated*)NULL); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)NULL); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated_with_base*)NULL); +} + +void multiple_base() +{ + using namespace boost::typeindex; + multiple_derived d; + base1* b1 = &d; + multiple_derived* d2 = runtime_pointer_cast(b1); + BOOST_TEST_NE(d2, (multiple_derived*)NULL); + BOOST_TEST_EQ(d2->name, "multiple_derived"); + + base2* b2 = runtime_pointer_cast(b1); + BOOST_TEST_NE(b2, (base2*)NULL); + BOOST_TEST_EQ(b2->name, "base2"); + + BOOST_TEST_EQ(runtime_pointer_cast(&d), (unrelated*)NULL); + BOOST_TEST_EQ(runtime_pointer_cast(b1), (unrelated*)NULL); + BOOST_TEST_EQ(runtime_pointer_cast(b1), (unrelated_with_base*)NULL); +} + +void virtual_base() +{ + using namespace boost::typeindex; + multiple_virtual_derived d; + base* b = &d; + multiple_virtual_derived* d2 = runtime_pointer_cast(b); + baseV1* bv1 = runtime_pointer_cast(b); + baseV2* bv2 = runtime_pointer_cast(b); + + BOOST_TEST_NE(d2, (multiple_virtual_derived*)NULL); + BOOST_TEST_EQ(d2->name, "multiple_virtual_derived"); + + BOOST_TEST_NE(bv1, (baseV1*)NULL); + BOOST_TEST_EQ(bv1->name, "baseV1"); + + BOOST_TEST_NE(bv2, (baseV2*)NULL); + BOOST_TEST_EQ(bv2->name, "baseV2"); + + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)NULL); + BOOST_TEST_EQ(runtime_pointer_cast(&d), (unrelated*)NULL); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated_with_base*)NULL); +} + +void pointer_interface() +{ + using namespace boost::typeindex; + single_derived d; + base* b = &d; + single_derived* d2 = runtime_cast(b); + BOOST_TEST_NE(d2, (single_derived*)NULL); + BOOST_TEST_EQ(d2->name, "single_derived"); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)NULL); +} + +void reference_interface() +{ + using namespace boost::typeindex; + single_derived d; + base& b = d; + single_derived& d2 = runtime_cast(b); + BOOST_TEST_EQ(d2.name, "single_derived"); + + try { + unrelated& u = runtime_cast(b); + (void)u; + BOOST_TEST(!"should throw bad_runtime_cast"); + } + catch(boost::typeindex::bad_runtime_cast&) { + } + catch(...) { + BOOST_TEST(!"should throw bad_runtime_cast"); + } +} + +void const_pointer_interface() +{ + using namespace boost::typeindex; + const single_derived d; + base const* b = &d; + single_derived const* d2 = runtime_cast(b); + BOOST_TEST_NE(d2, (single_derived*)NULL); + BOOST_TEST_EQ(d2->name, "single_derived"); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)NULL); +} + +void const_reference_interface() +{ + using namespace boost::typeindex; + const single_derived d; + base const& b = d; + single_derived const& d2 = runtime_cast(b); + BOOST_TEST_EQ(d2.name, "single_derived"); + + try { + unrelated const& u = runtime_cast(b); + (void)u; + BOOST_TEST(!"should throw bad_runtime_cast"); + } + catch(boost::typeindex::bad_runtime_cast&) { + } + catch(...) { + BOOST_TEST(!"should throw bad_runtime_cast"); + } +} + +void diamond_non_virtual() +{ + using namespace boost::typeindex; + level2 inst; + level1_a* l1a = &inst; + base* b1 = l1a; + level1_b* l1_b = runtime_cast(b1); + BOOST_TEST_EQ(l1_b->name, "level1_b"); + BOOST_TEST_NE(l1_b, (level1_b*)NULL); +} + +void boost_shared_ptr() +{ + using namespace boost::typeindex; + boost::shared_ptr d = boost::make_shared(); + boost::shared_ptr b = d; + boost::shared_ptr d2 = runtime_pointer_cast(b); + BOOST_TEST_NE(d2, boost::shared_ptr()); + BOOST_TEST_EQ(d2->name, "single_derived"); +} + +void std_shared_ptr() +{ + using namespace boost::typeindex; + std::shared_ptr d = std::make_shared(); + std::shared_ptr b = d; + std::shared_ptr d2 = runtime_pointer_cast(b); + BOOST_TEST_NE(d2, std::shared_ptr()); + BOOST_TEST_EQ(d2->name, "single_derived"); +} + +int main() { + no_base(); + single_derived(); + multiple_base(); + virtual_base(); + pointer_interface(); + reference_interface(); + const_pointer_interface(); + const_reference_interface(); + diamond_non_virtual(); + boost_shared_ptr(); + std_shared_ptr(); + return boost::report_errors(); +}