// Copyright (c) 2018-2025 Jean-Louis Leroy // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include #include #define BOOST_TEST_MODULE openmethod #include #include #include #include #include #include #include #include using namespace boost::openmethod; using namespace boost::openmethod::policies; struct Animal { virtual ~Animal() { } Animal() = default; Animal(const Animal&) = delete; int age; }; struct Cat : virtual Animal {}; struct Dog : virtual Animal {}; template void init_test() { BOOST_OPENMETHOD_REGISTER(use_classes); struct id; (void)&method), void, Policy>::fn; boost::openmethod::initialize(); } struct direct_vector_policy : default_policy::fork {}; struct indirect_vector_policy : default_policy::fork::add::replace< vptr, vptr_vector> {}; struct direct_map_policy : default_policy::fork::replace< vptr, vptr_map> {}; struct indirect_map_policy : default_policy::fork::add::replace< vptr, vptr_map> {}; using test_policies = boost::mp11::mp_list< direct_vector_policy, indirect_vector_policy, direct_map_policy, indirect_map_policy>; BOOST_AUTO_TEST_CASE_TEMPLATE(virtual_ptr_ctors, Policy, test_policies) { static_assert(std::is_same_v< typename virtual_ptr::element_type, Animal>); static_assert(std::is_same_v< decltype(std::declval>().get()), Animal*>); static_assert(!virtual_ptr::is_smart_ptr); static_assert(!virtual_ptr::is_smart_ptr); static_assert( std::is_same_v< decltype(*std::declval>()), Animal&>); static_assert(!std::is_constructible_v, Dog>); init_test(); { Dog dog; { virtual_ptr p(dog); BOOST_TEST(p.vptr() != nullptr); BOOST_TEST(p.vptr() == Policy::template static_vptr); virtual_ptr copy(p); virtual_ptr base(p); virtual_ptr const_copy(p); virtual_ptr const_base_copy(p); } { const auto p = virtual_ptr(dog); virtual_ptr const_copy(p); virtual_ptr const_base_copy(p); } { auto p = virtual_ptr(dog); } } } template< template class smart_ptr, template class other_smart_ptr, class Policy> struct check_smart_ctors { // construction // a virtual_ptr can be constructed from a smart_ptr (same class) static_assert(std::is_constructible_v< virtual_ptr, Policy>, smart_ptr>); // a virtual_ptr to const can be constructed from smart_ptr (same class) static_assert( std::is_constructible_v< virtual_ptr, Policy>, const smart_ptr&>); static_assert( std::is_constructible_v< virtual_ptr, Policy>, smart_ptr>); // a virtual_ptr can be constructed from a smart_ptr (derived class) static_assert(std::is_constructible_v< virtual_ptr, Policy>, smart_ptr>); // a virtual_ptr to const can be constructed from a smart_ptr (derived class) static_assert( std::is_constructible_v< virtual_ptr, Policy>, smart_ptr>); // a virtual_ptr cannot be constructed from a smart_ptr to a different class static_assert(!std::is_constructible_v< virtual_ptr, Policy>, smart_ptr>); // a virtual_ptr cannot be constructed from const smart_ptr static_assert( !std::is_constructible_v< virtual_ptr, Policy>, smart_ptr>); // policies must be the same static_assert(!std::is_constructible_v< virtual_ptr, policies::debug>, virtual_ptr, policies::release>>); // move constructible static_assert( std::is_move_constructible_v, Policy>>); // a smart virtual_ptr cannot be constructed from a plain reference or // pointer static_assert(!std::is_constructible_v< virtual_ptr, Policy>, Animal>); static_assert(!std::is_constructible_v< virtual_ptr, Policy>, Animal*>); // the smart pointer must be the same (e.g. both shared_ptr) static_assert(!std::is_constructible_v< virtual_ptr, Policy>, virtual_ptr, Policy>>); // a smart virtual_ptr converts to a plain one static_assert(std::is_constructible_v< virtual_ptr, virtual_ptr, Policy>>); // but not the other way around static_assert(!std::is_constructible_v< virtual_ptr, Policy>, virtual_ptr>); // --------------------- // test other properties static_assert(virtual_ptr, Policy>::is_smart_ptr); static_assert(virtual_ptr, Policy>::is_smart_ptr); static_assert(std::is_same_v< typename virtual_ptr, Policy>::element_type, Animal>); static_assert( std::is_same_v< decltype(std::declval, Policy>>() .get()), Animal*>); static_assert( std::is_same_v< decltype(*std::declval, Policy>>()), Animal&>); static_assert( std::is_same_v< decltype(std::declval, Policy>>() .pointer()), const smart_ptr&>); static_assert( std::is_same_v< decltype(*std::declval, Policy>>()), Animal&>); }; template struct check_smart_ctors< std::shared_ptr, std::unique_ptr, direct_vector_policy>; template struct check_smart_ctors< std::unique_ptr, std::shared_ptr, direct_vector_policy>; BOOST_AUTO_TEST_CASE_TEMPLATE(shared_virtual_ptr_ctors, Policy, test_policies) { { init_test(); auto dog = std::make_shared(); { shared_virtual_ptr ptr(dog); BOOST_TEST((ptr.get() == dog.get())); BOOST_TEST(ptr.pointer() == dog); BOOST_TEST(ptr.vptr() == Policy::template static_vptr); shared_virtual_ptr copy(ptr); BOOST_TEST((copy.get() == dog.get())); BOOST_TEST(copy.pointer() == dog); BOOST_TEST(copy.vptr() == Policy::template static_vptr); shared_virtual_ptr base(ptr); BOOST_TEST((base.get() == dog.get())); shared_virtual_ptr downcast = base.template cast(); BOOST_TEST((downcast.get() == dog.get())); BOOST_TEST(base.vptr() == Policy::template static_vptr); shared_virtual_ptr const_copy(ptr); shared_virtual_ptr base_const_copy(ptr); shared_virtual_ptr move_ptr(std::move(ptr)); BOOST_TEST(ptr.pointer().get() == nullptr); BOOST_TEST(move_ptr.pointer().get() == dog.get()); } { shared_virtual_ptr ptr(std::make_shared()); virtual_ptr dumb_vptr(ptr); BOOST_TEST(dumb_vptr.get() == ptr.get()); BOOST_TEST(dumb_vptr.vptr() == ptr.vptr()); } { shared_virtual_ptr ptr(std::make_shared()); virtual_ptr dumb_vptr(ptr); BOOST_TEST(dumb_vptr.get() == ptr.get()); BOOST_TEST(dumb_vptr.vptr() == ptr.vptr()); } { shared_virtual_ptr ptr(dog); shared_virtual_ptr move_const_ptr( std::move(ptr)); BOOST_TEST(ptr.pointer().get() == nullptr); BOOST_TEST(move_const_ptr.pointer().get() == dog.get()); } { shared_virtual_ptr ptr(dog); } { // should not compile: // unique_virtual_ptr unique_dog(dog); } } { auto dog = std::make_shared(); shared_virtual_ptr ptr(dog); BOOST_TEST(ptr.pointer().get() == dog.get()); BOOST_TEST(ptr.vptr() == Policy::template static_vptr); shared_virtual_ptr copy(ptr); shared_virtual_ptr base(ptr); } } BOOST_AUTO_TEST_CASE_TEMPLATE(unique_virtual_ptr_ctors, Policy, test_policies) { init_test(); { // a unique_virtual_ptr can be created from a std::unique_ptr std::unique_ptr is_smart_ptr = std::make_unique(); auto dumb_ptr = is_smart_ptr.get(); unique_virtual_ptr virtual_smart_ptr( std::move(is_smart_ptr)); // and ownership is transferred BOOST_TEST(is_smart_ptr.get() == nullptr); BOOST_TEST(virtual_smart_ptr.get() == dumb_ptr); } { // a virtual_ptr can be constructed from a unique_virtual_ptr auto virtual_smart_ptr = make_unique_virtual(); auto dumb_ptr = virtual_smart_ptr.get(); virtual_ptr virtual_dumb_ptr = virtual_smart_ptr; BOOST_TEST(virtual_dumb_ptr.get() == dumb_ptr); // ownership is not transferred BOOST_TEST(virtual_smart_ptr.get() == dumb_ptr); } { // unique_virtual_ptr can be moved auto unique = make_unique_virtual(); Dog* dumb_ptr = unique.get(); unique_virtual_ptr unique_moved = std::move(unique); BOOST_TEST(unique.get() == nullptr); BOOST_TEST(unique_moved.get() == dumb_ptr); } { unique_virtual_ptr base(std::make_unique()); auto p = base.get(); unique_virtual_ptr downcast = std::move(base).template cast(); BOOST_TEST((downcast.get() == p)); BOOST_TEST((base.get() == nullptr)); BOOST_TEST(downcast.vptr() == Policy::template static_vptr); } } BOOST_AUTO_TEST_CASE_TEMPLATE(indirect_virtual_ptr, Policy, test_policies) { BOOST_TEST_MESSAGE( "Policy = " << boost::core::demangle(typeid(Policy).name())); init_test(); Dog dog; virtual_ptr p(dog); BOOST_TEST_MESSAGE("After first call to initialize:"); BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr()); BOOST_TEST_MESSAGE( "static_vptr = " << Policy::template static_vptr); BOOST_TEST(p.vptr() == Policy::template static_vptr); // Add a class, to make sure dispatch data is not re-constructed in the same // place with the same values: struct Cat : Animal {}; BOOST_OPENMETHOD_CLASSES(Animal, Cat, Policy); init_test(); BOOST_TEST_MESSAGE("After second call to initialize:"); BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr()); BOOST_TEST_MESSAGE( "static_vptr = " << Policy::template static_vptr); if constexpr (Policy::template has_facet) { BOOST_TEST(p.vptr() == Policy::template static_vptr); } else { BOOST_TEST(p.vptr() != Policy::template static_vptr); } } BOOST_AUTO_TEST_CASE(virtual_ptr_final_error) { auto prev_handler = default_policy::set_error_handler( [](const default_policy::error_variant& ev) { if (auto error = std::get_if(&ev)) { static_assert(std::is_same_v< decltype(error), const type_mismatch_error*>); throw *error; } }); init_test(); bool threw = false; try { Dog snoopy; Animal& animal = snoopy; virtual_ptr::final(animal); } catch (const type_mismatch_error& error) { default_policy::set_error_handler(prev_handler); BOOST_TEST(error.type == reinterpret_cast(&typeid(Dog))); threw = true; } catch (...) { default_policy::set_error_handler(prev_handler); BOOST_FAIL("wrong exception"); return; } if constexpr (default_policy::has_facet) { if (!threw) { BOOST_FAIL("should have thrown"); } } else { if (threw) { BOOST_FAIL("should not have thrown"); } } }