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(); +} +