From 12751e0f83c9b337b2f643129394c9fc3b24dab0 Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Tue, 9 Aug 2016 14:51:36 -0400 Subject: [PATCH] Split runtime_cast into several files so clients can selectively include only what they use. Add support for shared_ptrs. --- include/boost/type_index/runtime_cast.hpp | 115 +----------------- .../runtime_cast/boost_shared_ptr_cast.hpp | 36 ++++++ .../runtime_cast/detail/runtime_cast_impl.hpp | 57 +++++++++ .../type_index/runtime_cast/pointer_cast.hpp | 55 +++++++++ .../runtime_cast/reference_cast.hpp | 48 ++++++++ .../runtime_cast/register_runtime_class.hpp | 59 +++++++++ .../runtime_cast/std_shared_ptr_cast.hpp | 36 ++++++ test/Jamfile.v2 | 2 +- ...t.cpp => type_index_runtime_cast_test.cpp} | 55 ++++++--- 9 files changed, 339 insertions(+), 124 deletions(-) create mode 100644 include/boost/type_index/runtime_cast/boost_shared_ptr_cast.hpp create mode 100644 include/boost/type_index/runtime_cast/detail/runtime_cast_impl.hpp create mode 100644 include/boost/type_index/runtime_cast/pointer_cast.hpp create mode 100644 include/boost/type_index/runtime_cast/reference_cast.hpp create mode 100644 include/boost/type_index/runtime_cast/register_runtime_class.hpp create mode 100644 include/boost/type_index/runtime_cast/std_shared_ptr_cast.hpp rename test/{runtime_cast_test.cpp => type_index_runtime_cast_test.cpp} (76%) diff --git a/include/boost/type_index/runtime_cast.hpp b/include/boost/type_index/runtime_cast.hpp index 0221cde..fa62a68 100644 --- a/include/boost/type_index/runtime_cast.hpp +++ b/include/boost/type_index/runtime_cast.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) CHris Glover, 2016. +// Copyright (c) Chris Glover, 2016. // // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -10,118 +10,15 @@ #define BOOST_TYPE_INDEX_RUNTIME_CAST_HPP /// \file runtime_cast.hpp -/// \brief Contains boost::typeindex::ctti_type_index class that is constexpr if C++14 constexpr is supported by compiler. -/// -/// boost::typeindex::runtime_cast class can be used as a replacement -/// for dynamic_cast on platforms where dynamic_cast is not available -/// or undesirable at a global level. +/// \brief Contains the basic utilities necessary to fully emulate +/// dynamic_cast for language level constructs (raw pointers and references). -#include -#include -#include -#include -#include -#include -#include +#include +#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) { - return u; -} - -template -T const* runtime_cast_impl(U const* u, boost::true_type) { - return u; -} - -template -T* runtime_cast_impl(U* u, boost::false_type) { - 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) { - return static_cast(u->boost_type_index_find_instance_(boost::typeindex::type_id())); -} - -template -type_index get_type_for_value(T const&) { - return type_id::type>(); -} - -} // namespace detail - -#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_CHECK_BASES(base_list) \ - BOOST_PP_SEQ_FOR_EACH(BOOST_TYPE_INDEX_CHECK_BASE_, _, base_list) - -#define BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI \ - virtual void const* boost_type_index_find_instance_(boost::typeindex::type_index const& idx) const BOOST_NOEXCEPT { \ - if(idx == boost::typeindex::detail::get_type_for_value(*this)) \ - return this; \ - return NULL; \ - } - -#define BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base_list) \ - virtual void const* boost_type_index_find_instance_(boost::typeindex::type_index const& idx) const BOOST_NOEXCEPT { \ - if(idx == boost::typeindex::detail::get_type_for_value(*this)) \ - return this; \ - BOOST_TYPE_INDEX_CHECK_BASES(base_list) \ - return 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()); - } - - 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()); - } - - template - T runtime_cast(U& u) { - typedef typename boost::remove_reference::type impl_type; - impl_type* value = detail::runtime_cast_impl(&u, boost::is_base_and_derived()); - if(!value) - boost::throw_exception(std::bad_cast()); - return *value; - } - - template - T runtime_cast(U const& u) { - typedef typename boost::remove_reference::type impl_type; - impl_type* value = detail::runtime_cast_impl(&u, boost::is_base_and_derived()); - if(!value) - boost::throw_exception(std::bad_cast()); - return *value; - } - - template - T* runtime_pointer_cast(U* u) BOOST_NOEXCEPT { - return detail::runtime_cast_impl(u, boost::is_base_and_derived()); - } - - 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_HPP \ No newline at end of file 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..0dc9b0c --- /dev/null +++ b/include/boost/type_index/runtime_cast/boost_shared_ptr_cast.hpp @@ -0,0 +1,36 @@ +// +// 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 { + +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 \ No newline at end of file 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..32bfffe --- /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 \ No newline at end of file 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..f76b0ba --- /dev/null +++ b/include/boost/type_index/runtime_cast/pointer_cast.hpp @@ -0,0 +1,55 @@ +// +// 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 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 +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +namespace boost { namespace typeindex { + +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()); +} + +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()); +} + +template +T* runtime_pointer_cast(U* u) BOOST_NOEXCEPT { + return detail::runtime_cast_impl(u, boost::is_base_and_derived()); +} + +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 \ No newline at end of file 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..9815832 --- /dev/null +++ b/include/boost/type_index/runtime_cast/reference_cast.hpp @@ -0,0 +1,48 @@ +// +// 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_REFERENCE_CAST_HPP +#define BOOST_TYPE_INDEX_RUNTIME_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 { + +template +T& runtime_cast(U& u) { + typedef typename boost::remove_reference::type impl_type; + impl_type* value = detail::runtime_cast_impl(&u, boost::is_base_and_derived()); + if(!value) + boost::throw_exception(std::bad_cast()); + return *value; +} + +template +T const& runtime_cast(U const& u) { + typedef typename boost::remove_reference::type impl_type; + impl_type* value = detail::runtime_cast_impl(&u, boost::is_base_and_derived()); + if(!value) + boost::throw_exception(std::bad_cast()); + return *value; +} + +}} // namespace boost::typeindex + +#endif // BOOST_TYPE_INDEX_RUNTIME_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 new file mode 100644 index 0000000..569666a --- /dev/null +++ b/include/boost/type_index/runtime_cast/register_runtime_class.hpp @@ -0,0 +1,59 @@ +// +// 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 +/// +/// 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 + +#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 + +#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_CHECK_BASES(base_list) \ + BOOST_PP_SEQ_FOR_EACH(BOOST_TYPE_INDEX_CHECK_BASE_, _, base_list) + +#define BOOST_TYPE_INDEX_REGISTER_RUNTIME_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; \ + return NULL; \ + } + +#define BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES(base_list) \ + 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_TYPE_INDEX_CHECK_BASES(base_list) \ + return NULL; \ + } +}} // namespace boost::typeindex + +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_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 new file mode 100644 index 0000000..e826640 --- /dev/null +++ b/include/boost/type_index/runtime_cast/std_shared_ptr_cast.hpp @@ -0,0 +1,36 @@ +// +// 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 boost_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 { + +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_BOOST_SHARED_PTR_CAST_HPP \ No newline at end of file diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 62b7e20..8a1d3a4 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -40,7 +40,7 @@ exe testing_crossmodule_anonymous_no_rtti : testing_crossmodule_anonymous.cpp te test-suite type_index : [ run type_index_test.cpp ] - [ run runtime_cast_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/runtime_cast_test.cpp b/test/type_index_runtime_cast_test.cpp similarity index 76% rename from test/runtime_cast_test.cpp rename to test/type_index_runtime_cast_test.cpp index a456eac..4ceebf9 100644 --- a/test/runtime_cast_test.cpp +++ b/test/type_index_runtime_cast_test.cpp @@ -5,7 +5,13 @@ // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +// #include +// #include + #include +#include +#include +#include #include @@ -20,72 +26,72 @@ std::string name; struct base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS IMPLEMENT_CLASS(base) }; struct single_derived : base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) IMPLEMENT_CLASS(single_derived) }; struct base1 { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS IMPLEMENT_CLASS(base1) }; struct base2 { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS IMPLEMENT_CLASS(base2) }; struct multiple_derived : base1, base2 { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base1)(base2)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base1)(base2)) IMPLEMENT_CLASS(multiple_derived) }; struct baseV1 : virtual base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) IMPLEMENT_CLASS(baseV1) }; struct baseV2 : virtual base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) IMPLEMENT_CLASS(baseV2) }; struct multiple_virtual_derived : baseV1, baseV2 { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((baseV1)(baseV2)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((baseV1)(baseV2)) IMPLEMENT_CLASS(multiple_virtual_derived) }; struct unrelated { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS IMPLEMENT_CLASS(unrelated) }; struct unrelated_with_base : base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) IMPLEMENT_CLASS(unrelated_with_base) }; struct unrelatedV1 : virtual base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) IMPLEMENT_CLASS(unrelatedV1) }; struct level1_a : base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) IMPLEMENT_CLASS(level1_a) }; struct level1_b : base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) IMPLEMENT_CLASS(level1_b) }; struct level2 : level1_a, level1_b { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((level1_a)(level1_b)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((level1_a)(level1_b)) IMPLEMENT_CLASS(level2) }; @@ -221,9 +227,30 @@ void diamond_non_virtual() 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*)nullptr); } +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();