// 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 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 static auto static_type() { if constexpr (std::is_base_of_v) { return reinterpret_cast(T::static_type); } else { return 0; } } template static auto dynamic_type(const T& obj) { if constexpr (std::is_base_of_v) { return reinterpret_cast(obj.type); } else { return 0; } } template static void type_name(type_id type, Stream& stream) { stream << (type == 0 ? "?" : reinterpret_cast(type)); } static auto type_index(type_id type) { return std::string_view( (type == 0 ? "?" : reinterpret_cast(type))); } }; struct test_policy : default_policy::fork::replace { }; BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy); BOOST_OPENMETHOD(poke, (virtual_, std::ostream&), void, test_policy); 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 static auto static_type() { if constexpr (std::is_base_of_v) { return T::static_type; } else { return 666; } } template static auto dynamic_type(const T& obj) { if constexpr (std::is_base_of_v) { return obj.type; } else { return 666; } } template static void type_name(type_id type, Stream& stream) { static const char* name[] = {"Animal", "Dog", "Cat"}; stream << (type == 666 ? "?" : name[type]); } static auto type_index(type_id type) { return type; } }; struct test_policy : default_policy::fork::replace< policies::rtti, custom_rtti>::remove {}; BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy); BOOST_OPENMETHOD(poke, (virtual_, std::ostream&), void, test_policy); 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 == 0); BOOST_TEST(Dog::static_type == 1); BOOST_TEST(Cat::static_type == 2); 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_policy); 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 void* cast_aux(std::size_t type) { return type == static_type ? this : nullptr; } static constexpr std::size_t static_type = 0; std::size_t type; }; template Derived custom_dynamic_cast(Base& obj) { 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) { } void* cast_aux(std::size_t type) override { return type == static_type ? this : Animal::cast_aux(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) { } void* cast_aux(std::size_t type) override { return type == static_type ? this : Animal::cast_aux(type); } static constexpr std::size_t static_type = 2; }; struct custom_rtti : policies::rtti { template static auto static_type() { if constexpr (std::is_base_of_v) { return T::static_type; } else { return 666; } } template static auto dynamic_type(const T& obj) { if constexpr (std::is_base_of_v) { return obj.type; } else { return 666; } } template static void type_name(type_id type, Stream& stream) { static const char* name[] = {"Animal", "Dog", "Cat"}; stream << (type == 666 ? "?" : name[type]); } static auto type_index(type_id type) { return type; } template static Derived dynamic_cast_ref(Base&& obj) { return custom_dynamic_cast(obj); } }; struct test_policy : default_policy::fork::replace< policies::rtti, custom_rtti>::remove {}; BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy); BOOST_OPENMETHOD(poke, (virtual_, std::ostream&), void, test_policy); 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 == 0); BOOST_TEST(Dog::static_type == 1); BOOST_TEST(Cat::static_type == 2); 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_policy); 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 defered_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 static auto static_type() { if constexpr (std::is_base_of_v) { return T::static_type; } else { static type_id invalid = 0; return invalid; } } template static auto dynamic_type(const T& obj) { if constexpr (std::is_base_of_v) { return obj.type; } else { return 666; } } template static void type_name(type_id type, Stream& stream) { static const char* name[] = {"Animal", "Dog", "Cat"}; stream << (type == 0 ? "?" : name[type]); } static auto type_index(type_id type) { return type; } }; struct test_policy : default_policy::fork::replace< policies::rtti, custom_rtti>::remove {}; BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy); BOOST_OPENMETHOD(poke, (virtual_, std::ostream&), void, test_policy); 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_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."); } } } // namespace defered_type_id