Files
openmethod/test/test_custom_rtti.cpp
2025-04-17 18:10:25 -04:00

497 lines
13 KiB
C++

// 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 <boost/test/included/unit_test.hpp>
#include <boost/utility/identity_type.hpp>
#include <boost/openmethod.hpp>
#include <boost/openmethod/compiler.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<class T>
static constexpr bool is_polymorphic = std::is_base_of_v<Animal, T>;
template<typename T>
static auto static_type() {
if constexpr (is_polymorphic<T>) {
return reinterpret_cast<type_id>(T::static_type);
} else {
return 0;
}
}
template<typename T>
static auto dynamic_type(const T& obj) {
if constexpr (is_polymorphic<T>) {
return reinterpret_cast<type_id>(obj.type);
} else {
return 0;
}
}
template<class Stream>
static void type_name(type_id type, Stream& stream) {
stream << (type == 0 ? "?" : reinterpret_cast<const char*>(type));
}
static auto type_index(type_id type) {
return std::string_view(
(type == 0 ? "?" : reinterpret_cast<const char*>(type)));
}
};
struct test_policy
: default_policy::fork<test_policy>::replace<policies::rtti, custom_rtti> {
};
BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy);
BOOST_OPENMETHOD(poke, (virtual_<Animal&>, 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<test_policy>();
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<class T>
static constexpr bool is_polymorphic = std::is_base_of_v<Animal, T>;
template<typename T>
static auto static_type() {
if constexpr (is_polymorphic<T>) {
return T::static_type;
} else {
return 666;
}
}
template<typename T>
static auto dynamic_type(const T& obj) {
if constexpr (is_polymorphic<T>) {
return obj.type;
} else {
return 666;
}
}
template<class Stream>
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<test_policy>::replace<
policies::rtti, custom_rtti>::remove<policies::type_hash> {};
BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy);
BOOST_OPENMETHOD(poke, (virtual_<Animal&>, 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<test_policy>::context+24]
// mov rax, qword ptr [rcx + 8*rax]
// mov rcx, qword ptr [rip + method<test_policy, poke, void
// (virtual_<Animal&>, basic_ostream<char, char_traits<char> >&)>::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<test_policy>();
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<class C>
using vptr = virtual_ptr<C, test_policy>;
BOOST_OPENMETHOD(poke, (vptr<Animal>, std::ostream&), void, test_policy);
BOOST_OPENMETHOD_OVERRIDE(poke, (vptr<Dog> dog, std::ostream& os), void) {
os << dog->name << " barks.";
}
BOOST_OPENMETHOD_OVERRIDE(poke, (vptr<Cat> cat, std::ostream& os), void) {
os << cat->name << " hisses.";
}
void call_poke(vptr<Animal> a, std::ostream& os) {
// mov rax, qword ptr [rip + method<test_policy, poke, ...>::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 type) -> void* {
return type == static_type ? this : nullptr;
}
static constexpr std::size_t static_type = 0;
std::size_t type;
};
template<typename Derived, typename Base>
auto custom_dynamic_cast(Base& obj) -> Derived {
using derived_type = std::remove_cv_t<std::remove_reference_t<Derived>>;
return *reinterpret_cast<derived_type*>(
const_cast<std::remove_cv_t<Base>&>(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 type) -> void* 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) {
}
auto cast_aux(std::size_t type) -> void* override {
return type == static_type ? this : Animal::cast_aux(type);
}
static constexpr std::size_t static_type = 2;
};
struct custom_rtti : policies::rtti {
template<class T>
static constexpr bool is_polymorphic = std::is_base_of_v<Animal, T>;
template<typename T>
static auto static_type() {
if constexpr (is_polymorphic<T>) {
return T::static_type;
} else {
return 666;
}
}
template<typename T>
static auto dynamic_type(const T& obj) {
if constexpr (is_polymorphic<T>) {
return obj.type;
} else {
return 666;
}
}
template<class Stream>
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<typename Derived, typename Base>
static auto dynamic_cast_ref(Base&& obj) -> Derived {
return custom_dynamic_cast<Derived>(obj);
}
};
struct test_policy
: default_policy::fork<test_policy>::replace<
policies::rtti, custom_rtti>::remove<policies::type_hash> {};
BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy);
BOOST_OPENMETHOD(poke, (virtual_<Animal&>, 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<test_policy>::context+24]
// mov rax, qword ptr [rcx + 8*rax]
// mov rcx, qword ptr [rip + method<test_policy, poke, void
// (virtual_<Animal&>, basic_ostream<char, char_traits<char> >&)>::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<test_policy>();
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<class C>
using vptr = virtual_ptr<C, test_policy>;
BOOST_OPENMETHOD(poke, (vptr<Animal>, std::ostream&), void, test_policy);
BOOST_OPENMETHOD_OVERRIDE(poke, (vptr<Dog> dog, std::ostream& os), void) {
os << dog->name << " barks.";
}
BOOST_OPENMETHOD_OVERRIDE(poke, (vptr<Cat> cat, std::ostream& os), void) {
os << cat->name << " hisses.";
}
void call_poke(vptr<Animal> a, std::ostream& os) {
// mov rax, qword ptr [rip + method<test_policy, poke, ...>::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<class T>
static constexpr bool is_polymorphic = std::is_base_of_v<Animal, T>;
template<typename T>
static auto static_type() {
if constexpr (is_polymorphic<T>) {
return T::static_type;
} else {
static type_id invalid = 0;
return invalid;
}
}
template<typename T>
static auto dynamic_type(const T& obj) {
if constexpr (is_polymorphic<T>) {
return obj.type;
} else {
return 666;
}
}
template<class Stream>
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<test_policy>::replace<
policies::rtti, custom_rtti>::remove<policies::type_hash> {};
BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy);
BOOST_OPENMETHOD(poke, (virtual_<Animal&>, 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<test_policy>();
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