From b230140529adf869daade2376b480a0014afd467 Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Fri, 15 Jul 2016 08:49:54 -0400 Subject: [PATCH 01/11] First version. --- examples/exact_types_match.cpp | 2 + include/boost/type_index/runtime_cast.hpp | 119 ++++++++++++++++++++++ test/Jamfile.v2 | 1 + test/runtime_cast_test.cpp | 119 ++++++++++++++++++++++ 4 files changed, 241 insertions(+) create mode 100644 include/boost/type_index/runtime_cast.hpp create mode 100644 test/runtime_cast_test.cpp 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/include/boost/type_index/runtime_cast.hpp b/include/boost/type_index/runtime_cast.hpp new file mode 100644 index 0000000..e8223be --- /dev/null +++ b/include/boost/type_index/runtime_cast.hpp @@ -0,0 +1,119 @@ +// +// 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 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. + +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +namespace boost { namespace typeindex { + +namespace detail { + +template +struct find_type; + +template +struct find_type { + void* operator()(Desired* p, type_index const& idx) const BOOST_NOEXCEPT { + if(idx == boost::typeindex::type_id()) + return p; + return nullptr; + } +}; + +template +struct find_type { + template + Desired* check_results(T* t) const BOOST_NOEXCEPT { + if(t) + return static_cast(t); + return nullptr; + } + + template + Desired* check_results(T* t, Rest... rest) const BOOST_NOEXCEPT { + if(t) + return static_cast(t); + return check_results(rest...); + } + + template + void* operator()(T* p, type_index const& idx) const BOOST_NOEXCEPT { + if(auto result = check_results(p->BaseList::boost_type_index_find_instance_(idx)...)) { + return result; + } + else { + return find_type()(p, idx); + } + } +}; + +template +T* runtime_cast_impl(U* u, std::true_type) { + return u; +} + +template +T const* runtime_cast_impl(U const* u, std::true_type) { + return u; +} + +template +T* runtime_cast_impl(U* u, std::false_type) { + return static_cast( + u->boost_type_index_find_instance_(boost::typeindex::type_id()) + ); +} + +template +T const* runtime_cast_impl(U const* u, std::false_type) { + return static_cast( + const_cast(u)->boost_type_index_find_instance_(boost::typeindex::type_id()) + ); +} + +} // namespace detail + +#define BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI \ + virtual void* boost_type_index_find_instance_(boost::typeindex::type_index const& idx) BOOST_NOEXCEPT { \ + return boost::typeindex::detail::find_type::type>()(this, idx); \ + } + +#define BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(...) \ + virtual void* boost_type_index_find_instance_(boost::typeindex::type_index const& idx) BOOST_NOEXCEPT { \ + if(auto ret = boost::typeindex::detail::find_type::type>()(this, idx)) \ + return ret; \ + return boost::typeindex::detail::find_type::type, __VA_ARGS__>()(this, idx);\ + } + + template + T* runtime_cast(U* u) { + return detail::runtime_cast_impl(u, std::is_same()); + } + + template + T const* runtime_cast(U const* u) { + return detail::runtime_cast_impl(u, std::is_same()); + } + +}} // namespace boost::typeindex + +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_HPP \ No newline at end of file diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6986c96..62b7e20 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 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/runtime_cast_test.cpp new file mode 100644 index 0000000..f304679 --- /dev/null +++ b/test/runtime_cast_test.cpp @@ -0,0 +1,119 @@ +// +// 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 + + +// 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_CLASS_RTTI + IMPLEMENT_CLASS(base) +}; + +struct single_derived : base { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) + IMPLEMENT_CLASS(single_derived) +}; + +struct base1 { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI + IMPLEMENT_CLASS(base1) +}; + +struct base2 { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI + IMPLEMENT_CLASS(base2) +}; + +struct multiple_derived : base1, base2 { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base1, base2) + IMPLEMENT_CLASS(multiple_derived) +}; + +struct baseV1 : virtual base { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) + IMPLEMENT_CLASS(baseV1) +}; + +struct baseV2 : virtual base { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) + IMPLEMENT_CLASS(baseV2) +}; + +struct multiple_virtual_derived : baseV1, baseV2 { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(baseV1, baseV2) + IMPLEMENT_CLASS(multiple_virtual_derived) +}; + +void no_base() +{ + using namespace boost::typeindex; + base b; + base* b2 = runtime_cast(&b); + BOOST_TEST_NE(b2, (base*)nullptr); + BOOST_TEST_EQ(b2->name, "base"); +} + +void single_base() +{ + using namespace boost::typeindex; + single_derived d; + base* b = &d; + single_derived* d2 = runtime_cast(b); + BOOST_TEST_NE(d2, (single_derived*)nullptr); + BOOST_TEST_EQ(d2->name, "single_derived"); +} + +void multiple_base() +{ + using namespace boost::typeindex; + multiple_derived d; + base1* b1 = &d; + multiple_derived* d2 = runtime_cast(b1); + BOOST_TEST_NE(d2, (multiple_derived*)nullptr); + BOOST_TEST_EQ(d2->name, "multiple_derived"); + + base2* b2 = runtime_cast(b1); + BOOST_TEST_NE(b2, (base2*)nullptr); + BOOST_TEST_EQ(b2->name, "base2"); +} + +void virtual_base() +{ + using namespace boost::typeindex; + multiple_virtual_derived d; + base* b = &d; + multiple_virtual_derived* d2 = runtime_cast(b); + baseV1* bv1 = runtime_cast(b); + baseV2* bv2 = runtime_cast(b); + + BOOST_TEST_NE(d2, (multiple_virtual_derived*)nullptr); + BOOST_TEST_EQ(d2->name, "multiple_virtual_derived"); + + BOOST_TEST_NE(bv1, (baseV1*)nullptr); + BOOST_TEST_EQ(bv1->name, "baseV1"); + + BOOST_TEST_NE(bv2, (baseV2*)nullptr); + BOOST_TEST_EQ(bv2->name, "baseV2"); +} + +int main() { + no_base(); + single_derived(); + return boost::report_errors(); +} + From 1dc83108268d595f74e2b6f3c6dfc5f33a7ded7c Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Fri, 15 Jul 2016 10:10:07 -0400 Subject: [PATCH 02/11] Optimization: Don't check all bases. --- include/boost/type_index/runtime_cast.hpp | 60 +++++++++-------------- test/runtime_cast_test.cpp | 2 + 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/include/boost/type_index/runtime_cast.hpp b/include/boost/type_index/runtime_cast.hpp index e8223be..e1e8ba5 100644 --- a/include/boost/type_index/runtime_cast.hpp +++ b/include/boost/type_index/runtime_cast.hpp @@ -27,64 +27,52 @@ namespace boost { namespace typeindex { namespace detail { -template +template struct find_type; -template -struct find_type { - void* operator()(Desired* p, type_index const& idx) const BOOST_NOEXCEPT { - if(idx == boost::typeindex::type_id()) - return p; +template +struct find_type { + template + void* operator()(T* p, type_index const& idx) const BOOST_NOEXCEPT { + if(idx == boost::typeindex::type_id()) + return nullptr; return nullptr; } }; -template +template struct find_type { template - Desired* check_results(T* t) const BOOST_NOEXCEPT { - if(t) - return static_cast(t); - return nullptr; + Current* check_current(T* p, type_index const& idx) const BOOST_NOEXCEPT{ + if(idx == boost::typeindex::type_id()) + return nullptr; + return nullptr; } - template - Desired* check_results(T* t, Rest... rest) const BOOST_NOEXCEPT { - if(t) - return static_cast(t); - return check_results(rest...); + template + void* check_bases(T* p, type_index const& idx) const BOOST_NOEXCEPT { + if(void* result = p->FirstBase::boost_type_index_find_instance_(idx)) + return result; + return check_bases(p, idx); } template void* operator()(T* p, type_index const& idx) const BOOST_NOEXCEPT { - if(auto result = check_results(p->BaseList::boost_type_index_find_instance_(idx)...)) { - return result; - } - else { - return find_type()(p, idx); - } + if(Current* current = check_current(p, idx)) + return p; + return check_bases(p, idx); } }; template -T* runtime_cast_impl(U* u, std::true_type) { - return u; -} - -template -T const* runtime_cast_impl(U const* u, std::true_type) { - return u; -} - -template -T* runtime_cast_impl(U* u, std::false_type) { +T* runtime_cast_impl(U* u) { return static_cast( u->boost_type_index_find_instance_(boost::typeindex::type_id()) ); } template -T const* runtime_cast_impl(U const* u, std::false_type) { +T const* runtime_cast_impl(U const* u) { return static_cast( const_cast(u)->boost_type_index_find_instance_(boost::typeindex::type_id()) ); @@ -106,12 +94,12 @@ T const* runtime_cast_impl(U const* u, std::false_type) { template T* runtime_cast(U* u) { - return detail::runtime_cast_impl(u, std::is_same()); + return detail::runtime_cast_impl(u); } template T const* runtime_cast(U const* u) { - return detail::runtime_cast_impl(u, std::is_same()); + return detail::runtime_cast_impl(u); } }} // namespace boost::typeindex diff --git a/test/runtime_cast_test.cpp b/test/runtime_cast_test.cpp index f304679..77aadb0 100644 --- a/test/runtime_cast_test.cpp +++ b/test/runtime_cast_test.cpp @@ -114,6 +114,8 @@ void virtual_base() int main() { no_base(); single_derived(); + multiple_base(); + virtual_base(); return boost::report_errors(); } From ee7b15a493d10bc1fad93a12ab42f2fdfd260810 Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Fri, 15 Jul 2016 08:49:54 -0400 Subject: [PATCH 03/11] First version. TODO: Error messages when using mismatched types are not clear. Needs consideration. --- examples/exact_types_match.cpp | 2 + include/boost/type_index/runtime_cast.hpp | 141 +++++++++++++++ test/Jamfile.v2 | 1 + test/runtime_cast_test.cpp | 210 ++++++++++++++++++++++ 4 files changed, 354 insertions(+) create mode 100644 include/boost/type_index/runtime_cast.hpp create mode 100644 test/runtime_cast_test.cpp 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/include/boost/type_index/runtime_cast.hpp b/include/boost/type_index/runtime_cast.hpp new file mode 100644 index 0000000..333753d --- /dev/null +++ b/include/boost/type_index/runtime_cast.hpp @@ -0,0 +1,141 @@ +// +// 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 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. + +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +namespace boost { namespace typeindex { + +namespace detail { + +template +struct find_type { + template + Current* check_current(T* p, type_index const& idx) const BOOST_NOEXCEPT { + if(idx == boost::typeindex::type_id()) + return p; + return nullptr; + } + + template + void* check_bases(T* p, type_index const& idx) const BOOST_NOEXCEPT { + return nullptr; + } + + template + void* check_bases(T* p, type_index const& idx) const BOOST_NOEXCEPT { + if(void* result = p->FirstBase::boost_type_index_find_instance_(idx)) + return result; + return check_bases(p, idx); + } + + template + void* operator()(T* p, type_index const& idx) const BOOST_NOEXCEPT { + if(Current* current = check_current(p, idx)) + return p; + return check_bases(p, idx); + } +}; + +template +T* runtime_cast_impl(U* u, std::true_type) { + return u; +} + +template +T const* runtime_cast_impl(U const* u, std::true_type) { + return u; +} + +template +T* runtime_cast_impl(U* u, std::false_type) { + return static_cast( + u->boost_type_index_find_instance_(boost::typeindex::type_id()) + ); +} + +template +T const* runtime_cast_impl(U const* u, std::false_type) { + return static_cast( + const_cast(u)->boost_type_index_find_instance_(boost::typeindex::type_id()) + ); +} + +} // namespace detail + +#define BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI \ + virtual void* boost_type_index_find_instance_(boost::typeindex::type_index const& idx) BOOST_NOEXCEPT { \ + if(idx == boost::typeindex::type_id::type>()) \ + return this; \ + return nullptr; \ + } + +#define BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(...) \ + virtual void* boost_type_index_find_instance_(boost::typeindex::type_index const& idx) BOOST_NOEXCEPT { \ + return boost::typeindex::detail::find_type::type, __VA_ARGS__>()(this, idx);\ + } + + template + T runtime_cast(U* u) BOOST_NOEXCEPT { + typedef typename std::remove_pointer::type impl_type; + return detail::runtime_cast_impl(u, std::is_same()); + } + + template + T runtime_cast(U const* u) BOOST_NOEXCEPT { + typedef typename std::remove_pointer::type impl_type; + return detail::runtime_cast_impl(u, std::is_same()); + } + + template + T runtime_cast(U& u) { + typedef typename std::remove_reference::type impl_type; + impl_type* value = detail::runtime_cast_impl(&u, std::is_same()); + if(!value) + boost::throw_exception(std::bad_cast()); + return *value; + } + + template + T runtime_cast(U const& u) { + typedef typename std::remove_reference::type impl_type; + impl_type* value = detail::runtime_cast_impl(&u, std::is_same()); + 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, std::is_same()); + } + + template + T const* runtime_pointer_cast(U const* u) BOOST_NOEXCEPT { + return detail::runtime_cast_impl(u, std::is_same()); + } + +}} // namespace boost::typeindex + +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_HPP \ No newline at end of file diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6986c96..62b7e20 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 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/runtime_cast_test.cpp new file mode 100644 index 0000000..970566d --- /dev/null +++ b/test/runtime_cast_test.cpp @@ -0,0 +1,210 @@ +// +// 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 + + +// 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_CLASS_RTTI + IMPLEMENT_CLASS(base) +}; + +struct single_derived : base { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) + IMPLEMENT_CLASS(single_derived) +}; + +struct base1 { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI + IMPLEMENT_CLASS(base1) +}; + +struct base2 { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI + IMPLEMENT_CLASS(base2) +}; + +struct multiple_derived : base1, base2 { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base1, base2) + IMPLEMENT_CLASS(multiple_derived) +}; + +struct baseV1 : virtual base { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) + IMPLEMENT_CLASS(baseV1) +}; + +struct baseV2 : virtual base { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) + IMPLEMENT_CLASS(baseV2) +}; + +struct multiple_virtual_derived : baseV1, baseV2 { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(baseV1, baseV2) + IMPLEMENT_CLASS(multiple_virtual_derived) +}; + +struct unrelated { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI +}; + +struct unrelated_with_base : base { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) +}; + +struct unrelatedV1 : virtual base { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) +}; + +void no_base() +{ + using namespace boost::typeindex; + base b; + base* b2 = runtime_pointer_cast(&b); + BOOST_TEST_NE(b2, (base*)nullptr); + BOOST_TEST_EQ(b2->name, "base"); + + BOOST_TEST_EQ(runtime_pointer_cast(&b), (unrelated*)nullptr); + BOOST_TEST_EQ(runtime_pointer_cast(&b), (single_derived*)nullptr); + BOOST_TEST_EQ(runtime_pointer_cast(&b), (unrelatedV1*)nullptr); + BOOST_TEST_EQ(runtime_pointer_cast(&b), (unrelated_with_base*)nullptr); +} + +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*)nullptr); + BOOST_TEST_EQ(d2->name, "single_derived"); + + BOOST_TEST_EQ(runtime_pointer_cast(&d), (unrelated*)nullptr); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)nullptr); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated_with_base*)nullptr); +} + +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*)nullptr); + BOOST_TEST_EQ(d2->name, "multiple_derived"); + + base2* b2 = runtime_pointer_cast(b1); + BOOST_TEST_NE(b2, (base2*)nullptr); + BOOST_TEST_EQ(b2->name, "base2"); + + BOOST_TEST_EQ(runtime_pointer_cast(&d), (unrelated*)nullptr); + BOOST_TEST_EQ(runtime_pointer_cast(b1), (unrelated*)nullptr); + BOOST_TEST_EQ(runtime_pointer_cast(b1), (unrelated_with_base*)nullptr); +} + +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*)nullptr); + BOOST_TEST_EQ(d2->name, "multiple_virtual_derived"); + + BOOST_TEST_NE(bv1, (baseV1*)nullptr); + BOOST_TEST_EQ(bv1->name, "baseV1"); + + BOOST_TEST_NE(bv2, (baseV2*)nullptr); + BOOST_TEST_EQ(bv2->name, "baseV2"); + + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)nullptr); + BOOST_TEST_EQ(runtime_pointer_cast(&d), (unrelated*)nullptr); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated_with_base*)nullptr); +} + +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*)nullptr); + BOOST_TEST_EQ(d2->name, "single_derived"); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)nullptr); +} + +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_cast"); + } + catch(...) { + } +} + +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*)nullptr); + BOOST_TEST_EQ(d2->name, "single_derived"); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)nullptr); +} + +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_cast"); + } + catch(...) { + } +} + +int main() { + no_base(); + single_derived(); + multiple_base(); + virtual_base(); + pointer_interface(); + reference_interface(); + const_pointer_interface(); + const_reference_interface(); + return boost::report_errors(); +} + From 0b006b6af644400caab395351b467cdb0b929c43 Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Mon, 8 Aug 2016 12:16:58 -0400 Subject: [PATCH 04/11] Remove all references to std::type_traits Replace variadic template with boost preprocessor. --- include/boost/type_index/runtime_cast.hpp | 100 ++++++++++------------ test/runtime_cast_test.cpp | 43 ++++++++-- 2 files changed, 79 insertions(+), 64 deletions(-) diff --git a/include/boost/type_index/runtime_cast.hpp b/include/boost/type_index/runtime_cast.hpp index 333753d..0221cde 100644 --- a/include/boost/type_index/runtime_cast.hpp +++ b/include/boost/type_index/runtime_cast.hpp @@ -18,7 +18,10 @@ #include #include -#include +#include +#include +#include +#include #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -29,89 +32,72 @@ namespace boost { namespace typeindex { namespace detail { -template -struct find_type { - template - Current* check_current(T* p, type_index const& idx) const BOOST_NOEXCEPT { - if(idx == boost::typeindex::type_id()) - return p; - return nullptr; - } - - template - void* check_bases(T* p, type_index const& idx) const BOOST_NOEXCEPT { - return nullptr; - } - - template - void* check_bases(T* p, type_index const& idx) const BOOST_NOEXCEPT { - if(void* result = p->FirstBase::boost_type_index_find_instance_(idx)) - return result; - return check_bases(p, idx); - } - - template - void* operator()(T* p, type_index const& idx) const BOOST_NOEXCEPT { - if(Current* current = check_current(p, idx)) - return p; - return check_bases(p, idx); - } -}; - template -T* runtime_cast_impl(U* u, std::true_type) { +T* runtime_cast_impl(U* u, boost::true_type) { return u; } template -T const* runtime_cast_impl(U const* u, std::true_type) { +T const* runtime_cast_impl(U const* u, boost::true_type) { return u; } template -T* runtime_cast_impl(U* u, std::false_type) { - return static_cast( +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, std::false_type) { - return static_cast( - const_cast(u)->boost_type_index_find_instance_(boost::typeindex::type_id()) - ); +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_REGISTER_CLASS_RTTI \ - virtual void* boost_type_index_find_instance_(boost::typeindex::type_index const& idx) BOOST_NOEXCEPT { \ - if(idx == boost::typeindex::type_id::type>()) \ - return this; \ - return nullptr; \ +#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(...) \ - virtual void* boost_type_index_find_instance_(boost::typeindex::type_index const& idx) BOOST_NOEXCEPT { \ - return boost::typeindex::detail::find_type::type, __VA_ARGS__>()(this, idx);\ +#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 std::remove_pointer::type impl_type; - return detail::runtime_cast_impl(u, std::is_same()); + 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 std::remove_pointer::type impl_type; - return detail::runtime_cast_impl(u, std::is_same()); + 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 std::remove_reference::type impl_type; - impl_type* value = detail::runtime_cast_impl(&u, std::is_same()); + 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; @@ -119,8 +105,8 @@ T const* runtime_cast_impl(U const* u, std::false_type) { template T runtime_cast(U const& u) { - typedef typename std::remove_reference::type impl_type; - impl_type* value = detail::runtime_cast_impl(&u, std::is_same()); + 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; @@ -128,12 +114,12 @@ T const* runtime_cast_impl(U const* u, std::false_type) { template T* runtime_pointer_cast(U* u) BOOST_NOEXCEPT { - return detail::runtime_cast_impl(u, std::is_same()); + 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, std::is_same()); + return detail::runtime_cast_impl(u, boost::is_base_and_derived()); } }} // namespace boost::typeindex diff --git a/test/runtime_cast_test.cpp b/test/runtime_cast_test.cpp index 970566d..a456eac 100644 --- a/test/runtime_cast_test.cpp +++ b/test/runtime_cast_test.cpp @@ -25,7 +25,7 @@ struct base { }; struct single_derived : base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) IMPLEMENT_CLASS(single_derived) }; @@ -40,35 +40,53 @@ struct base2 { }; struct multiple_derived : base1, base2 { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base1, base2) + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base1)(base2)) IMPLEMENT_CLASS(multiple_derived) }; struct baseV1 : virtual base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) IMPLEMENT_CLASS(baseV1) }; struct baseV2 : virtual base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) IMPLEMENT_CLASS(baseV2) }; struct multiple_virtual_derived : baseV1, baseV2 { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(baseV1, baseV2) + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((baseV1)(baseV2)) IMPLEMENT_CLASS(multiple_virtual_derived) }; struct unrelated { BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI + IMPLEMENT_CLASS(unrelated) }; struct unrelated_with_base : base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) + IMPLEMENT_CLASS(unrelated_with_base) }; struct unrelatedV1 : virtual base { - BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES(base) + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) + IMPLEMENT_CLASS(unrelatedV1) +}; + +struct level1_a : base { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) + IMPLEMENT_CLASS(level1_a) +}; + +struct level1_b : base { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((base)) + IMPLEMENT_CLASS(level1_b) +}; + +struct level2 : level1_a, level1_b { + BOOST_TYPE_INDEX_REGISTER_CLASS_RTTI_BASES((level1_a)(level1_b)) + IMPLEMENT_CLASS(level2) }; void no_base() @@ -196,6 +214,16 @@ void const_reference_interface() } } +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_NE(l1_b, (level1_b*)nullptr); +} + int main() { no_base(); single_derived(); @@ -205,6 +233,7 @@ int main() { reference_interface(); const_pointer_interface(); const_reference_interface(); + diamond_non_virtual(); return boost::report_errors(); } From 12751e0f83c9b337b2f643129394c9fc3b24dab0 Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Tue, 9 Aug 2016 14:51:36 -0400 Subject: [PATCH 05/11] 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(); From 2fb667a6a021b3e0257577a609831c2f22f6154b Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Tue, 9 Aug 2016 23:13:54 -0400 Subject: [PATCH 06/11] Experiment with a version of runtime_cast that has only a single registration macro. --- .../runtime_cast/register_runtime_class.hpp | 22 ++++----------- test/type_index_runtime_cast_test.cpp | 28 +++++++++---------- 2 files changed, 20 insertions(+), 30 deletions(-) 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 569666a..49883a0 100644 --- a/include/boost/type_index/runtime_cast/register_runtime_class.hpp +++ b/include/boost/type_index/runtime_cast/register_runtime_class.hpp @@ -37,22 +37,12 @@ inline type_index runtime_class_construct_type_id(T const*) { #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; \ +#define BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(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_PP_SEQ_FOR_EACH(BOOST_TYPE_INDEX_CHECK_BASE_, _, base_list) \ + return NULL; \ } }} // namespace boost::typeindex diff --git a/test/type_index_runtime_cast_test.cpp b/test/type_index_runtime_cast_test.cpp index 4ceebf9..546affd 100644 --- a/test/type_index_runtime_cast_test.cpp +++ b/test/type_index_runtime_cast_test.cpp @@ -26,72 +26,72 @@ std::string name; struct base { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_PP_SEQ_NIL) IMPLEMENT_CLASS(base) }; struct single_derived : base { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) IMPLEMENT_CLASS(single_derived) }; struct base1 { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_PP_SEQ_NIL) IMPLEMENT_CLASS(base1) }; struct base2 { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_PP_SEQ_NIL) IMPLEMENT_CLASS(base2) }; struct multiple_derived : base1, base2 { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base1)(base2)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base1)(base2)) IMPLEMENT_CLASS(multiple_derived) }; struct baseV1 : virtual base { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) IMPLEMENT_CLASS(baseV1) }; struct baseV2 : virtual base { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) IMPLEMENT_CLASS(baseV2) }; struct multiple_virtual_derived : baseV1, baseV2 { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((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_REGISTER_RUNTIME_CLASS(BOOST_PP_SEQ_NIL) IMPLEMENT_CLASS(unrelated) }; struct unrelated_with_base : base { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) IMPLEMENT_CLASS(unrelated_with_base) }; struct unrelatedV1 : virtual base { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) IMPLEMENT_CLASS(unrelatedV1) }; struct level1_a : base { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) IMPLEMENT_CLASS(level1_a) }; struct level1_b : base { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((base)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base)) IMPLEMENT_CLASS(level1_b) }; struct level2 : level1_a, level1_b { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS_BASES((level1_a)(level1_b)) + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((level1_a)(level1_b)) IMPLEMENT_CLASS(level2) }; From 5af925602e54d5eee88acf9bf7748e846e41524f Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Wed, 10 Aug 2016 10:06:41 -0400 Subject: [PATCH 07/11] Invoke smart_ptr tests. --- test/type_index_runtime_cast_test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/type_index_runtime_cast_test.cpp b/test/type_index_runtime_cast_test.cpp index 546affd..152a4b2 100644 --- a/test/type_index_runtime_cast_test.cpp +++ b/test/type_index_runtime_cast_test.cpp @@ -261,6 +261,8 @@ int main() { const_pointer_interface(); const_reference_interface(); diamond_non_virtual(); + boost_shared_ptr(); + std_shared_ptr(); return boost::report_errors(); } From ad26256d0989b11604c1e97fb2d7a4ee4c82cad1 Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Thu, 11 Aug 2016 10:31:16 -0400 Subject: [PATCH 08/11] 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) }; From 9cd218bbc463425424c9f5a94ac1c89e60f8d59c Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Fri, 19 Aug 2016 18:42:22 -0400 Subject: [PATCH 09/11] Add missing include to example. --- examples/inheritance.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/inheritance.cpp b/examples/inheritance.cpp index 8bcdbf2..3da7527 100644 --- a/examples/inheritance.cpp +++ b/examples/inheritance.cpp @@ -13,6 +13,7 @@ */ #include +#include #include struct A { @@ -37,9 +38,9 @@ int main() { 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; + D d; const A& d_as_a = d; - print_real_type(dc_as_a); // Outputs `struct D` + print_real_type(d_as_a); // Outputs `struct D` } From 3bb646d19bda528b2b5f03647eea20489e79f986 Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Sun, 21 Aug 2016 11:01:26 -0400 Subject: [PATCH 10/11] - Use boost::addressof instead of & - Remove nullptr from test - add boost::typeindex::bad_runtime_cast, remove std::bad_cast --- examples/runtime_cast.cpp | 38 ++++++++---- .../runtime_cast/reference_cast.hpp | 21 ++++--- test/type_index_runtime_cast_test.cpp | 60 ++++++++++--------- 3 files changed, 72 insertions(+), 47 deletions(-) diff --git a/examples/runtime_cast.cpp b/examples/runtime_cast.cpp index f359d51..73125b6 100644 --- a/examples/runtime_cast.cpp +++ b/examples/runtime_cast.cpp @@ -45,32 +45,44 @@ 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 + 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." << '\n'; - else + 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* 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 + 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." << '\n'; - else + 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; } diff --git a/include/boost/type_index/runtime_cast/reference_cast.hpp b/include/boost/type_index/runtime_cast/reference_cast.hpp index f814547..6f30b50 100644 --- a/include/boost/type_index/runtime_cast/reference_cast.hpp +++ b/include/boost/type_index/runtime_cast/reference_cast.hpp @@ -13,11 +13,11 @@ /// \brief Contains the overload of boost::typeindex::runtime_cast for /// reference types. +#include #include #include #include #include -#include #ifdef BOOST_HAS_PRAGMA_ONCE # pragma once @@ -25,17 +25,23 @@ 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 std::bad_cast() +/// 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(&u, boost::is_base_and_derived()); + impl_type* value = detail::runtime_cast_impl( + boost::addressof(u), boost::is_base_and_derived()); if(!value) - boost::throw_exception(std::bad_cast()); + BOOST_THROW_EXCEPTION(bad_runtime_cast()); return *value; } @@ -43,13 +49,14 @@ T& runtime_cast(U& u) { /// \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() +/// 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(&u, boost::is_base_and_derived()); + impl_type* value = detail::runtime_cast_impl( + boost::addressof(u), boost::is_base_and_derived()); if(!value) - boost::throw_exception(std::bad_cast()); + BOOST_THROW_EXCEPTION(bad_runtime_cast()); return *value; } diff --git a/test/type_index_runtime_cast_test.cpp b/test/type_index_runtime_cast_test.cpp index 2bf5b9d..56ebe71 100644 --- a/test/type_index_runtime_cast_test.cpp +++ b/test/type_index_runtime_cast_test.cpp @@ -100,13 +100,13 @@ void no_base() using namespace boost::typeindex; base b; base* b2 = runtime_pointer_cast(&b); - BOOST_TEST_NE(b2, (base*)nullptr); + BOOST_TEST_NE(b2, (base*)NULL); BOOST_TEST_EQ(b2->name, "base"); - BOOST_TEST_EQ(runtime_pointer_cast(&b), (unrelated*)nullptr); - BOOST_TEST_EQ(runtime_pointer_cast(&b), (single_derived*)nullptr); - BOOST_TEST_EQ(runtime_pointer_cast(&b), (unrelatedV1*)nullptr); - BOOST_TEST_EQ(runtime_pointer_cast(&b), (unrelated_with_base*)nullptr); + 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() @@ -115,12 +115,12 @@ void single_base() single_derived d; base* b = &d; single_derived* d2 = runtime_pointer_cast(b); - BOOST_TEST_NE(d2, (single_derived*)nullptr); + BOOST_TEST_NE(d2, (single_derived*)NULL); BOOST_TEST_EQ(d2->name, "single_derived"); - BOOST_TEST_EQ(runtime_pointer_cast(&d), (unrelated*)nullptr); - BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)nullptr); - BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated_with_base*)nullptr); + 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() @@ -129,16 +129,16 @@ void multiple_base() multiple_derived d; base1* b1 = &d; multiple_derived* d2 = runtime_pointer_cast(b1); - BOOST_TEST_NE(d2, (multiple_derived*)nullptr); + 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*)nullptr); + BOOST_TEST_NE(b2, (base2*)NULL); BOOST_TEST_EQ(b2->name, "base2"); - BOOST_TEST_EQ(runtime_pointer_cast(&d), (unrelated*)nullptr); - BOOST_TEST_EQ(runtime_pointer_cast(b1), (unrelated*)nullptr); - BOOST_TEST_EQ(runtime_pointer_cast(b1), (unrelated_with_base*)nullptr); + 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() @@ -150,18 +150,18 @@ void virtual_base() baseV1* bv1 = runtime_pointer_cast(b); baseV2* bv2 = runtime_pointer_cast(b); - BOOST_TEST_NE(d2, (multiple_virtual_derived*)nullptr); + BOOST_TEST_NE(d2, (multiple_virtual_derived*)NULL); BOOST_TEST_EQ(d2->name, "multiple_virtual_derived"); - BOOST_TEST_NE(bv1, (baseV1*)nullptr); + BOOST_TEST_NE(bv1, (baseV1*)NULL); BOOST_TEST_EQ(bv1->name, "baseV1"); - BOOST_TEST_NE(bv2, (baseV2*)nullptr); + BOOST_TEST_NE(bv2, (baseV2*)NULL); BOOST_TEST_EQ(bv2->name, "baseV2"); - BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)nullptr); - BOOST_TEST_EQ(runtime_pointer_cast(&d), (unrelated*)nullptr); - BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated_with_base*)nullptr); + 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() @@ -170,9 +170,9 @@ void pointer_interface() single_derived d; base* b = &d; single_derived* d2 = runtime_cast(b); - BOOST_TEST_NE(d2, (single_derived*)nullptr); + BOOST_TEST_NE(d2, (single_derived*)NULL); BOOST_TEST_EQ(d2->name, "single_derived"); - BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)nullptr); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)NULL); } void reference_interface() @@ -186,9 +186,12 @@ void reference_interface() try { unrelated& u = runtime_cast(b); (void)u; - BOOST_TEST(!"should throw bad_cast"); + BOOST_TEST(!"should throw bad_runtime_cast"); + } + catch(boost::typeindex::bad_runtime_cast&) { } catch(...) { + BOOST_TEST(!"should throw bad_runtime_cast"); } } @@ -198,9 +201,9 @@ void const_pointer_interface() const single_derived d; base const* b = &d; single_derived const* d2 = runtime_cast(b); - BOOST_TEST_NE(d2, (single_derived*)nullptr); + BOOST_TEST_NE(d2, (single_derived*)NULL); BOOST_TEST_EQ(d2->name, "single_derived"); - BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)nullptr); + BOOST_TEST_EQ(runtime_pointer_cast(b), (unrelated*)NULL); } void const_reference_interface() @@ -214,9 +217,12 @@ void const_reference_interface() try { unrelated const& u = runtime_cast(b); (void)u; - BOOST_TEST(!"should throw bad_cast"); + BOOST_TEST(!"should throw bad_runtime_cast"); + } + catch(boost::typeindex::bad_runtime_cast&) { } catch(...) { + BOOST_TEST(!"should throw bad_runtime_cast"); } } @@ -228,7 +234,7 @@ void diamond_non_virtual() 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); + BOOST_TEST_NE(l1_b, (level1_b*)NULL); } void boost_shared_ptr() From b6b23bad3aecb299ea3a2fa965103c702a1d020d Mon Sep 17 00:00:00 2001 From: Chris Glover Date: Sun, 21 Aug 2016 12:47:44 -0400 Subject: [PATCH 11/11] Cleanup line endings, file endings, some spacing etc. --- doc/type_index.qbk | 2 +- examples/runtime_cast.cpp | 12 ++++----- include/boost/type_index/runtime_cast.hpp | 2 +- .../runtime_cast/boost_shared_ptr_cast.hpp | 12 ++++----- .../runtime_cast/detail/runtime_cast_impl.hpp | 6 ++--- .../type_index/runtime_cast/pointer_cast.hpp | 18 ++++++------- .../runtime_cast/reference_cast.hpp | 8 +++--- .../runtime_cast/register_runtime_class.hpp | 26 +++++++++---------- .../runtime_cast/std_shared_ptr_cast.hpp | 12 ++++----- test/type_index_runtime_cast_test.cpp | 11 ++++---- 10 files changed, 54 insertions(+), 55 deletions(-) diff --git a/doc/type_index.qbk b/doc/type_index.qbk index d985c21..539fc83 100644 --- a/doc/type_index.qbk +++ b/doc/type_index.qbk @@ -246,7 +246,7 @@ 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 +[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. diff --git a/examples/runtime_cast.cpp b/examples/runtime_cast.cpp index 73125b6..43e7aa7 100644 --- a/examples/runtime_cast.cpp +++ b/examples/runtime_cast.cpp @@ -29,11 +29,11 @@ struct B { {} }; -struct C : A { - BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((A)) +struct C : A { + BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((A)) }; -struct D : B { +struct D : B { BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((B)) }; @@ -46,7 +46,7 @@ int main() { A* a = &c; if(C* cp = boost::typeindex::runtime_cast(a)) { - std::cout << "Yes, a points to a C: " + std::cout << "Yes, a points to a C: " << a << "->" << cp << '\n'; } else { @@ -54,7 +54,7 @@ int main() { } if(E* ce = boost::typeindex::runtime_cast(a)) { - std::cout << "Error: Expected a to not points to an E: " + std::cout << "Error: Expected a to not points to an E: " << a << "->" << ce << '\n'; } else { @@ -64,7 +64,7 @@ int main() { 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: " + std::cout << "Yes, we can cross-cast from a C* to a D* when we actually have an E: " << cp2 << "->" << dp << '\n'; } else { diff --git a/include/boost/type_index/runtime_cast.hpp b/include/boost/type_index/runtime_cast.hpp index 692cd1b..c72b119 100644 --- a/include/boost/type_index/runtime_cast.hpp +++ b/include/boost/type_index/runtime_cast.hpp @@ -25,4 +25,4 @@ # pragma once #endif -#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_HPP \ No newline at end of file +#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 index cdde1fb..e31f19d 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,15 +23,15 @@ namespace boost { namespace typeindex { -/// \brief Creates a new instance of std::shared_ptr whose stored pointer is obtained from u's +/// \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 +/// 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. +/// \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) { @@ -39,8 +39,8 @@ boost::shared_ptr runtime_pointer_cast(boost::shared_ptr const& u) { 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 +#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 index 32bfffe..8164eda 100644 --- a/include/boost/type_index/runtime_cast/detail/runtime_cast_impl.hpp +++ b/include/boost/type_index/runtime_cast/detail/runtime_cast_impl.hpp @@ -31,7 +31,7 @@ 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 { @@ -43,7 +43,7 @@ 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 { @@ -54,4 +54,4 @@ T const* runtime_cast_impl(U const* u, boost::false_type) BOOST_NOEXCEPT { }} // namespace boost::typeindex -#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_DETAIL_RUNTIME_CAST_IMPL_HPP \ No newline at end of file +#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 index 8f6c3d4..49a7613 100644 --- a/include/boost/type_index/runtime_cast/pointer_cast.hpp +++ b/include/boost/type_index/runtime_cast/pointer_cast.hpp @@ -32,7 +32,7 @@ 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. @@ -45,24 +45,24 @@ T runtime_cast(U const* 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 +/// \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. +/// \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 +/// \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. +/// \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 { @@ -71,4 +71,4 @@ T const* runtime_pointer_cast(U const* u) BOOST_NOEXCEPT { }} // namespace boost::typeindex -#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_POINTER_CAST_HPP \ No newline at end of file +#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 index 6f30b50..10b5dc1 100644 --- a/include/boost/type_index/runtime_cast/reference_cast.hpp +++ b/include/boost/type_index/runtime_cast/reference_cast.hpp @@ -33,7 +33,7 @@ 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 +/// \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) { @@ -43,12 +43,12 @@ T& runtime_cast(U& u) { 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 +/// \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) { @@ -62,4 +62,4 @@ T const& runtime_cast(U const& u) { }} // namespace boost::typeindex -#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_REFERENCE_CAST_HPP \ No newline at end of file +#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 index cd0e8dd..37ce686 100644 --- a/include/boost/type_index/runtime_cast/register_runtime_class.hpp +++ b/include/boost/type_index/runtime_cast/register_runtime_class.hpp @@ -39,39 +39,39 @@ inline type_index runtime_class_construct_type_id(T const*) { /// \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 +/// 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(); +/// BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS(BOOST_TYPE_INDEX_NO_BASE_CLASS) +/// virtual ~base1(); /// }; /// -/// struct base2 { +/// 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)) -/// }; +/// BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base1)) +/// }; /// -/// struct derived2 : base1, base2 { -/// BOOST_TYPE_INDEX_REGISTER_RUNTIME_CLASS((base1)(base2)) +/// 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 +/// \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 \ @@ -80,9 +80,9 @@ inline type_index runtime_class_construct_type_id(T const*) { 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 \ No newline at end of file +#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 index 12e8f1c..277a524 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 @@ -23,15 +23,15 @@ namespace boost { namespace typeindex { -/// \brief Creates a new instance of std::shared_ptr whose stored pointer is obtained from u's +/// \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 +/// 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. +/// \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) { @@ -39,8 +39,8 @@ std::shared_ptr runtime_pointer_cast(std::shared_ptr const& u) { 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 \ No newline at end of file +#endif // BOOST_TYPE_INDEX_RUNTIME_CAST_STD_SHARED_PTR_CAST_HPP diff --git a/test/type_index_runtime_cast_test.cpp b/test/type_index_runtime_cast_test.cpp index 56ebe71..c64222a 100644 --- a/test/type_index_runtime_cast_test.cpp +++ b/test/type_index_runtime_cast_test.cpp @@ -180,8 +180,8 @@ 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"); + single_derived& d2 = runtime_cast(b); + BOOST_TEST_EQ(d2.name, "single_derived"); try { unrelated& u = runtime_cast(b); @@ -211,8 +211,8 @@ 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"); + single_derived const& d2 = runtime_cast(b); + BOOST_TEST_EQ(d2.name, "single_derived"); try { unrelated const& u = runtime_cast(b); @@ -233,7 +233,7 @@ 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_EQ(l1_b->name, "level1_b"); BOOST_TEST_NE(l1_b, (level1_b*)NULL); } @@ -271,4 +271,3 @@ int main() { std_shared_ptr(); return boost::report_errors(); } -