// qright (c) 2018-2025 Jean-Louis Leroy // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt // or q at http://www.boost.org/LICENSE_1_0.txt) #define BOOST_TEST_MODULE openmethod #include #include "test_virtual_ptr_value_semantics.hpp" BOOST_AUTO_TEST_CASE_TEMPLATE( plain_virtual_ptr_value, Registry, test_policies) { static_assert( std::is_same_v< typename virtual_ptr::element_type, Animal>); static_assert(std::is_same_v< typename virtual_ptr::element_type, const Animal>); static_assert(std::is_same_v< decltype(std::declval>().get()), Animal*>); static_assert(!IsSmartPtr); static_assert(!IsSmartPtr); static_assert( std::is_same_v< decltype(*std::declval>()), Animal&>); init_test(); // ------------------------------------------------------------------------- // construction and assignment from plain references and pointers { virtual_ptr p{nullptr}; BOOST_TEST(p.get() == nullptr); BOOST_TEST(p.vptr() == nullptr); } { Dog snoopy; virtual_ptr p(snoopy); BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); Dog hector; p = hector; BOOST_TEST(p.get() == &hector); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { Dog snoopy; virtual_ptr p(snoopy); BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); Cat felix; p = felix; BOOST_TEST(p.get() == &felix); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { const Dog snoopy; virtual_ptr p(snoopy); BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); const Dog hector; p = hector; BOOST_TEST(p.get() == &hector); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { const Dog snoopy; virtual_ptr p(snoopy); BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); const Cat felix; p = felix; BOOST_TEST(p.get() == &felix); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { Dog snoopy; virtual_ptr p(&snoopy); BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); Dog hector; p = &hector; BOOST_TEST(p.get() == &hector); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { Dog snoopy; virtual_ptr p(&snoopy); BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); Cat felix; p = &felix; BOOST_TEST(p.get() == &felix); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { const Dog snoopy; virtual_ptr p(&snoopy); BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); const Dog hector; p = &hector; BOOST_TEST(p.get() == &hector); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { const Dog snoopy; virtual_ptr p(&snoopy); BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); const Cat felix; p = &felix; BOOST_TEST(p.get() == &felix); BOOST_TEST(p.vptr() == Registry::template static_vptr); } // virtual_ptr p{Dog()}; static_assert(!construct_assign_ok, Dog&&>); // ------------------------------------------------------------------------- // construction and assignment from other virtual_ptr { // virtual_ptr(const virtual_ptr&) Dog snoopy; const virtual_ptr p(snoopy); virtual_ptr q(p); BOOST_TEST(q.get() == &snoopy); BOOST_TEST(q.vptr() == Registry::template static_vptr); } { // virtual_ptr(virtual_ptr&) Dog snoopy; virtual_ptr p(snoopy); virtual_ptr q(p); BOOST_TEST(q.get() == &snoopy); BOOST_TEST(q.vptr() == Registry::template static_vptr); } { // virtual_ptr(virtual_ptr&&) Dog snoopy; virtual_ptr p(snoopy); virtual_ptr q(std::move(p)); BOOST_TEST(q.get() == &snoopy); BOOST_TEST(q.vptr() == Registry::template static_vptr); BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { // virtual_ptr(const virtual_ptr&) Dog snoopy; const virtual_ptr p(snoopy); virtual_ptr base(p); BOOST_TEST(base.get() == &snoopy); BOOST_TEST(base.vptr() == Registry::template static_vptr); } { // virtual_ptr(const virtual_ptr&) Dog snoopy; const virtual_ptr p(snoopy); virtual_ptr const_q(p); BOOST_TEST(const_q.get() == &snoopy); BOOST_TEST(const_q.vptr() == Registry::template static_vptr); } { // virtual_ptr(const virtual_ptr&) Dog snoopy; const virtual_ptr p(snoopy); virtual_ptr const_base_q(p); BOOST_TEST(const_base_q.get() == &snoopy); BOOST_TEST(const_base_q.vptr() == Registry::template static_vptr); } { // virtual_ptr() virtual_ptr p{nullptr}; BOOST_TEST(p.get() == nullptr); BOOST_TEST(p.vptr() == nullptr); } // ------------------------------------------------------------------------- // assignment { virtual_ptr p; Dog snoopy; p = snoopy; BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { virtual_ptr p; Dog snoopy; p = &snoopy; BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { virtual_ptr p; Dog snoopy; p = snoopy; BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { virtual_ptr p; Dog snoopy; p = &snoopy; BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); } { Dog snoopy; virtual_ptr p(snoopy); p = nullptr; BOOST_TEST(p.get() == nullptr); BOOST_TEST(p.vptr() == nullptr); } static_assert(!construct_assign_ok, const Dog&>); static_assert(!construct_assign_ok, const Dog*>); } BOOST_AUTO_TEST_CASE_TEMPLATE(indirect_virtual_ptr, Registry, test_policies) { BOOST_TEST_MESSAGE( "Registry = " << boost::core::demangle(typeid(Registry).name())); init_test(); Dog snoopy; virtual_ptr p(snoopy); BOOST_TEST_MESSAGE("After first call to initialize:"); BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr()); BOOST_TEST_MESSAGE( "static_vptr = " << Registry::template static_vptr); BOOST_TEST(p.vptr() == Registry::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, Registry); init_test(); BOOST_TEST_MESSAGE("After second call to initialize:"); BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr()); BOOST_TEST_MESSAGE( "static_vptr = " << Registry::template static_vptr); if constexpr (Registry::has_indirect_vptr) { BOOST_TEST(p.vptr() == Registry::template static_vptr); } else { BOOST_TEST(p.vptr() != Registry::template static_vptr); } } BOOST_AUTO_TEST_CASE(virtual_ptr_final_error) { auto prev_handler = default_registry::error_handler::set([](const auto& ev) { if (auto error = std::get_if(&ev)) { static_assert( std::is_same_v); throw *error; } }); init_test(); bool threw = false; try { Dog snoopy; Animal& animal = snoopy; virtual_ptr::final(animal); } catch (const final_error& error) { default_registry::error_handler::set(prev_handler); BOOST_TEST(error.static_type == &typeid(Animal)); BOOST_TEST(error.dynamic_type == &typeid(Dog)); threw = true; } catch (...) { default_registry::error_handler::set(prev_handler); BOOST_FAIL("wrong exception"); return; } if constexpr (default_registry::has_runtime_checks) { if (!threw) { BOOST_FAIL("should have thrown"); } } else { if (threw) { BOOST_FAIL("should not have thrown"); } } } // Cannot construct or assign a virtual_ptr from a non-polymorphic object. static_assert( !construct_assign_ok, const NonPolymorphic&>); static_assert( !construct_assign_ok, NonPolymorphic&>); static_assert( !construct_assign_ok, NonPolymorphic&&>); static_assert( !construct_assign_ok, const NonPolymorphic*>); static_assert( !construct_assign_ok, NonPolymorphic*>); // OK from another virtual_ptr though, because it can be constructed using // 'final'. static_assert(construct_assign_ok< virtual_ptr, virtual_ptr>);