From 0c606a1c201f95313bdc4c40a9f416c02ca10231 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Thu, 2 Mar 2023 18:50:20 +0700 Subject: [PATCH] new non-recursive upcast system --- develop/singles/headers/meta.hpp/meta_all.hpp | 245 +++++++++++++----- .../untests/meta_features/multiple2_tests.cpp | 204 +++++++++++++++ .../untests/meta_types/class_type_tests.cpp | 17 +- headers/meta.hpp/meta_binds/class_bind.hpp | 122 +++++++-- .../meta_detail/value_utilities/utraits.hpp | 66 +++-- headers/meta.hpp/meta_types.hpp | 19 +- headers/meta.hpp/meta_types/class_type.hpp | 38 ++- 7 files changed, 562 insertions(+), 149 deletions(-) create mode 100644 develop/untests/meta_features/multiple2_tests.cpp diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index 2f113e0..882d773 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -2509,7 +2509,8 @@ namespace meta_hpp [[nodiscard]] any_type get_argument_type(std::size_t position) const noexcept; [[nodiscard]] const any_type_list& get_argument_types() const noexcept; - [[nodiscard]] const class_set& get_bases() const noexcept; + [[nodiscard]] const class_set& get_base_classes() const noexcept; + [[nodiscard]] const class_set& get_derived_classes() const noexcept; [[nodiscard]] const constructor_set& get_constructors() const noexcept; [[nodiscard]] const destructor_set& get_destructors() const noexcept; [[nodiscard]] const function_set& get_functions() const noexcept; @@ -2743,7 +2744,8 @@ namespace meta_hpp::detail const std::size_t align; const any_type_list argument_types; - class_set bases; + class_set base_classes; + class_set derived_classes; constructor_set constructors; destructor_set destructors; function_set functions; @@ -2752,13 +2754,14 @@ namespace meta_hpp::detail typedef_map typedefs; variable_set variables; - struct base_info final { - using upcast_fptr = void* (*)(void*); - const upcast_fptr upcast; - }; + using upcast_func_t = void* (*)(void*); + using upcast_func_list_t = std::vector; - using base_info_map = std::map>; - base_info_map bases_info; + using base_upcasts_t = std::map>; + using deep_upcasts_t = std::multimap>; + + base_upcasts_t base_upcasts; + deep_upcasts_t deep_upcasts; template < class_kind Class > explicit class_type_data(type_list); @@ -4787,6 +4790,73 @@ namespace meta_hpp : type_bind_base{resolve_type(), std::move(metadata)} {} } +namespace meta_hpp::detail::class_bind_impl +{ + using base_upcasts_t = class_type_data::base_upcasts_t; + using deep_upcasts_t = class_type_data::deep_upcasts_t; + + using upcast_func_t = class_type_data::upcast_func_t; + using upcast_func_list_t = class_type_data::upcast_func_list_t; + + using new_bases_db_t = std::map; + using deep_upcasts_db_t = std::map>; + using derived_classes_db_t = std::map>; + + template < class_kind Class, class_kind Base > + requires detail::class_bind_base_kind + void update_new_bases_db( // + new_bases_db_t& new_bases_db + ) { + new_bases_db.emplace(resolve_type(), [](void* from) { + return static_cast(static_cast(static_cast(from))); + }); + } + + inline void update_deep_upcasts_db( // + const class_type& derived_class, + const class_type& new_base_class, + const upcast_func_list_t& derived_to_new_base, + deep_upcasts_db_t& deep_upcasts_db + ) { + const class_type_data& derived_class_data = *type_access(derived_class); + const class_type_data& new_base_class_data = *type_access(new_base_class); + + const auto [deep_upcasts_db_iter, _] = deep_upcasts_db.try_emplace(derived_class, derived_class_data.deep_upcasts); + deep_upcasts_t& derived_deep_upcasts = deep_upcasts_db_iter->second; + derived_deep_upcasts.emplace(new_base_class, derived_to_new_base); + + for ( auto&& [new_deep_class, new_base_to_deep] : new_base_class_data.deep_upcasts ) { + upcast_func_list_t derived_to_new_deep; + derived_to_new_deep.reserve(derived_to_new_base.size() + new_base_to_deep.size()); + derived_to_new_deep.insert(derived_to_new_deep.end(), derived_to_new_base.begin(), derived_to_new_base.end()); + derived_to_new_deep.insert(derived_to_new_deep.end(), new_base_to_deep.begin(), new_base_to_deep.end()); + derived_deep_upcasts.emplace(new_deep_class, std::move(derived_to_new_deep)); + } + + for ( const class_type& subderived_class : derived_class_data.derived_classes ) { + const class_type_data& subderived_data = *type_access(subderived_class); + + upcast_func_list_t subderived_to_new_base; + subderived_to_new_base.reserve(derived_to_new_base.size() + 1); + subderived_to_new_base.insert(subderived_to_new_base.end(), subderived_data.base_upcasts.at(derived_class)); + subderived_to_new_base.insert(subderived_to_new_base.end(), derived_to_new_base.begin(), derived_to_new_base.end()); + + update_deep_upcasts_db(subderived_class, new_base_class, subderived_to_new_base, deep_upcasts_db); + } + } + + inline void updata_derived_classes_db( // + const class_type& self_class, + const class_type& new_base_class, + derived_classes_db_t& derived_classes_db + ) { + const class_type_data& base_class_data = *type_access(new_base_class); + class_set new_derived_classes{base_class_data.derived_classes}; + new_derived_classes.emplace(self_class); + derived_classes_db.emplace(new_base_class, std::move(new_derived_classes)); + } +} + namespace meta_hpp { template < detail::class_kind Class > @@ -4805,28 +4875,47 @@ namespace meta_hpp template < detail::class_kind... Bases > requires(... && detail::class_bind_base_kind) class_bind& class_bind::base_() { - const auto register_base{[this](std::in_place_type_t) { - const class_type& base_type = resolve_type(); + using namespace detail; + using namespace detail::class_bind_impl; - auto&& [position, emplaced] = get_data().bases.emplace(base_type); - if ( !emplaced ) { - return; + if ( (... && get_data().base_classes.contains(resolve_type())) ) { + return *this; + } + + new_bases_db_t new_bases_db; + (update_new_bases_db(new_bases_db), ...); + + deep_upcasts_db_t deep_upcasts_db; + derived_classes_db_t derived_classes_db; + + class_set new_base_classes{get_data().base_classes}; + base_upcasts_t new_base_upcasts{get_data().base_upcasts}; + + for ( auto&& [new_base_class, self_to_new_base] : new_bases_db ) { + if ( new_base_classes.contains(new_base_class) ) { + continue; } - META_HPP_TRY { - get_data().bases_info.emplace( // - base_type, - detail::class_type_data::base_info{ - .upcast{[](void* derived) -> void* { return static_cast(static_cast(derived)); }}} - ); - } - META_HPP_CATCH(...) { - get_data().bases.erase(position); - META_HPP_RETHROW(); - } - }}; + update_deep_upcasts_db(*this, new_base_class, {self_to_new_base}, deep_upcasts_db); + updata_derived_classes_db(*this, new_base_class, derived_classes_db); + + new_base_classes.emplace(new_base_class); + new_base_upcasts.emplace(new_base_class, self_to_new_base); + } + + get_data().base_classes.swap(new_base_classes); + get_data().base_upcasts.swap(new_base_upcasts); + + for ( auto&& [derived_class, new_deep_upcasts] : deep_upcasts_db ) { + class_type_data& derived_data = *type_access(derived_class); + derived_data.deep_upcasts.swap(new_deep_upcasts); + } + + for ( auto&& [base_class, new_derived_classes] : derived_classes_db ) { + class_type_data& base_data = *type_access(base_class); + base_data.derived_classes.swap(new_derived_classes); + } - (register_base(std::in_place_type), ...); return *this; } @@ -5600,37 +5689,34 @@ namespace meta_hpp::detail return ptr; } - for ( auto&& [base_type, base_info] : type_access(from)->bases_info ) { - if ( base_type == to ) { - return base_info.upcast(ptr); - } + void* base_ptr = nullptr; - if ( base_type.is_derived_from(to) ) { - return pointer_upcast(base_info.upcast(ptr), base_type, to); + class_type_data& from_data = *type_access(from); + class_type_data::deep_upcasts_t& deep_upcasts = from_data.deep_upcasts; + + for ( auto iter{deep_upcasts.lower_bound(to)}; iter != deep_upcasts.end() && iter->first == to; ++iter ) { + void* new_base_ptr = [ptr, iter]() mutable { + for ( class_type_data::upcast_func_t upcast : iter->second ) { + ptr = upcast(ptr); + } + return ptr; + }(); + + if ( base_ptr == nullptr ) { + base_ptr = new_base_ptr; + } else if ( base_ptr != new_base_ptr ) { + // ambiguous conversions + return nullptr; } } - return nullptr; + return base_ptr; } [[nodiscard]] inline const void* pointer_upcast(const void* ptr, const class_type& from, const class_type& to) { // NOLINTNEXTLINE(*-const-cast) return pointer_upcast(const_cast(ptr), from, to); } - - template < class_kind To, class_kind From > - [[nodiscard]] To* pointer_upcast(type_registry& registry, From* ptr) { - const class_type& to_class = registry.resolve_type(); - const class_type& from_class = registry.resolve_type(); - return static_cast(pointer_upcast(ptr, from_class, to_class)); - } - - template < class_kind To, class_kind From > - [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { - const class_type& to_class = registry.resolve_type(); - const class_type& from_class = registry.resolve_type(); - return static_cast(pointer_upcast(ptr, from_class, to_class)); - } } namespace meta_hpp::detail @@ -5647,8 +5733,10 @@ namespace meta_hpp::detail const class_type& to_class = to.as_class(); const class_type& from_class = from.as_class(); - if ( to_class && from_class && from_class.is_derived_from(to_class) ) { - return pointer_upcast(ptr, from_class, to_class); + if ( to_class && from_class ) { + if ( void* base_ptr = pointer_upcast(ptr, from_class, to_class) ) { + return base_ptr; + } } return nullptr; @@ -5660,6 +5748,27 @@ namespace meta_hpp::detail } } +namespace meta_hpp::detail +{ + template < typename To, typename From > + [[nodiscard]] To* pointer_upcast(type_registry& registry, From* ptr) { + return static_cast(pointer_upcast( // + ptr, + registry.resolve_type(), + registry.resolve_type() + )); + } + + template < typename To, typename From > + [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { + return static_cast(pointer_upcast( // + ptr, + registry.resolve_type(), + registry.resolve_type() + )); + } +} + namespace meta_hpp::detail { class uarg_base { @@ -7926,8 +8035,12 @@ namespace meta_hpp return data_->argument_types; } - inline const class_set& class_type::get_bases() const noexcept { - return data_->bases; + inline const class_set& class_type::get_base_classes() const noexcept { + return data_->base_classes; + } + + inline const class_set& class_type::get_derived_classes() const noexcept { + return data_->derived_classes; } inline const constructor_set& class_type::get_constructors() const noexcept { @@ -8007,16 +8120,10 @@ namespace meta_hpp return false; } - if ( derived.data_->bases.contains(*this) ) { + if ( derived.data_->deep_upcasts.contains(*this) ) { return true; } - for ( const class_type& derived_base : derived.data_->bases ) { - if ( is_base_of(derived_base) ) { - return true; - } - } - return false; } @@ -8030,16 +8137,10 @@ namespace meta_hpp return false; } - if ( data_->bases.contains(base) ) { + if ( data_->deep_upcasts.contains(base) ) { return true; } - for ( const class_type& self_base : data_->bases ) { - if ( self_base.is_derived_from(base) ) { - return true; - } - } - return false; } @@ -8050,7 +8151,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const function& function = base.get_function(name) ) { return function; } @@ -8066,7 +8167,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const member& member = base.get_member(name) ) { return member; } @@ -8082,7 +8183,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const method& method = base.get_method(name) ) { return method; } @@ -8096,7 +8197,7 @@ namespace meta_hpp return iter->second; } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const any_type& type = base.get_typedef(name) ) { return type; } @@ -8112,7 +8213,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const variable& variable = base.get_variable(name) ) { return variable; } @@ -8184,7 +8285,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const function& function = base.get_function_with(name, first, last) ) { return function; } @@ -8224,7 +8325,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const method& method = base.get_method_with(name, first, last) ) { return method; } diff --git a/develop/untests/meta_features/multiple2_tests.cpp b/develop/untests/meta_features/multiple2_tests.cpp new file mode 100644 index 0000000..88cc309 --- /dev/null +++ b/develop/untests/meta_features/multiple2_tests.cpp @@ -0,0 +1,204 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/meta.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2021-2023, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include +#include + +namespace +{ + struct A { std::string a{"a"}; }; + struct B0 : virtual A { std::string b0{"b0"}; }; + struct B1 : virtual A { std::string b1{"b1"}; }; + + struct C : B0, B1 { std::string c{"c"}; }; + + struct D0 : C { std::string d0{"d0"}; }; + struct E0 : D0 { std::string e0{"e0"}; }; + + struct D1 : C { std::string d1{"d1"}; }; + struct E1 : D1 { std::string e1{"e1"}; }; +} + +TEST_CASE("meta/meta_features/multiple2/_") { + namespace meta = meta_hpp; + + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); + + meta::class_().base_(); + meta::class_().base_(); + + meta::class_().base_(); + meta::class_().base_(); + + // * <- B0 <- * * <- D0 * <- E0 + // A C + // * <- B1 <- * * <- D1 * <- E1 +} + +TEST_CASE("meta/meta_features/multiple2") { + namespace meta = meta_hpp; + + const meta::class_type A_type = meta::resolve_type(); + const meta::class_type B0_type = meta::resolve_type(); + const meta::class_type B1_type = meta::resolve_type(); + const meta::class_type C_type = meta::resolve_type(); + const meta::class_type D0_type = meta::resolve_type(); + const meta::class_type D1_type = meta::resolve_type(); + const meta::class_type E0_type = meta::resolve_type(); + const meta::class_type E1_type = meta::resolve_type(); + + REQUIRE(A_type); + REQUIRE(B0_type); + REQUIRE(B1_type); + REQUIRE(C_type); + REQUIRE(D0_type); + REQUIRE(D1_type); + REQUIRE(E0_type); + REQUIRE(E1_type); + + SUBCASE("is_base_of") { + CHECK(!A_type.is_base_of(A_type)); + CHECK(A_type.is_base_of(B0_type)); + CHECK(A_type.is_base_of(B1_type)); + CHECK(A_type.is_base_of(C_type)); + CHECK(A_type.is_base_of(D0_type)); + CHECK(A_type.is_base_of(D1_type)); + CHECK(A_type.is_base_of(E0_type)); + CHECK(A_type.is_base_of(E1_type)); + + CHECK(!B0_type.is_base_of(A_type)); + CHECK(!B0_type.is_base_of(B0_type)); + CHECK(!B0_type.is_base_of(B1_type)); + CHECK(B0_type.is_base_of(C_type)); + CHECK(B0_type.is_base_of(D0_type)); + CHECK(B0_type.is_base_of(D1_type)); + CHECK(B0_type.is_base_of(E0_type)); + CHECK(B0_type.is_base_of(E1_type)); + + CHECK(!B1_type.is_base_of(A_type)); + CHECK(!B1_type.is_base_of(B0_type)); + CHECK(!B1_type.is_base_of(B1_type)); + CHECK(B1_type.is_base_of(C_type)); + CHECK(B1_type.is_base_of(D0_type)); + CHECK(B1_type.is_base_of(D1_type)); + CHECK(B1_type.is_base_of(E0_type)); + CHECK(B1_type.is_base_of(E1_type)); + + CHECK(!C_type.is_base_of(A_type)); + CHECK(!C_type.is_base_of(B0_type)); + CHECK(!C_type.is_base_of(B1_type)); + CHECK(!C_type.is_base_of(C_type)); + CHECK(C_type.is_base_of(D0_type)); + CHECK(C_type.is_base_of(D1_type)); + CHECK(C_type.is_base_of(E0_type)); + CHECK(C_type.is_base_of(E1_type)); + + CHECK(!D0_type.is_base_of(A_type)); + CHECK(!D0_type.is_base_of(B0_type)); + CHECK(!D0_type.is_base_of(B1_type)); + CHECK(!D0_type.is_base_of(C_type)); + CHECK(!D0_type.is_base_of(D0_type)); + CHECK(!D0_type.is_base_of(D1_type)); + CHECK(D0_type.is_base_of(E0_type)); + CHECK(!D0_type.is_base_of(E1_type)); + + CHECK(!D1_type.is_base_of(A_type)); + CHECK(!D1_type.is_base_of(B0_type)); + CHECK(!D1_type.is_base_of(B1_type)); + CHECK(!D1_type.is_base_of(C_type)); + CHECK(!D1_type.is_base_of(D0_type)); + CHECK(!D1_type.is_base_of(D1_type)); + CHECK(!D1_type.is_base_of(E0_type)); + CHECK(D1_type.is_base_of(E1_type)); + + CHECK(!E0_type.is_base_of(A_type)); + CHECK(!E0_type.is_base_of(B0_type)); + CHECK(!E0_type.is_base_of(B1_type)); + CHECK(!E0_type.is_base_of(C_type)); + CHECK(!E0_type.is_base_of(D0_type)); + CHECK(!E0_type.is_base_of(D1_type)); + CHECK(!E0_type.is_base_of(E0_type)); + CHECK(!E0_type.is_base_of(E1_type)); + + CHECK(!E1_type.is_base_of(A_type)); + CHECK(!E1_type.is_base_of(B0_type)); + CHECK(!E1_type.is_base_of(B1_type)); + CHECK(!E1_type.is_base_of(C_type)); + CHECK(!E1_type.is_base_of(D0_type)); + CHECK(!E1_type.is_base_of(D1_type)); + CHECK(!E1_type.is_base_of(E0_type)); + CHECK(!E1_type.is_base_of(E1_type)); + } + + SUBCASE("pointer_upcast") { + using meta::detail::type_registry; + using meta::detail::pointer_upcast; + + type_registry& r{type_registry::instance()}; + + { + A a; + CHECK(pointer_upcast(r, &a) == &a); CHECK(pointer_upcast(r, &a)->a == "a"); + CHECK_FALSE(pointer_upcast(r, &a)); + CHECK_FALSE(pointer_upcast(r, &a)); + CHECK_FALSE(pointer_upcast(r, &a)); + } + + { + B0 b0; + CHECK(pointer_upcast(r, &b0) == &b0); CHECK(pointer_upcast(r, &b0)->a == "a"); + CHECK(pointer_upcast(r, &b0) == &b0); CHECK(pointer_upcast(r, &b0)->b0 == "b0"); + CHECK_FALSE(pointer_upcast(r, &b0)); + CHECK_FALSE(pointer_upcast(r, &b0)); + } + + { + B1 b1; + CHECK(pointer_upcast(r, &b1) == &b1); CHECK(pointer_upcast(r, &b1)->a == "a"); + CHECK_FALSE(pointer_upcast(r, &b1)); + CHECK(pointer_upcast(r, &b1) == &b1); CHECK(pointer_upcast(r, &b1)->b1 == "b1"); + CHECK_FALSE(pointer_upcast(r, &b1)); + } + + { + C c; + CHECK(pointer_upcast(r, &c) == &c); CHECK(pointer_upcast(r, &c)->a == "a"); + CHECK(pointer_upcast(r, &c) == &c); CHECK(pointer_upcast(r, &c)->b0 == "b0"); + CHECK(pointer_upcast(r, &c) == &c); CHECK(pointer_upcast(r, &c)->b1 == "b1"); + CHECK(pointer_upcast(r, &c) == &c); CHECK(pointer_upcast(r, &c)->c == "c"); + CHECK_FALSE(pointer_upcast(r, &c)); + CHECK_FALSE(pointer_upcast(r, &c)); + CHECK_FALSE(pointer_upcast(r, &c)); + CHECK_FALSE(pointer_upcast(r, &c)); + } + + { + E0 e0; + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->a == "a"); + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->b0 == "b0"); + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->b1 == "b1"); + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->c == "c"); + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->d0 == "d0"); + CHECK_FALSE(pointer_upcast(r, &e0)); + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->e0 == "e0"); + CHECK_FALSE(pointer_upcast(r, &e0)); + } + + { + E1 e1; + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->a == "a"); + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->b0 == "b0"); + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->b1 == "b1"); + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->c == "c"); + CHECK_FALSE(pointer_upcast(r, &e1)); + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->d1 == "d1"); + CHECK_FALSE(pointer_upcast(r, &e1)); + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->e1 == "e1"); + } + } +} diff --git a/develop/untests/meta_types/class_type_tests.cpp b/develop/untests/meta_types/class_type_tests.cpp index a68c25e..7ab1db3 100644 --- a/develop/untests/meta_types/class_type_tests.cpp +++ b/develop/untests/meta_types/class_type_tests.cpp @@ -202,11 +202,18 @@ TEST_CASE("meta/meta_types/class_type") { CHECK(final_derived_clazz_type.get_constructors().size() == 1); } - SUBCASE("get_bases") { - CHECK(base_clazz_1_type.get_bases() == meta::class_set{}); - CHECK(base_clazz_2_type.get_bases() == meta::class_set{}); - CHECK(derived_clazz_type.get_bases() == meta::class_set{base_clazz_1_type, base_clazz_2_type}); - CHECK(final_derived_clazz_type.get_bases() == meta::class_set{derived_clazz_type}); + SUBCASE("get_base_classes") { + CHECK(base_clazz_1_type.get_base_classes() == meta::class_set{}); + CHECK(base_clazz_2_type.get_base_classes() == meta::class_set{}); + CHECK(derived_clazz_type.get_base_classes() == meta::class_set{base_clazz_1_type, base_clazz_2_type}); + CHECK(final_derived_clazz_type.get_base_classes() == meta::class_set{derived_clazz_type}); + } + + SUBCASE("get_derived_classes") { + CHECK(base_clazz_1_type.get_derived_classes() == meta::class_set{derived_clazz_type}); + CHECK(base_clazz_2_type.get_derived_classes() == meta::class_set{derived_clazz_type}); + CHECK(derived_clazz_type.get_derived_classes() == meta::class_set{final_derived_clazz_type}); + CHECK(final_derived_clazz_type.get_derived_classes() == meta::class_set{}); } SUBCASE("get_functions") { diff --git a/headers/meta.hpp/meta_binds/class_bind.hpp b/headers/meta.hpp/meta_binds/class_bind.hpp index 8515490..dcbaf52 100644 --- a/headers/meta.hpp/meta_binds/class_bind.hpp +++ b/headers/meta.hpp/meta_binds/class_bind.hpp @@ -10,6 +10,73 @@ #include "../meta_binds.hpp" #include "../meta_registry.hpp" +namespace meta_hpp::detail::class_bind_impl +{ + using base_upcasts_t = class_type_data::base_upcasts_t; + using deep_upcasts_t = class_type_data::deep_upcasts_t; + + using upcast_func_t = class_type_data::upcast_func_t; + using upcast_func_list_t = class_type_data::upcast_func_list_t; + + using new_bases_db_t = std::map; + using deep_upcasts_db_t = std::map>; + using derived_classes_db_t = std::map>; + + template < class_kind Class, class_kind Base > + requires detail::class_bind_base_kind + void update_new_bases_db( // + new_bases_db_t& new_bases_db + ) { + new_bases_db.emplace(resolve_type(), [](void* from) { + return static_cast(static_cast(static_cast(from))); + }); + } + + inline void update_deep_upcasts_db( // + const class_type& derived_class, + const class_type& new_base_class, + const upcast_func_list_t& derived_to_new_base, + deep_upcasts_db_t& deep_upcasts_db + ) { + const class_type_data& derived_class_data = *type_access(derived_class); + const class_type_data& new_base_class_data = *type_access(new_base_class); + + const auto [deep_upcasts_db_iter, _] = deep_upcasts_db.try_emplace(derived_class, derived_class_data.deep_upcasts); + deep_upcasts_t& derived_deep_upcasts = deep_upcasts_db_iter->second; + derived_deep_upcasts.emplace(new_base_class, derived_to_new_base); + + for ( auto&& [new_deep_class, new_base_to_deep] : new_base_class_data.deep_upcasts ) { + upcast_func_list_t derived_to_new_deep; + derived_to_new_deep.reserve(derived_to_new_base.size() + new_base_to_deep.size()); + derived_to_new_deep.insert(derived_to_new_deep.end(), derived_to_new_base.begin(), derived_to_new_base.end()); + derived_to_new_deep.insert(derived_to_new_deep.end(), new_base_to_deep.begin(), new_base_to_deep.end()); + derived_deep_upcasts.emplace(new_deep_class, std::move(derived_to_new_deep)); + } + + for ( const class_type& subderived_class : derived_class_data.derived_classes ) { + const class_type_data& subderived_data = *type_access(subderived_class); + + upcast_func_list_t subderived_to_new_base; + subderived_to_new_base.reserve(derived_to_new_base.size() + 1); + subderived_to_new_base.insert(subderived_to_new_base.end(), subderived_data.base_upcasts.at(derived_class)); + subderived_to_new_base.insert(subderived_to_new_base.end(), derived_to_new_base.begin(), derived_to_new_base.end()); + + update_deep_upcasts_db(subderived_class, new_base_class, subderived_to_new_base, deep_upcasts_db); + } + } + + inline void updata_derived_classes_db( // + const class_type& self_class, + const class_type& new_base_class, + derived_classes_db_t& derived_classes_db + ) { + const class_type_data& base_class_data = *type_access(new_base_class); + class_set new_derived_classes{base_class_data.derived_classes}; + new_derived_classes.emplace(self_class); + derived_classes_db.emplace(new_base_class, std::move(new_derived_classes)); + } +} + namespace meta_hpp { template < detail::class_kind Class > @@ -28,28 +95,47 @@ namespace meta_hpp template < detail::class_kind... Bases > requires(... && detail::class_bind_base_kind) class_bind& class_bind::base_() { - const auto register_base{[this](std::in_place_type_t) { - const class_type& base_type = resolve_type(); + using namespace detail; + using namespace detail::class_bind_impl; - auto&& [position, emplaced] = get_data().bases.emplace(base_type); - if ( !emplaced ) { - return; + if ( (... && get_data().base_classes.contains(resolve_type())) ) { + return *this; + } + + new_bases_db_t new_bases_db; + (update_new_bases_db(new_bases_db), ...); + + deep_upcasts_db_t deep_upcasts_db; + derived_classes_db_t derived_classes_db; + + class_set new_base_classes{get_data().base_classes}; + base_upcasts_t new_base_upcasts{get_data().base_upcasts}; + + for ( auto&& [new_base_class, self_to_new_base] : new_bases_db ) { + if ( new_base_classes.contains(new_base_class) ) { + continue; } - META_HPP_TRY { - get_data().bases_info.emplace( // - base_type, - detail::class_type_data::base_info{ - .upcast{[](void* derived) -> void* { return static_cast(static_cast(derived)); }}} - ); - } - META_HPP_CATCH(...) { - get_data().bases.erase(position); - META_HPP_RETHROW(); - } - }}; + update_deep_upcasts_db(*this, new_base_class, {self_to_new_base}, deep_upcasts_db); + updata_derived_classes_db(*this, new_base_class, derived_classes_db); + + new_base_classes.emplace(new_base_class); + new_base_upcasts.emplace(new_base_class, self_to_new_base); + } + + get_data().base_classes.swap(new_base_classes); + get_data().base_upcasts.swap(new_base_upcasts); + + for ( auto&& [derived_class, new_deep_upcasts] : deep_upcasts_db ) { + class_type_data& derived_data = *type_access(derived_class); + derived_data.deep_upcasts.swap(new_deep_upcasts); + } + + for ( auto&& [base_class, new_derived_classes] : derived_classes_db ) { + class_type_data& base_data = *type_access(base_class); + base_data.derived_classes.swap(new_derived_classes); + } - (register_base(std::in_place_type), ...); return *this; } diff --git a/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp b/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp index 57e0f6f..8d8ed5a 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp @@ -124,37 +124,34 @@ namespace meta_hpp::detail return ptr; } - for ( auto&& [base_type, base_info] : type_access(from)->bases_info ) { - if ( base_type == to ) { - return base_info.upcast(ptr); - } + void* base_ptr = nullptr; - if ( base_type.is_derived_from(to) ) { - return pointer_upcast(base_info.upcast(ptr), base_type, to); + class_type_data& from_data = *type_access(from); + class_type_data::deep_upcasts_t& deep_upcasts = from_data.deep_upcasts; + + for ( auto iter{deep_upcasts.lower_bound(to)}; iter != deep_upcasts.end() && iter->first == to; ++iter ) { + void* new_base_ptr = [ptr, iter]() mutable { + for ( class_type_data::upcast_func_t upcast : iter->second ) { + ptr = upcast(ptr); + } + return ptr; + }(); + + if ( base_ptr == nullptr ) { + base_ptr = new_base_ptr; + } else if ( base_ptr != new_base_ptr ) { + // ambiguous conversions + return nullptr; } } - return nullptr; + return base_ptr; } [[nodiscard]] inline const void* pointer_upcast(const void* ptr, const class_type& from, const class_type& to) { // NOLINTNEXTLINE(*-const-cast) return pointer_upcast(const_cast(ptr), from, to); } - - template < class_kind To, class_kind From > - [[nodiscard]] To* pointer_upcast(type_registry& registry, From* ptr) { - const class_type& to_class = registry.resolve_type(); - const class_type& from_class = registry.resolve_type(); - return static_cast(pointer_upcast(ptr, from_class, to_class)); - } - - template < class_kind To, class_kind From > - [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { - const class_type& to_class = registry.resolve_type(); - const class_type& from_class = registry.resolve_type(); - return static_cast(pointer_upcast(ptr, from_class, to_class)); - } } namespace meta_hpp::detail @@ -171,8 +168,10 @@ namespace meta_hpp::detail const class_type& to_class = to.as_class(); const class_type& from_class = from.as_class(); - if ( to_class && from_class && from_class.is_derived_from(to_class) ) { - return pointer_upcast(ptr, from_class, to_class); + if ( to_class && from_class ) { + if ( void* base_ptr = pointer_upcast(ptr, from_class, to_class) ) { + return base_ptr; + } } return nullptr; @@ -183,3 +182,24 @@ namespace meta_hpp::detail return pointer_upcast(const_cast(ptr), from, to); } } + +namespace meta_hpp::detail +{ + template < typename To, typename From > + [[nodiscard]] To* pointer_upcast(type_registry& registry, From* ptr) { + return static_cast(pointer_upcast( // + ptr, + registry.resolve_type(), + registry.resolve_type() + )); + } + + template < typename To, typename From > + [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { + return static_cast(pointer_upcast( // + ptr, + registry.resolve_type(), + registry.resolve_type() + )); + } +} diff --git a/headers/meta.hpp/meta_types.hpp b/headers/meta.hpp/meta_types.hpp index 1c179e7..9d109f0 100644 --- a/headers/meta.hpp/meta_types.hpp +++ b/headers/meta.hpp/meta_types.hpp @@ -157,7 +157,8 @@ namespace meta_hpp [[nodiscard]] any_type get_argument_type(std::size_t position) const noexcept; [[nodiscard]] const any_type_list& get_argument_types() const noexcept; - [[nodiscard]] const class_set& get_bases() const noexcept; + [[nodiscard]] const class_set& get_base_classes() const noexcept; + [[nodiscard]] const class_set& get_derived_classes() const noexcept; [[nodiscard]] const constructor_set& get_constructors() const noexcept; [[nodiscard]] const destructor_set& get_destructors() const noexcept; [[nodiscard]] const function_set& get_functions() const noexcept; @@ -391,7 +392,8 @@ namespace meta_hpp::detail const std::size_t align; const any_type_list argument_types; - class_set bases; + class_set base_classes; + class_set derived_classes; constructor_set constructors; destructor_set destructors; function_set functions; @@ -400,13 +402,14 @@ namespace meta_hpp::detail typedef_map typedefs; variable_set variables; - struct base_info final { - using upcast_fptr = void* (*)(void*); - const upcast_fptr upcast; - }; + using upcast_func_t = void* (*)(void*); + using upcast_func_list_t = std::vector; - using base_info_map = std::map>; - base_info_map bases_info; + using base_upcasts_t = std::map>; + using deep_upcasts_t = std::multimap>; + + base_upcasts_t base_upcasts; + deep_upcasts_t deep_upcasts; template < class_kind Class > explicit class_type_data(type_list); diff --git a/headers/meta.hpp/meta_types/class_type.hpp b/headers/meta.hpp/meta_types/class_type.hpp index cbea19d..5718aeb 100644 --- a/headers/meta.hpp/meta_types/class_type.hpp +++ b/headers/meta.hpp/meta_types/class_type.hpp @@ -58,8 +58,12 @@ namespace meta_hpp return data_->argument_types; } - inline const class_set& class_type::get_bases() const noexcept { - return data_->bases; + inline const class_set& class_type::get_base_classes() const noexcept { + return data_->base_classes; + } + + inline const class_set& class_type::get_derived_classes() const noexcept { + return data_->derived_classes; } inline const constructor_set& class_type::get_constructors() const noexcept { @@ -139,16 +143,10 @@ namespace meta_hpp return false; } - if ( derived.data_->bases.contains(*this) ) { + if ( derived.data_->deep_upcasts.contains(*this) ) { return true; } - for ( const class_type& derived_base : derived.data_->bases ) { - if ( is_base_of(derived_base) ) { - return true; - } - } - return false; } @@ -162,16 +160,10 @@ namespace meta_hpp return false; } - if ( data_->bases.contains(base) ) { + if ( data_->deep_upcasts.contains(base) ) { return true; } - for ( const class_type& self_base : data_->bases ) { - if ( self_base.is_derived_from(base) ) { - return true; - } - } - return false; } @@ -182,7 +174,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const function& function = base.get_function(name) ) { return function; } @@ -198,7 +190,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const member& member = base.get_member(name) ) { return member; } @@ -214,7 +206,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const method& method = base.get_method(name) ) { return method; } @@ -228,7 +220,7 @@ namespace meta_hpp return iter->second; } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const any_type& type = base.get_typedef(name) ) { return type; } @@ -244,7 +236,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const variable& variable = base.get_variable(name) ) { return variable; } @@ -316,7 +308,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const function& function = base.get_function_with(name, first, last) ) { return function; } @@ -356,7 +348,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const method& method = base.get_method_with(name, first, last) ) { return method; }