// 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) #define BOOST_TEST_MODULE openmethod #include #include #include #include #include "test_util.hpp" using namespace boost::openmethod; namespace type_hash { struct Animal { const char* name; Animal(const char* name, const char* type) : name(name), type(type) { } static constexpr const char* static_type = "Animal"; const char* type; }; struct Dog : Animal { Dog(const char* name, const char* type = static_type) : Animal(name, type) { } static constexpr const char* static_type = "Dog"; }; struct Cat : Animal { Cat(const char* name, const char* type = static_type) : Animal(name, type) { } static constexpr const char* static_type = "Cat"; }; struct custom_rtti : policies::rtti { template struct fn : defaults { template static constexpr bool is_polymorphic = std::is_base_of_v; template static auto static_type() { if constexpr (is_polymorphic) { return T::static_type; } else { return nullptr; } } template static auto dynamic_type(const T& obj) { if constexpr (is_polymorphic) { return obj.type; } else { return nullptr; } } template static void type_name(type_id type, Stream& stream) { stream << (type == nullptr ? "?" : reinterpret_cast(type)); } static auto type_index(type_id type) { return std::string_view( (type == nullptr ? "?" : reinterpret_cast(type))); } }; }; struct test_registry : test_registry_<__COUNTER__>::with {}; BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_registry); BOOST_OPENMETHOD(poke, (virtual_, std::ostream&), void, test_registry); BOOST_OPENMETHOD_OVERRIDE(poke, (Dog & dog, std::ostream& os), void) { os << dog.name << " barks."; } BOOST_OPENMETHOD_OVERRIDE(poke, (Cat & cat, std::ostream& os), void) { os << cat.name << " hisses."; } BOOST_AUTO_TEST_CASE(custom_rtti_simple_projection) { initialize(); Animal &&a = Dog("Snoopy"), &&b = Cat("Sylvester"); { std::stringstream os; poke(a, os); BOOST_TEST(os.str() == "Snoopy barks."); } { std::stringstream os; poke(b, os); BOOST_TEST(os.str() == "Sylvester hisses."); } } } // namespace type_hash namespace no_projection { struct Animal { const char* name; Animal(const char* name, std::size_t type) : name(name), type(type) { } static constexpr std::size_t static_type = 0; std::size_t type; }; struct Dog : Animal { Dog(const char* name, std::size_t type = static_type) : Animal(name, type) { } static constexpr std::size_t static_type = 1; }; struct Cat : Animal { Cat(const char* name, std::size_t type = static_type) : Animal(name, type) { } static constexpr std::size_t static_type = 2; }; struct custom_rtti : policies::rtti { template struct fn : defaults { template static constexpr bool is_polymorphic = std::is_base_of_v; inline static auto invalid_type_id = type_id(0xFFFFFF); template static auto static_type() { if constexpr (is_polymorphic) { return type_id(T::static_type); } else { return invalid_type_id; } } template static auto dynamic_type(const T& obj) { if constexpr (is_polymorphic) { return type_id(obj.type); } else { return invalid_type_id; } } template static void type_name(type_id type, Stream& stream) { static const char* name[] = {"Animal", "Dog", "Cat"}; stream << (type == invalid_type_id ? "?" : name[std::size_t(type)]); } static auto type_index(type_id type) { return type; } }; }; struct test_registry : test_registry_<__COUNTER__>::with::without< policies::type_hash> {}; BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_registry); BOOST_OPENMETHOD(poke, (virtual_, std::ostream&), void, test_registry); BOOST_OPENMETHOD_OVERRIDE(poke, (Dog & dog, std::ostream& os), void) { os << dog.name << " barks."; } BOOST_OPENMETHOD_OVERRIDE(poke, (Cat & cat, std::ostream& os), void) { os << cat.name << " hisses."; } void call_poke(Animal& a, std::ostream& os) { return poke(a, os); } // mov rax, qword ptr [rdi + 8] // mov rcx, qword ptr [rip + domain::context+24] // mov rax, qword ptr [rcx + 8*rax] // mov rcx, qword ptr [rip + method, basic_ostream >&)>::fn+80] mov // rax, qword ptr [rax + 8*rcx] jmp rax # // TAILCALL BOOST_AUTO_TEST_CASE(custom_rtti_simple) { BOOST_TEST(Animal::static_type == 0u); BOOST_TEST(Dog::static_type == 1u); BOOST_TEST(Cat::static_type == 2u); initialize(); Animal &&a = Dog("Snoopy"), &&b = Cat("Sylvester"); { std::stringstream os; poke(a, os); BOOST_TEST(os.str() == "Snoopy barks."); } { std::stringstream os; poke(b, os); BOOST_TEST(os.str() == "Sylvester hisses."); } } namespace using_vptr { template using vptr = virtual_ptr; BOOST_OPENMETHOD(poke, (vptr, std::ostream&), void, test_registry); BOOST_OPENMETHOD_OVERRIDE(poke, (vptr dog, std::ostream& os), void) { os << dog->name << " barks."; } BOOST_OPENMETHOD_OVERRIDE(poke, (vptr cat, std::ostream& os), void) { os << cat->name << " hisses."; } void call_poke(vptr a, std::ostream& os) { // mov rax, qword ptr [rip + method::fn+80] // mov rax, qword ptr [rsi + 8*rax] // jmp rax # TAILCALL return poke(a, os); } } // namespace using_vptr } // namespace no_projection namespace virtual_base { struct Animal { const char* name; Animal(const char* name, std::size_t type) : name(name), type(type) { } virtual auto cast_aux(std::size_t to_type) -> void* { return to_type == static_type ? this : nullptr; } static constexpr std::size_t static_type = 0; std::size_t type; }; template auto custom_dynamic_cast(Base& obj) -> Derived { using derived_type = std::remove_cv_t>; return *reinterpret_cast( const_cast&>(obj).cast_aux( derived_type::static_type)); } struct Dog : virtual Animal { Dog(const char* name, std::size_t type = static_type) : Animal(name, type) { } auto cast_aux(std::size_t to_type) -> void* override { return to_type == static_type ? this : Animal::cast_aux(to_type); } static constexpr std::size_t static_type = 1; }; struct Cat : virtual Animal { Cat(const char* name, std::size_t type = static_type) : Animal(name, type) { } auto cast_aux(std::size_t to_type) -> void* override { return to_type == static_type ? this : Animal::cast_aux(to_type); } static constexpr std::size_t static_type = 2; }; struct custom_rtti : policies::rtti { template struct fn : defaults { template static constexpr bool is_polymorphic = std::is_base_of_v; template static auto static_type() { if constexpr (is_polymorphic) { return type_id(T::static_type); } else { return type_id(0xFFFF); } } template static auto dynamic_type(const T& obj) { if constexpr (is_polymorphic) { return type_id(obj.type); } else { return type_id(0xFFFF); } } template static void type_name(type_id type, Stream& stream) { static const char* name[] = {"Animal", "Dog", "Cat"}; stream << (type == type_id(0xFFFF) ? "?" : name[std::size_t(type)]); } static auto type_index(type_id type) { return type; } template static auto dynamic_cast_ref(Base&& obj) -> Derived { return custom_dynamic_cast(obj); } }; }; struct test_registry : test_registry_<__COUNTER__>::with::without< policies::type_hash> {}; BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_registry); BOOST_OPENMETHOD(poke, (virtual_, std::ostream&), void, test_registry); BOOST_OPENMETHOD_OVERRIDE(poke, (Dog & dog, std::ostream& os), void) { os << dog.name << " barks."; } BOOST_OPENMETHOD_OVERRIDE(poke, (Cat & cat, std::ostream& os), void) { os << cat.name << " hisses."; } void call_poke(Animal& a, std::ostream& os) { return poke(a, os); } // mov rax, qword ptr [rdi + 8] // mov rcx, qword ptr [rip + domain::context+24] // mov rax, qword ptr [rcx + 8*rax] // mov rcx, qword ptr [rip + method, basic_ostream >&)>::fn+80] mov // rax, qword ptr [rax + 8*rcx] jmp rax # // TAILCALL BOOST_AUTO_TEST_CASE(virtual_base) { BOOST_TEST(Animal::static_type == 0u); BOOST_TEST(Dog::static_type == 1u); BOOST_TEST(Cat::static_type == 2u); initialize(); Animal &&a = Dog("Snoopy"), &&b = Cat("Sylvester"); { std::stringstream os; poke(a, os); BOOST_TEST(os.str() == "Snoopy barks."); } { std::stringstream os; poke(b, os); BOOST_TEST(os.str() == "Sylvester hisses."); } } namespace using_vptr { template using vptr = virtual_ptr; BOOST_OPENMETHOD(poke, (vptr, std::ostream&), void, test_registry); BOOST_OPENMETHOD_OVERRIDE(poke, (vptr dog, std::ostream& os), void) { os << dog->name << " barks."; } BOOST_OPENMETHOD_OVERRIDE(poke, (vptr cat, std::ostream& os), void) { os << cat->name << " hisses."; } void call_poke(vptr a, std::ostream& os) { // mov rax, qword ptr [rip + method::fn+80] // mov rax, qword ptr [rsi + 8*rax] // jmp rax # TAILCALL return poke(a, os); } } // namespace using_vptr } // namespace virtual_base namespace deferred_type_id { struct Animal { const char* name; Animal(const char* name, std::size_t type) : name(name), type(type) { } static std::size_t last_type_id; static std::size_t static_type; std::size_t type; }; std::size_t Animal::last_type_id; std::size_t Animal::static_type = ++Animal::last_type_id; struct Dog : Animal { Dog(const char* name, std::size_t type = static_type) : Animal(name, type) { } static std::size_t static_type; }; std::size_t Dog::static_type = ++Animal::last_type_id; struct Cat : Animal { Cat(const char* name, std::size_t type = static_type) : Animal(name, type) { } static std::size_t static_type; }; std::size_t Cat::static_type = ++Animal::last_type_id; struct custom_rtti : policies::deferred_static_rtti { template struct fn : defaults { template static constexpr bool is_polymorphic = std::is_base_of_v; template static auto static_type() { if constexpr (is_polymorphic) { return type_id(T::static_type); } else { return type_id(0); } } template static auto dynamic_type(const T& obj) { if constexpr (is_polymorphic) { return type_id(obj.type); } else { return type_id(0); } } template static void type_name(type_id type, Stream& stream) { static const char* name[] = {"?", "Animal", "Dog", "Cat"}; stream << name[std::size_t(type)]; } static auto type_index(type_id type) { return type; } }; }; struct test_registry : test_registry_<__COUNTER__>::with::without< policies::type_hash> {}; BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_registry); BOOST_OPENMETHOD(poke, (virtual_, std::ostream&), void, test_registry); BOOST_OPENMETHOD_OVERRIDE(poke, (Dog & dog, std::ostream& os), void) { os << dog.name << " barks."; } BOOST_OPENMETHOD_OVERRIDE(poke, (Cat & cat, std::ostream& os), void) { os << cat.name << " hisses."; } BOOST_OPENMETHOD( meet, (virtual_, virtual_, std::ostream&), void, test_registry); BOOST_OPENMETHOD_OVERRIDE(meet, (Dog&, Dog&, std::ostream& os), void) { os << "Both wag tails."; } BOOST_AUTO_TEST_CASE(custom_rtti_deferred) { initialize(); Animal &&a = Dog("Snoopy"), &&b = Cat("Sylvester"); { std::stringstream os; poke(a, os); BOOST_TEST(os.str() == "Snoopy barks."); } { std::stringstream os; poke(b, os); BOOST_TEST(os.str() == "Sylvester hisses."); } { std::stringstream os; meet(a, a, os); BOOST_TEST(os.str() == "Both wag tails."); } } } // namespace deferred_type_id