mirror of
https://github.com/boostorg/openmethod.git
synced 2026-01-27 19:12:11 +00:00
363 lines
11 KiB
C++
363 lines
11 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)
|
|
|
|
#include <iostream>
|
|
#include <type_traits>
|
|
|
|
#include <boost/openmethod.hpp>
|
|
#include <boost/openmethod/compiler.hpp>
|
|
#include <boost/openmethod/compiler.hpp>
|
|
|
|
#include "test_util.hpp"
|
|
|
|
#define BOOST_TEST_MODULE compiler
|
|
#include <boost/test/included/unit_test.hpp>
|
|
|
|
using namespace boost::openmethod;
|
|
using namespace boost::openmethod::detail;
|
|
|
|
using class_ = generic_compiler::class_;
|
|
using cc_method = generic_compiler::method;
|
|
using overrider = generic_compiler::overrider;
|
|
|
|
std::ostream& operator<<(std::ostream& os, const class_* cls) {
|
|
return os
|
|
<< reinterpret_cast<const std::type_info*>(cls->type_ids[0])->name();
|
|
}
|
|
|
|
std::string empty = "{}";
|
|
|
|
template<template<typename...> class Container, typename T>
|
|
auto str(const Container<T>& container) {
|
|
std::ostringstream os;
|
|
os << "{";
|
|
const char* sep = "";
|
|
|
|
for (const auto& item : container) {
|
|
os << sep << item;
|
|
sep = ", ";
|
|
}
|
|
|
|
os << "}";
|
|
|
|
return os.str();
|
|
}
|
|
|
|
template<typename... Ts>
|
|
auto sstr(Ts... args) {
|
|
std::vector<class_*> vec{args...};
|
|
std::sort(vec.begin(), vec.end());
|
|
return str(vec);
|
|
}
|
|
|
|
template<typename T>
|
|
auto sstr(const std::unordered_set<T>& container) {
|
|
return sstr(std::vector<T>(container.begin(), container.end()));
|
|
}
|
|
|
|
template<typename T, typename Compiler>
|
|
auto get_class(const Compiler& comp) {
|
|
return comp.class_map.at(typeid(T));
|
|
}
|
|
|
|
// A B
|
|
// \ / \
|
|
// AB D
|
|
// | |
|
|
// C E
|
|
// \ /
|
|
// F
|
|
|
|
struct A {
|
|
virtual ~A() {
|
|
}
|
|
};
|
|
|
|
struct B {
|
|
virtual ~B() {
|
|
}
|
|
};
|
|
|
|
struct AB : A, B {};
|
|
|
|
struct C : AB {};
|
|
|
|
struct D : B {};
|
|
|
|
struct E : D {};
|
|
|
|
struct F : C, E {};
|
|
|
|
// ============================================================================
|
|
// Test use_classes.
|
|
|
|
BOOST_AUTO_TEST_CASE(test_use_classes) {
|
|
using test_policy = test_policy_<__COUNTER__>;
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, B, AB, C, D, E, test_policy>);
|
|
|
|
std::vector<class_*> actual, expected;
|
|
|
|
auto comp = initialize<test_policy>();
|
|
|
|
auto a = get_class<A>(comp);
|
|
auto b = get_class<B>(comp);
|
|
auto ab = get_class<AB>(comp);
|
|
auto c = get_class<C>(comp);
|
|
auto d = get_class<D>(comp);
|
|
auto e = get_class<E>(comp);
|
|
|
|
// -----------------------------------------------------------------------
|
|
// A
|
|
BOOST_REQUIRE_EQUAL(sstr(a->direct_bases), empty);
|
|
BOOST_REQUIRE_EQUAL(sstr(a->direct_derived), sstr(ab));
|
|
BOOST_REQUIRE_EQUAL(sstr(a->transitive_derived), sstr(a, ab, c));
|
|
|
|
// -----------------------------------------------------------------------
|
|
// B
|
|
BOOST_REQUIRE_EQUAL(sstr(b->direct_bases), empty);
|
|
BOOST_REQUIRE_EQUAL(sstr(b->direct_derived), sstr(ab, d));
|
|
BOOST_REQUIRE_EQUAL(sstr(b->transitive_derived), sstr(b, ab, c, d, e));
|
|
|
|
// -----------------------------------------------------------------------
|
|
// AB
|
|
BOOST_REQUIRE_EQUAL(sstr(ab->direct_bases), sstr(a, b));
|
|
BOOST_REQUIRE_EQUAL(sstr(ab->direct_derived), sstr(c));
|
|
BOOST_REQUIRE_EQUAL(sstr(ab->transitive_derived), sstr(ab, c));
|
|
|
|
// -----------------------------------------------------------------------
|
|
// C
|
|
BOOST_REQUIRE_EQUAL(sstr(c->direct_bases), sstr(ab));
|
|
BOOST_REQUIRE_EQUAL(sstr(c->direct_derived), empty);
|
|
BOOST_REQUIRE_EQUAL(sstr(c->transitive_derived), sstr(c));
|
|
|
|
// -----------------------------------------------------------------------
|
|
// D
|
|
BOOST_REQUIRE_EQUAL(sstr(d->direct_bases), sstr(b));
|
|
BOOST_REQUIRE_EQUAL(sstr(d->direct_derived), sstr(e));
|
|
BOOST_REQUIRE_EQUAL(sstr(d->transitive_derived), sstr(d, e));
|
|
|
|
// -----------------------------------------------------------------------
|
|
// E
|
|
BOOST_REQUIRE_EQUAL(sstr(e->direct_bases), sstr(d));
|
|
BOOST_REQUIRE_EQUAL(sstr(e->direct_derived), empty);
|
|
BOOST_REQUIRE_EQUAL(sstr(e->transitive_derived), sstr(e));
|
|
}
|
|
|
|
/// ============================================================================
|
|
// Test assign_slots.
|
|
|
|
template<typename Compiler>
|
|
const auto& get_method(const Compiler& comp, const method_info& info) {
|
|
for (const auto& m : comp.methods) {
|
|
if (m.info == &info) {
|
|
return m;
|
|
}
|
|
}
|
|
|
|
BOOST_FAIL("method not found");
|
|
|
|
return comp.methods.front();
|
|
}
|
|
|
|
template<int>
|
|
struct M;
|
|
|
|
#define ADD_METHOD(CLASS) \
|
|
auto& BOOST_PP_CAT(m_, CLASS) = \
|
|
method<CLASS(virtual_<CLASS&>), void, test_policy>::fn;
|
|
|
|
#define ADD_METHOD_N(CLASS, N) \
|
|
auto& BOOST_PP_CAT(BOOST_PP_CAT(m_, CLASS), N) = \
|
|
method<M<N>(virtual_<CLASS&>), void, test_policy>::fn;
|
|
|
|
BOOST_AUTO_TEST_CASE(test_assign_slots_a_b1_c) {
|
|
using test_policy = test_policy_<__COUNTER__>;
|
|
|
|
// A
|
|
// / \
|
|
// B1 C
|
|
|
|
struct A {};
|
|
struct B : A {};
|
|
struct C : A {};
|
|
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, B, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, C, test_policy>);
|
|
ADD_METHOD(B);
|
|
auto comp = initialize<test_policy>();
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_B).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_B).slots[0] == 0);
|
|
BOOST_TEST(get_class<A>(comp)->vtbl.size() == 0);
|
|
BOOST_TEST(get_class<B>(comp)->vtbl.size() == 1);
|
|
BOOST_TEST(get_class<C>(comp)->vtbl.size() == 0);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_c1) {
|
|
using test_policy = test_policy_<__COUNTER__>;
|
|
|
|
// A1
|
|
// / \
|
|
// B1 C1
|
|
|
|
struct A {};
|
|
struct B : A {};
|
|
struct C : A {};
|
|
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, B, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, C, test_policy>);
|
|
ADD_METHOD(A);
|
|
ADD_METHOD(B);
|
|
ADD_METHOD(C);
|
|
auto comp = initialize<test_policy>();
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_A).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_A).slots[0] == 0);
|
|
BOOST_TEST(get_class<A>(comp)->vtbl.size() == 1);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_B).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_B).slots[0] == 1);
|
|
BOOST_TEST(get_class<B>(comp)->vtbl.size() == 2);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_C).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_C).slots[0] == 1);
|
|
BOOST_TEST(get_class<C>(comp)->vtbl.size() == 2);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_d1_c1_d1) {
|
|
using test_policy = test_policy_<__COUNTER__>;
|
|
|
|
// A1
|
|
// / \
|
|
// B1 C1 - slots 0-2 are wasted
|
|
// \ /
|
|
// D1
|
|
|
|
struct A {};
|
|
struct B : virtual A {};
|
|
struct C : virtual A {};
|
|
struct D : B, C {};
|
|
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, B, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, C, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<D, B, C, test_policy>);
|
|
ADD_METHOD(A);
|
|
ADD_METHOD(B);
|
|
ADD_METHOD(C);
|
|
ADD_METHOD(D);
|
|
auto comp = initialize<test_policy>();
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_A).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_A).slots[0] == 0);
|
|
BOOST_TEST(get_class<A>(comp)->vtbl.size() == 1);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_B).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_B).slots[0] == 1);
|
|
BOOST_TEST(get_class<B>(comp)->vtbl.size() == 2);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_D).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_D).slots[0] == 2);
|
|
BOOST_TEST(get_class<D>(comp)->vtbl.size() == 4);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_C).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_C).slots[0] == 3);
|
|
BOOST_TEST(get_class<C>(comp)->vtbl.size() == 4);
|
|
// slots 0-2 in C are wasted, to make room for methods in B and D
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_d1_c1_d1_e2) {
|
|
using test_policy = test_policy_<__COUNTER__>;
|
|
|
|
// A1
|
|
// / \
|
|
// B1 C1 slots 0-2 are wasted
|
|
// \ / \
|
|
// D1 E2 but E can use them
|
|
|
|
struct A {};
|
|
struct B : virtual A {};
|
|
struct C : virtual A {};
|
|
struct E : C {};
|
|
struct D : B, C {};
|
|
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, B, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, C, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<C, E, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<D, B, C, test_policy>);
|
|
ADD_METHOD(A);
|
|
ADD_METHOD(B);
|
|
ADD_METHOD(C);
|
|
ADD_METHOD(D);
|
|
ADD_METHOD_N(E, 1);
|
|
ADD_METHOD_N(E, 2);
|
|
ADD_METHOD_N(E, 3);
|
|
auto comp = initialize<test_policy>();
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_A).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_A).slots[0] == 0);
|
|
BOOST_TEST(get_class<A>(comp)->vtbl.size() == 1);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_B).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_B).slots[0] == 1);
|
|
BOOST_TEST(get_class<B>(comp)->vtbl.size() == 2);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_D).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_D).slots[0] == 2);
|
|
BOOST_TEST(get_class<D>(comp)->vtbl.size() == 4);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_C).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_C).slots[0] == 3);
|
|
BOOST_TEST(get_class<C>(comp)->vtbl.size() == 4);
|
|
// slots 0-2 in C are wasted, to make room for methods in B and D
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_E1).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_E1).slots[0] == 1);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_E2).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_E2).slots[0] == 2);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_E3).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_E3).slots[0] == 4);
|
|
|
|
BOOST_TEST(get_class<E>(comp)->vtbl.size() == 5);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_assign_slots_a1_c1_b1) {
|
|
using test_policy = test_policy_<__COUNTER__>;
|
|
|
|
// A1 B1
|
|
// \ /
|
|
// C1
|
|
|
|
struct A {};
|
|
struct B {};
|
|
struct C : A, B {};
|
|
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<B, test_policy>);
|
|
BOOST_OPENMETHOD_REGISTER(use_classes<A, B, C, test_policy>);
|
|
ADD_METHOD(A);
|
|
ADD_METHOD(B);
|
|
ADD_METHOD(C);
|
|
auto comp = initialize<test_policy>();
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_A).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_A).slots[0] == 0);
|
|
BOOST_TEST(get_class<A>(comp)->vtbl.size() == 1);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_C).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_C).slots[0] == 1);
|
|
BOOST_TEST(get_class<C>(comp)->vtbl.size() == 3);
|
|
|
|
BOOST_TEST_REQUIRE(get_method(comp, m_B).slots.size() == 1);
|
|
BOOST_TEST(get_method(comp, m_B).slots[0] == 2);
|
|
BOOST_TEST(get_class<B>(comp)->first_slot == 2);
|
|
BOOST_TEST(get_class<B>(comp)->vtbl.size() == 1);
|
|
}
|