diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index c92873a..c31356a 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -2574,6 +2574,10 @@ namespace meta_hpp [[nodiscard]] bool is_direct_base_of() const noexcept; [[nodiscard]] bool is_direct_base_of(const class_type& derived) const noexcept; + template < detail::class_kind Derived > + [[nodiscard]] bool is_virtual_base_of() const noexcept; + [[nodiscard]] bool is_virtual_base_of(const class_type& derived) const noexcept; + template < detail::class_kind Base > [[nodiscard]] bool is_derived_from() const noexcept; [[nodiscard]] bool is_derived_from(const class_type& base) const noexcept; @@ -2582,6 +2586,10 @@ namespace meta_hpp [[nodiscard]] bool is_direct_derived_from() const noexcept; [[nodiscard]] bool is_direct_derived_from(const class_type& base) const noexcept; + template < detail::class_kind Base > + [[nodiscard]] bool is_virtual_derived_from() const noexcept; + [[nodiscard]] bool is_virtual_derived_from(const class_type& base) const noexcept; + [[nodiscard]] function get_function(std::string_view name) const noexcept; [[nodiscard]] member get_member(std::string_view name) const noexcept; [[nodiscard]] method get_method(std::string_view name) const noexcept; @@ -2800,17 +2808,28 @@ namespace meta_hpp::detail typedef_map typedefs; variable_set variables; - using upcast_func_t = void* (*)(void*); + struct upcast_func_t final { + void* (*upcast)(void*){}; + bool is_virtual_upcast{}; + class_type target_class{}; + + template < typename Derived, typename Base > + requires std::is_base_of_v + upcast_func_t(std::in_place_type_t, std::in_place_type_t); + + [[nodiscard]] void* apply(void* ptr) const noexcept; + [[nodiscard]] const void* apply(const void* ptr) const noexcept; + }; struct upcast_func_list_t final { + class_set vbases; std::vector upcasts; - upcast_func_list_t() = default; - upcast_func_list_t(upcast_func_t _upcast); - upcast_func_list_t(std::vector _upcasts); + upcast_func_list_t(const upcast_func_t& _upcast); + upcast_func_list_t(class_set _vbases, std::vector _upcasts); - void* apply(void* ptr) const noexcept; - const void* apply(const void* ptr) const noexcept; + [[nodiscard]] void* apply(void* ptr) const noexcept; + [[nodiscard]] const void* apply(const void* ptr) const noexcept; friend upcast_func_list_t operator+(const upcast_func_list_t& l, const upcast_func_list_t& r); }; @@ -4861,13 +4880,14 @@ namespace meta_hpp::detail::class_bind_impl using derived_classes_db_t = std::map>; template < class_kind Class, class_kind Base > - requires detail::class_bind_base_kind + requires std::is_base_of_v 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))); - }); + new_bases_db.try_emplace( // + resolve_type(), + std::in_place_type, + std::in_place_type); } inline void update_deep_upcasts_db( // @@ -5722,8 +5742,19 @@ namespace meta_hpp::detail const class_type& base_class = base.as_class(); const class_type& derived_class = derived.as_class(); - if ( base_class && derived_class && base_class.is_base_of(derived_class) ) { - return true; + if ( base_class && derived_class ) { + const class_type_data& derived_data = *type_access(derived_class); + const class_type_data::deep_upcasts_t& deep_upcasts = derived_data.deep_upcasts; + + if ( const auto iter{deep_upcasts.lower_bound(base)}; iter != deep_upcasts.end() && iter->first == base ) { + if ( const auto next_iter{std::next(iter)}; next_iter == deep_upcasts.end() || next_iter->first != base ) { + return true; + } + + if ( base_class.is_virtual_base_of(derived_class) ) { + return true; + } + } } return false; @@ -5732,7 +5763,7 @@ namespace meta_hpp::detail namespace meta_hpp::detail { - [[nodiscard]] inline void* pointer_upcast(void* ptr, const class_type& from, const class_type& to) { + [[nodiscard]] inline void* unchecked_pointer_upcast(void* ptr, const class_type& from, const class_type& to) { if ( nullptr == ptr || !from || !to ) { return nullptr; } @@ -5741,32 +5772,24 @@ namespace meta_hpp::detail return ptr; } - void* base_ptr = nullptr; + const class_type_data& from_data = *type_access(from); - 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 ) { - if ( void* new_base_ptr{iter->second.apply(ptr)}; base_ptr == nullptr ) { - base_ptr = new_base_ptr; - } else if ( base_ptr != new_base_ptr ) { - // ambiguous conversions - return nullptr; - } + if ( auto iter{from_data.deep_upcasts.find(to)}; iter != from_data.deep_upcasts.end() ) { + return iter->second.apply(ptr); } - return base_ptr; + return nullptr; } - [[nodiscard]] inline const void* pointer_upcast(const void* ptr, const class_type& from, const class_type& to) { + [[nodiscard]] inline const void* unchecked_pointer_upcast(const void* ptr, const class_type& from, const class_type& to) { // NOLINTNEXTLINE(*-const-cast) - return pointer_upcast(const_cast(ptr), from, to); + return unchecked_pointer_upcast(const_cast(ptr), from, to); } } namespace meta_hpp::detail { - [[nodiscard]] inline void* pointer_upcast(void* ptr, const any_type& from, const any_type& to) { + [[nodiscard]] inline void* unchecked_pointer_upcast(void* ptr, const any_type& from, const any_type& to) { if ( nullptr == ptr || !from || !to ) { return nullptr; } @@ -5779,7 +5802,7 @@ namespace meta_hpp::detail const class_type& from_class = from.as_class(); if ( to_class && from_class ) { - if ( void* base_ptr = pointer_upcast(ptr, from_class, to_class) ) { + if ( void* base_ptr = unchecked_pointer_upcast(ptr, from_class, to_class) ) { return base_ptr; } } @@ -5787,17 +5810,17 @@ namespace meta_hpp::detail return nullptr; } - [[nodiscard]] inline const void* pointer_upcast(const void* ptr, const any_type& from, const any_type& to) { + [[nodiscard]] inline const void* unchecked_pointer_upcast(const void* ptr, const any_type& from, const any_type& to) { // NOLINTNEXTLINE(*-const-cast) - return pointer_upcast(const_cast(ptr), from, to); + return unchecked_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( // + [[nodiscard]] To* unchecked_pointer_upcast(type_registry& registry, From* ptr) { + return static_cast(unchecked_pointer_upcast( // ptr, registry.resolve_type(), registry.resolve_type() @@ -5805,8 +5828,8 @@ namespace meta_hpp::detail } template < typename To, typename From > - [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { - return static_cast(pointer_upcast( // + [[nodiscard]] const To* unchecked_pointer_upcast(type_registry& registry, const From* ptr) { + return static_cast(unchecked_pointer_upcast( // ptr, registry.resolve_type(), registry.resolve_type() @@ -6059,21 +6082,27 @@ namespace meta_hpp::detail if ( from_type.is_array() ) { const array_type& from_type_array = from_type.as_array(); - return static_cast(pointer_upcast( // + void* to_ptr = unchecked_pointer_upcast( // data_, from_type_array.get_data_type(), to_type_ptr.get_data_type() - )); + ); + META_HPP_ASSERT(to_ptr); + + return static_cast(to_ptr); } if ( from_type.is_pointer() ) { const pointer_type& from_type_ptr = from_type.as_pointer(); - return static_cast(pointer_upcast( // + void* to_ptr = unchecked_pointer_upcast( // *static_cast(data_), from_type_ptr.get_data_type(), to_type_ptr.get_data_type() - )); + ); + META_HPP_ASSERT(to_ptr); + + return static_cast(to_ptr); } throw_exception(error_code::bad_argument_cast); @@ -6094,7 +6123,8 @@ namespace meta_hpp::detail const any_type& from_type = get_raw_type(); const any_type& to_type = registry.resolve_type(); - void* to_ptr = pointer_upcast(data_, from_type, to_type); + void* to_ptr = unchecked_pointer_upcast(data_, from_type, to_type); + META_HPP_ASSERT(to_ptr); if constexpr ( std::is_lvalue_reference_v ) { return *static_cast(to_ptr); @@ -6561,10 +6591,12 @@ namespace meta_hpp::detail const any_type& to_type = registry.resolve_type(); if ( from_type.is_class() && to_type.is_class() ) { - const class_type& from_class = from_type.as_class(); - const class_type& to_class = to_type.as_class(); - - void* to_ptr = pointer_upcast(data_, from_class, to_class); + void* to_ptr = unchecked_pointer_upcast( // + data_, + from_type.as_class(), + to_type.as_class() + ); + META_HPP_ASSERT(to_ptr); if constexpr ( !std::is_reference_v ) { return *static_cast(to_ptr); @@ -6584,11 +6616,12 @@ namespace meta_hpp::detail const any_type& from_data_type = from_type_ptr.get_data_type(); if ( from_data_type.is_class() && to_type.is_class() ) { - const class_type& from_data_class = from_data_type.as_class(); - const class_type& to_class = to_type.as_class(); - - void** from_data_ptr = static_cast(data_); - void* to_ptr = pointer_upcast(*from_data_ptr, from_data_class, to_class); + void* to_ptr = unchecked_pointer_upcast( // + *static_cast(data_), + from_data_type.as_class(), + to_type.as_class() + ); + META_HPP_ASSERT(to_ptr); if constexpr ( !std::is_reference_v ) { return *static_cast(to_ptr); @@ -8121,16 +8154,45 @@ namespace meta_hpp::detail , size{class_traits::size} , align{class_traits::align} , argument_types{resolve_types(typename class_traits::argument_types{})} {} +} - inline class_type_data::upcast_func_list_t::upcast_func_list_t(upcast_func_t _upcast) - : upcasts{_upcast} {} +namespace meta_hpp::detail +{ + template < typename Derived, typename Base > + requires std::is_base_of_v + inline class_type_data::upcast_func_t::upcast_func_t(std::in_place_type_t, std::in_place_type_t) + : upcast{[](void* from) -> void* { return static_cast(static_cast(from)); }} + , is_virtual_upcast{is_virtual_base_of_v} + , target_class{resolve_type()} {} - inline class_type_data::upcast_func_list_t::upcast_func_list_t(std::vector _upcasts) - : upcasts{std::move(_upcasts)} {} + inline void* class_type_data::upcast_func_t::apply(void* ptr) const noexcept { + return upcast(ptr); + } + + inline const void* class_type_data::upcast_func_t::apply(const void* ptr) const noexcept { + // NOLINTNEXTLINE(*-const-cast) + return apply(const_cast(ptr)); + } +} + +namespace meta_hpp::detail +{ + inline class_type_data::upcast_func_list_t::upcast_func_list_t(const upcast_func_t& _upcast) + : upcasts{_upcast} { + for ( const upcast_func_t& upcast : upcasts ) { + if ( upcast.is_virtual_upcast ) { + vbases.emplace(upcast.target_class); + } + } + } + + inline class_type_data::upcast_func_list_t::upcast_func_list_t(class_set _vbases, std::vector _upcasts) + : vbases{std::move(_vbases)} + , upcasts{std::move(_upcasts)} {} inline void* class_type_data::upcast_func_list_t::apply(void* ptr) const noexcept { - for ( upcast_func_t upcast : upcasts ) { - ptr = upcast(ptr); + for ( const upcast_func_t& upcast : upcasts ) { + ptr = upcast.apply(ptr); } return ptr; } @@ -8144,11 +8206,16 @@ namespace meta_hpp::detail const class_type_data::upcast_func_list_t& l, const class_type_data::upcast_func_list_t& r ) { + class_set new_vbases; + new_vbases.insert(l.vbases.begin(), l.vbases.end()); + new_vbases.insert(r.vbases.begin(), r.vbases.end()); + std::vector new_upcasts; new_upcasts.reserve(l.upcasts.size() + r.upcasts.size()); new_upcasts.insert(new_upcasts.end(), l.upcasts.begin(), l.upcasts.end()); new_upcasts.insert(new_upcasts.end(), r.upcasts.begin(), r.upcasts.end()); - return class_type_data::upcast_func_list_t{std::move(new_upcasts)}; + + return class_type_data::upcast_func_list_t{std::move(new_vbases), std::move(new_upcasts)}; } } @@ -8271,13 +8338,38 @@ namespace meta_hpp return is_valid() && derived.is_valid() && derived.data_->base_upcasts.contains(*this); } + template < detail::class_kind Derived > + bool class_type::is_virtual_base_of() const noexcept { + return is_virtual_base_of(resolve_type()); + } + + inline bool class_type::is_virtual_base_of(const class_type& derived) const noexcept { + if ( !is_valid() || !derived.is_valid() ) { + return false; + } + + const detail::class_type_data& derived_data = *derived.data_; + const auto upcasts_range = derived_data.deep_upcasts.equal_range(*this); + + if ( upcasts_range.first == upcasts_range.second ) { + return false; + } + + const class_set& first_vbases = upcasts_range.first->second.vbases; + return std::any_of(first_vbases.begin(), first_vbases.end(), [&upcasts_range](const class_type& vbase) { + return std::all_of(std::next(upcasts_range.first), upcasts_range.second, [&vbase](auto&& upcasts_p) { + return upcasts_p.second.vbases.contains(vbase); + }); + }); + } + template < detail::class_kind Base > bool class_type::is_derived_from() const noexcept { return is_derived_from(resolve_type()); } inline bool class_type::is_derived_from(const class_type& base) const noexcept { - return is_valid() && base.is_valid() && data_->deep_upcasts.contains(base); + return base.is_base_of(*this); } template < detail::class_kind Base > @@ -8286,7 +8378,16 @@ namespace meta_hpp } inline bool class_type::is_direct_derived_from(const class_type& base) const noexcept { - return is_valid() && base.is_valid() && data_->base_upcasts.contains(base); + return base.is_direct_base_of(*this); + } + + template < detail::class_kind Base > + bool class_type::is_virtual_derived_from() const noexcept { + return is_virtual_derived_from(resolve_type()); + } + + inline bool class_type::is_virtual_derived_from(const class_type& base) const noexcept { + return base.is_virtual_base_of(*this); } inline function class_type::get_function(std::string_view name) const noexcept { diff --git a/develop/untests/meta_features/ambiguous_tests.cpp b/develop/untests/meta_features/ambiguous_tests.cpp new file mode 100644 index 0000000..31f6c04 --- /dev/null +++ b/develop/untests/meta_features/ambiguous_tests.cpp @@ -0,0 +1,96 @@ +/******************************************************************************* + * 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 A1 { std::string a1{"a1"}; }; + struct B1 : A1 { std::string b1{"b1"}; }; + struct C1 : A1 { std::string c1{"c1"}; }; + struct D1 : B1, C1 { std::string d1{"d1"}; }; + + struct A2 { std::string a2{"a2"}; }; + struct B2 : virtual A2 { std::string b2{"b2"}; }; + struct C2 : virtual A2 { std::string c2{"c2"}; }; + struct D2 : B2, C2 { std::string d2{"d2"}; }; + + struct A3 { std::string a3{"a3"}; }; + struct B3 : virtual A3 { std::string b3{"b3"}; }; + struct C3 : A3 { std::string c3{"c3"}; }; + struct D3 : B3, C3 { std::string d3{"d3"}; }; +} + +TEST_CASE("meta/meta_features/ambiguous") { + namespace meta = meta_hpp; + + { + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); + + // A1 < B1 + // < D1 + // A1 < C1 + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + + CHECK(meta::is_invocable_with(+[](const A1&){}, A1{})); + CHECK(meta::is_invocable_with(+[](const A1&){}, B1{})); + CHECK(meta::is_invocable_with(+[](const A1&){}, C1{})); + CHECK_FALSE(meta::is_invocable_with(+[](const A1&){}, D1{})); + + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + } + + { + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); + + // A2 <= B2 + // < D2 + // A2 <= C2 + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + + CHECK(meta::is_invocable_with(+[](const A2&){}, A2{})); + CHECK(meta::is_invocable_with(+[](const A2&){}, B2{})); + CHECK(meta::is_invocable_with(+[](const A2&){}, C2{})); + CHECK(meta::is_invocable_with(+[](const A2&){}, D2{})); + + CHECK(meta::resolve_type().is_virtual_base_of()); + } + + { + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); + + // A3 <= B3 + // < D3 + // A3 < C3 + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + + CHECK(meta::is_invocable_with(+[](const A3&){}, A3{})); + CHECK(meta::is_invocable_with(+[](const A3&){}, B3{})); + CHECK(meta::is_invocable_with(+[](const A3&){}, C3{})); + CHECK_FALSE(meta::is_invocable_with(+[](const A3&){}, D3{})); + + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + } +} diff --git a/develop/untests/meta_features/diamond_tests.cpp b/develop/untests/meta_features/diamond_tests.cpp index 11384af..adf6fd9 100644 --- a/develop/untests/meta_features/diamond_tests.cpp +++ b/develop/untests/meta_features/diamond_tests.cpp @@ -120,47 +120,47 @@ TEST_CASE("meta/meta_features/diamond") { CHECK(!E_type.is_derived_from(E_type)); } - SUBCASE("pointer_upcast") { - using meta::detail::pointer_upcast; + SUBCASE("unchecked_pointer_upcast") { + using meta::detail::unchecked_pointer_upcast; { A a; - CHECK(pointer_upcast(r, &a) == &a); - CHECK_FALSE(pointer_upcast(r, &a)); - CHECK_FALSE(pointer_upcast(r, &a)); - CHECK_FALSE(pointer_upcast(r, &a)); - CHECK_FALSE(pointer_upcast(r, &a)); + CHECK(unchecked_pointer_upcast(r, &a) == &a); + CHECK_FALSE(unchecked_pointer_upcast(r, &a)); + CHECK_FALSE(unchecked_pointer_upcast(r, &a)); + CHECK_FALSE(unchecked_pointer_upcast(r, &a)); + CHECK_FALSE(unchecked_pointer_upcast(r, &a)); } { const B b; - CHECK(pointer_upcast(r, &b) == &b); - CHECK(pointer_upcast(r, &b) == &b); - CHECK_FALSE(pointer_upcast(r, &b)); - CHECK_FALSE(pointer_upcast(r, &b)); - CHECK_FALSE(pointer_upcast(r, &b)); + CHECK(unchecked_pointer_upcast(r, &b) == &b); + CHECK(unchecked_pointer_upcast(r, &b) == &b); + CHECK_FALSE(unchecked_pointer_upcast(r, &b)); + CHECK_FALSE(unchecked_pointer_upcast(r, &b)); + CHECK_FALSE(unchecked_pointer_upcast(r, &b)); } { C c; - CHECK(pointer_upcast(r, &c) == &c); - CHECK_FALSE(pointer_upcast(r, &c)); - CHECK(pointer_upcast(r, &c) == &c); - CHECK_FALSE(pointer_upcast(r, &c)); - CHECK_FALSE(pointer_upcast(r, &c)); + CHECK(unchecked_pointer_upcast(r, &c) == &c); + CHECK_FALSE(unchecked_pointer_upcast(r, &c)); + CHECK(unchecked_pointer_upcast(r, &c) == &c); + CHECK_FALSE(unchecked_pointer_upcast(r, &c)); + CHECK_FALSE(unchecked_pointer_upcast(r, &c)); } { const D d; - CHECK(pointer_upcast(r, &d) == &d); - CHECK(pointer_upcast(r, &d) == &d); - CHECK(pointer_upcast(r, &d) == &d); - CHECK(pointer_upcast(r, &d) == &d); - CHECK_FALSE(pointer_upcast(r, &d)); + CHECK(unchecked_pointer_upcast(r, &d) == &d); + CHECK(unchecked_pointer_upcast(r, &d) == &d); + CHECK(unchecked_pointer_upcast(r, &d) == &d); + CHECK(unchecked_pointer_upcast(r, &d) == &d); + CHECK_FALSE(unchecked_pointer_upcast(r, &d)); } { E e; - CHECK_FALSE(pointer_upcast(r, &e)); - CHECK_FALSE(pointer_upcast(r, &e)); - CHECK_FALSE(pointer_upcast(r, &e)); - CHECK_FALSE(pointer_upcast(r, &e)); - CHECK(pointer_upcast(r, &e) == &e); + CHECK_FALSE(unchecked_pointer_upcast(r, &e)); + CHECK_FALSE(unchecked_pointer_upcast(r, &e)); + CHECK_FALSE(unchecked_pointer_upcast(r, &e)); + CHECK_FALSE(unchecked_pointer_upcast(r, &e)); + CHECK(unchecked_pointer_upcast(r, &e) == &e); } } diff --git a/develop/untests/meta_features/multiple2_tests.cpp b/develop/untests/meta_features/multiple2_tests.cpp index 88cc309..fd3533a 100644 --- a/develop/untests/meta_features/multiple2_tests.cpp +++ b/develop/untests/meta_features/multiple2_tests.cpp @@ -135,70 +135,70 @@ TEST_CASE("meta/meta_features/multiple2") { CHECK(!E1_type.is_base_of(E1_type)); } - SUBCASE("pointer_upcast") { + SUBCASE("unchecked_pointer_upcast") { using meta::detail::type_registry; - using meta::detail::pointer_upcast; + using meta::detail::unchecked_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)); + CHECK(unchecked_pointer_upcast(r, &a) == &a); CHECK(unchecked_pointer_upcast(r, &a)->a == "a"); + CHECK_FALSE(unchecked_pointer_upcast(r, &a)); + CHECK_FALSE(unchecked_pointer_upcast(r, &a)); + CHECK_FALSE(unchecked_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)); + CHECK(unchecked_pointer_upcast(r, &b0) == &b0); CHECK(unchecked_pointer_upcast(r, &b0)->a == "a"); + CHECK(unchecked_pointer_upcast(r, &b0) == &b0); CHECK(unchecked_pointer_upcast(r, &b0)->b0 == "b0"); + CHECK_FALSE(unchecked_pointer_upcast(r, &b0)); + CHECK_FALSE(unchecked_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)); + CHECK(unchecked_pointer_upcast(r, &b1) == &b1); CHECK(unchecked_pointer_upcast(r, &b1)->a == "a"); + CHECK_FALSE(unchecked_pointer_upcast(r, &b1)); + CHECK(unchecked_pointer_upcast(r, &b1) == &b1); CHECK(unchecked_pointer_upcast(r, &b1)->b1 == "b1"); + CHECK_FALSE(unchecked_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)); + CHECK(unchecked_pointer_upcast(r, &c) == &c); CHECK(unchecked_pointer_upcast(r, &c)->a == "a"); + CHECK(unchecked_pointer_upcast(r, &c) == &c); CHECK(unchecked_pointer_upcast(r, &c)->b0 == "b0"); + CHECK(unchecked_pointer_upcast(r, &c) == &c); CHECK(unchecked_pointer_upcast(r, &c)->b1 == "b1"); + CHECK(unchecked_pointer_upcast(r, &c) == &c); CHECK(unchecked_pointer_upcast(r, &c)->c == "c"); + CHECK_FALSE(unchecked_pointer_upcast(r, &c)); + CHECK_FALSE(unchecked_pointer_upcast(r, &c)); + CHECK_FALSE(unchecked_pointer_upcast(r, &c)); + CHECK_FALSE(unchecked_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)); + CHECK(unchecked_pointer_upcast(r, &e0) == &e0); CHECK(unchecked_pointer_upcast(r, &e0)->a == "a"); + CHECK(unchecked_pointer_upcast(r, &e0) == &e0); CHECK(unchecked_pointer_upcast(r, &e0)->b0 == "b0"); + CHECK(unchecked_pointer_upcast(r, &e0) == &e0); CHECK(unchecked_pointer_upcast(r, &e0)->b1 == "b1"); + CHECK(unchecked_pointer_upcast(r, &e0) == &e0); CHECK(unchecked_pointer_upcast(r, &e0)->c == "c"); + CHECK(unchecked_pointer_upcast(r, &e0) == &e0); CHECK(unchecked_pointer_upcast(r, &e0)->d0 == "d0"); + CHECK_FALSE(unchecked_pointer_upcast(r, &e0)); + CHECK(unchecked_pointer_upcast(r, &e0) == &e0); CHECK(unchecked_pointer_upcast(r, &e0)->e0 == "e0"); + CHECK_FALSE(unchecked_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"); + CHECK(unchecked_pointer_upcast(r, &e1) == &e1); CHECK(unchecked_pointer_upcast(r, &e1)->a == "a"); + CHECK(unchecked_pointer_upcast(r, &e1) == &e1); CHECK(unchecked_pointer_upcast(r, &e1)->b0 == "b0"); + CHECK(unchecked_pointer_upcast(r, &e1) == &e1); CHECK(unchecked_pointer_upcast(r, &e1)->b1 == "b1"); + CHECK(unchecked_pointer_upcast(r, &e1) == &e1); CHECK(unchecked_pointer_upcast(r, &e1)->c == "c"); + CHECK_FALSE(unchecked_pointer_upcast(r, &e1)); + CHECK(unchecked_pointer_upcast(r, &e1) == &e1); CHECK(unchecked_pointer_upcast(r, &e1)->d1 == "d1"); + CHECK_FALSE(unchecked_pointer_upcast(r, &e1)); + CHECK(unchecked_pointer_upcast(r, &e1) == &e1); CHECK(unchecked_pointer_upcast(r, &e1)->e1 == "e1"); } } } diff --git a/develop/untests/meta_features/multiple_tests.cpp b/develop/untests/meta_features/multiple_tests.cpp index 25481f8..22dc29d 100644 --- a/develop/untests/meta_features/multiple_tests.cpp +++ b/develop/untests/meta_features/multiple_tests.cpp @@ -150,65 +150,65 @@ TEST_CASE("meta/meta_features/multiple") { CHECK(!F_type.is_derived_from(F_type)); } - SUBCASE("pointer_upcast") { - using meta::detail::pointer_upcast; + SUBCASE("unchecked_pointer_upcast") { + using meta::detail::unchecked_pointer_upcast; { A a; - CHECK(pointer_upcast(r, &a) == &a); - CHECK_FALSE(pointer_upcast(r, &a)); - CHECK_FALSE(pointer_upcast(r, &a)); - CHECK_FALSE(pointer_upcast(r, &a)); - CHECK_FALSE(pointer_upcast(r, &a)); - CHECK_FALSE(pointer_upcast(r, &a)); + CHECK(unchecked_pointer_upcast(r, &a) == &a); + CHECK_FALSE(unchecked_pointer_upcast(r, &a)); + CHECK_FALSE(unchecked_pointer_upcast(r, &a)); + CHECK_FALSE(unchecked_pointer_upcast(r, &a)); + CHECK_FALSE(unchecked_pointer_upcast(r, &a)); + CHECK_FALSE(unchecked_pointer_upcast(r, &a)); } { const B b; - CHECK(pointer_upcast(r, &b) == &b); - CHECK(pointer_upcast(r, &b) == &b); - CHECK_FALSE(pointer_upcast(r, &b)); - CHECK_FALSE(pointer_upcast(r, &b)); - CHECK_FALSE(pointer_upcast(r, &b)); - CHECK_FALSE(pointer_upcast(r, &b)); + CHECK(unchecked_pointer_upcast(r, &b) == &b); + CHECK(unchecked_pointer_upcast(r, &b) == &b); + CHECK_FALSE(unchecked_pointer_upcast(r, &b)); + CHECK_FALSE(unchecked_pointer_upcast(r, &b)); + CHECK_FALSE(unchecked_pointer_upcast(r, &b)); + CHECK_FALSE(unchecked_pointer_upcast(r, &b)); } { C c; - CHECK(pointer_upcast(r, &c) == &c); - CHECK_FALSE(pointer_upcast(r, &c)); - CHECK(pointer_upcast(r, &c) == &c); - CHECK_FALSE(pointer_upcast(r, &c)); - CHECK_FALSE(pointer_upcast(r, &c)); - CHECK_FALSE(pointer_upcast(r, &c)); + CHECK(unchecked_pointer_upcast(r, &c) == &c); + CHECK_FALSE(unchecked_pointer_upcast(r, &c)); + CHECK(unchecked_pointer_upcast(r, &c) == &c); + CHECK_FALSE(unchecked_pointer_upcast(r, &c)); + CHECK_FALSE(unchecked_pointer_upcast(r, &c)); + CHECK_FALSE(unchecked_pointer_upcast(r, &c)); } { const D d; - CHECK_FALSE(pointer_upcast(r,&d)); - CHECK(pointer_upcast(r, pointer_upcast(r,&d)) == static_cast(static_cast(&d))); - CHECK(pointer_upcast(r, pointer_upcast(r,&d)) == static_cast(static_cast(&d))); - CHECK(pointer_upcast(r, &d) == &d); - CHECK(pointer_upcast(r, &d) == &d); - CHECK(pointer_upcast(r, &d) == &d); - CHECK_FALSE(pointer_upcast(r, &d)); - CHECK_FALSE(pointer_upcast(r, &d)); + // CHECK_FALSE(unchecked_pointer_upcast(r,&d)); // ambiguous + CHECK(unchecked_pointer_upcast(r, unchecked_pointer_upcast(r,&d)) == static_cast(static_cast(&d))); + CHECK(unchecked_pointer_upcast(r, unchecked_pointer_upcast(r,&d)) == static_cast(static_cast(&d))); + CHECK(unchecked_pointer_upcast(r, &d) == &d); + CHECK(unchecked_pointer_upcast(r, &d) == &d); + CHECK(unchecked_pointer_upcast(r, &d) == &d); + CHECK_FALSE(unchecked_pointer_upcast(r, &d)); + CHECK_FALSE(unchecked_pointer_upcast(r, &d)); } { const E e; - CHECK_FALSE(pointer_upcast(r,&e)); - CHECK(pointer_upcast(r, pointer_upcast(r,&e)) == static_cast(static_cast(&e))); - CHECK(pointer_upcast(r, pointer_upcast(r,&e)) == static_cast(static_cast(&e))); - CHECK(pointer_upcast(r, &e) == &e); - CHECK(pointer_upcast(r, &e) == &e); - CHECK(pointer_upcast(r, &e) == &e); - CHECK(pointer_upcast(r, &e) == &e); - CHECK_FALSE(pointer_upcast(r, &e)); + // CHECK_FALSE(unchecked_pointer_upcast(r,&e)); // ambiguous + CHECK(unchecked_pointer_upcast(r, unchecked_pointer_upcast(r,&e)) == static_cast(static_cast(&e))); + CHECK(unchecked_pointer_upcast(r, unchecked_pointer_upcast(r,&e)) == static_cast(static_cast(&e))); + CHECK(unchecked_pointer_upcast(r, &e) == &e); + CHECK(unchecked_pointer_upcast(r, &e) == &e); + CHECK(unchecked_pointer_upcast(r, &e) == &e); + CHECK(unchecked_pointer_upcast(r, &e) == &e); + CHECK_FALSE(unchecked_pointer_upcast(r, &e)); } { F f; - CHECK_FALSE(pointer_upcast(r, &f)); - CHECK_FALSE(pointer_upcast(r, &f)); - CHECK_FALSE(pointer_upcast(r, &f)); - CHECK_FALSE(pointer_upcast(r, &f)); - CHECK_FALSE(pointer_upcast(r, &f)); - CHECK(pointer_upcast(r, &f) == &f); + CHECK_FALSE(unchecked_pointer_upcast(r, &f)); + CHECK_FALSE(unchecked_pointer_upcast(r, &f)); + CHECK_FALSE(unchecked_pointer_upcast(r, &f)); + CHECK_FALSE(unchecked_pointer_upcast(r, &f)); + CHECK_FALSE(unchecked_pointer_upcast(r, &f)); + CHECK(unchecked_pointer_upcast(r, &f) == &f); } } } diff --git a/develop/untests/meta_issues/random_issue_1.cpp b/develop/untests/meta_issues/random_issue_1.cpp index dc9e875..39c918e 100644 --- a/develop/untests/meta_issues/random_issue_1.cpp +++ b/develop/untests/meta_issues/random_issue_1.cpp @@ -9,30 +9,91 @@ namespace { - struct A {}; - struct B : A {}; - struct C : A {}; - struct D : B, C {}; + struct A1 {}; + struct B1 : A1 {}; + struct C1 : A1 {}; + struct D1 : B1, C1 {}; + + // A1 <- B1 <- * + // D1 + // A1 <- C1 <- * + + struct A2 {}; + struct B2 : virtual A2 {}; + struct C2 : virtual B2 {}; + struct D2 : virtual B2 {}; + struct E2 : C2, D2 {}; + + // A2 <= B2 <= C2 <- * + // E2 + // A2 <= B2 <= D2 <- * + + struct A3 {}; + struct B3 : virtual A3 {}; + struct C3 : A3 {}; + struct D3 : B3, C3 {}; + + // A3 <= B3 <- * + // D3 + // A3 <- C3 <- * - // A * <- B <- * - // D - // A * <- C <- * } TEST_CASE("meta/meta_issues/random/1") { namespace meta = meta_hpp; - meta::class_().base_(); - meta::class_().base_(); - meta::class_().base_(); + { + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); - CHECK(meta::is_invocable_with(+[](const D&){ return true; }, D{})); - CHECK(meta::is_invocable_with(+[](const C&){ return true; }, D{})); - CHECK(meta::is_invocable_with(+[](const B&){ return true; }, D{})); - CHECK_FALSE(meta::is_invocable_with(+[](const A&){ return true; }, D{})); + CHECK(meta::is_invocable_with(+[](const D1&){ return true; }, D1{})); + CHECK(meta::is_invocable_with(+[](const C1&){ return true; }, D1{})); + CHECK(meta::is_invocable_with(+[](const B1&){ return true; }, D1{})); + CHECK_FALSE(meta::is_invocable_with(+[](const A1&){ return true; }, D1{})); - CHECK(meta::try_invoke(+[](const D&){ return true; }, D{})); - CHECK(meta::try_invoke(+[](const C&){ return true; }, D{})); - CHECK(meta::try_invoke(+[](const B&){ return true; }, D{})); - CHECK_FALSE(meta::try_invoke(+[](const A&){ return true; }, D{})); + CHECK(meta::try_invoke(+[](const D1&){ return true; }, D1{})); + CHECK(meta::try_invoke(+[](const C1&){ return true; }, D1{})); + CHECK(meta::try_invoke(+[](const B1&){ return true; }, D1{})); + CHECK_FALSE(meta::try_invoke(+[](const A1&){ return true; }, D1{})); + + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + } + + { + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); + + CHECK(meta::is_invocable_with(+[](const A2&){ return true; }, E2{})); + CHECK(meta::is_invocable_with(+[](const B2&){ return true; }, E2{})); + CHECK(meta::is_invocable_with(+[](const C2&){ return true; }, E2{})); + CHECK(meta::is_invocable_with(+[](const D2&){ return true; }, E2{})); + + CHECK(meta::try_invoke(+[](const A2&){ return true; }, E2{})); + CHECK(meta::try_invoke(+[](const B2&){ return true; }, E2{})); + CHECK(meta::try_invoke(+[](const C2&){ return true; }, E2{})); + CHECK(meta::try_invoke(+[](const D2&){ return true; }, E2{})); + + CHECK(meta::resolve_type().is_virtual_base_of()); + } + + { + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + + CHECK(meta::is_invocable_with(+[](const A3&){ return true; }, A3{})); + CHECK(meta::is_invocable_with(+[](const A3&){ return true; }, B3{})); + CHECK(meta::is_invocable_with(+[](const A3&){ return true; }, C3{})); + CHECK_FALSE(meta::is_invocable_with(+[](const A3&){ return true; }, D3{})); + + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + } } diff --git a/headers/meta.hpp/meta_binds/class_bind.hpp b/headers/meta.hpp/meta_binds/class_bind.hpp index 1fd71b4..2f90528 100644 --- a/headers/meta.hpp/meta_binds/class_bind.hpp +++ b/headers/meta.hpp/meta_binds/class_bind.hpp @@ -23,13 +23,14 @@ namespace meta_hpp::detail::class_bind_impl using derived_classes_db_t = std::map>; template < class_kind Class, class_kind Base > - requires detail::class_bind_base_kind + requires std::is_base_of_v 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))); - }); + new_bases_db.try_emplace( // + resolve_type(), + std::in_place_type, + std::in_place_type); } inline void update_deep_upcasts_db( // diff --git a/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp b/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp index 72dbffa..4f7224f 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp @@ -258,21 +258,27 @@ namespace meta_hpp::detail if ( from_type.is_array() ) { const array_type& from_type_array = from_type.as_array(); - return static_cast(pointer_upcast( // + void* to_ptr = unchecked_pointer_upcast( // data_, from_type_array.get_data_type(), to_type_ptr.get_data_type() - )); + ); + META_HPP_ASSERT(to_ptr); + + return static_cast(to_ptr); } if ( from_type.is_pointer() ) { const pointer_type& from_type_ptr = from_type.as_pointer(); - return static_cast(pointer_upcast( // + void* to_ptr = unchecked_pointer_upcast( // *static_cast(data_), from_type_ptr.get_data_type(), to_type_ptr.get_data_type() - )); + ); + META_HPP_ASSERT(to_ptr); + + return static_cast(to_ptr); } throw_exception(error_code::bad_argument_cast); @@ -293,7 +299,8 @@ namespace meta_hpp::detail const any_type& from_type = get_raw_type(); const any_type& to_type = registry.resolve_type(); - void* to_ptr = pointer_upcast(data_, from_type, to_type); + void* to_ptr = unchecked_pointer_upcast(data_, from_type, to_type); + META_HPP_ASSERT(to_ptr); if constexpr ( std::is_lvalue_reference_v ) { return *static_cast(to_ptr); diff --git a/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp b/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp index c20e7d6..6c88ae3 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp @@ -192,10 +192,12 @@ namespace meta_hpp::detail const any_type& to_type = registry.resolve_type(); if ( from_type.is_class() && to_type.is_class() ) { - const class_type& from_class = from_type.as_class(); - const class_type& to_class = to_type.as_class(); - - void* to_ptr = pointer_upcast(data_, from_class, to_class); + void* to_ptr = unchecked_pointer_upcast( // + data_, + from_type.as_class(), + to_type.as_class() + ); + META_HPP_ASSERT(to_ptr); if constexpr ( !std::is_reference_v ) { return *static_cast(to_ptr); @@ -215,11 +217,12 @@ namespace meta_hpp::detail const any_type& from_data_type = from_type_ptr.get_data_type(); if ( from_data_type.is_class() && to_type.is_class() ) { - const class_type& from_data_class = from_data_type.as_class(); - const class_type& to_class = to_type.as_class(); - - void** from_data_ptr = static_cast(data_); - void* to_ptr = pointer_upcast(*from_data_ptr, from_data_class, to_class); + void* to_ptr = unchecked_pointer_upcast( // + *static_cast(data_), + from_data_type.as_class(), + to_type.as_class() + ); + META_HPP_ASSERT(to_ptr); if constexpr ( !std::is_reference_v ) { return *static_cast(to_ptr); diff --git a/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp b/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp index 01986ff..5277000 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp @@ -105,8 +105,19 @@ namespace meta_hpp::detail const class_type& base_class = base.as_class(); const class_type& derived_class = derived.as_class(); - if ( base_class && derived_class && base_class.is_base_of(derived_class) ) { - return true; + if ( base_class && derived_class ) { + const class_type_data& derived_data = *type_access(derived_class); + const class_type_data::deep_upcasts_t& deep_upcasts = derived_data.deep_upcasts; + + if ( const auto iter{deep_upcasts.lower_bound(base)}; iter != deep_upcasts.end() && iter->first == base ) { + if ( const auto next_iter{std::next(iter)}; next_iter == deep_upcasts.end() || next_iter->first != base ) { + return true; + } + + if ( base_class.is_virtual_base_of(derived_class) ) { + return true; + } + } } return false; @@ -115,7 +126,7 @@ namespace meta_hpp::detail namespace meta_hpp::detail { - [[nodiscard]] inline void* pointer_upcast(void* ptr, const class_type& from, const class_type& to) { + [[nodiscard]] inline void* unchecked_pointer_upcast(void* ptr, const class_type& from, const class_type& to) { if ( nullptr == ptr || !from || !to ) { return nullptr; } @@ -124,32 +135,24 @@ namespace meta_hpp::detail return ptr; } - void* base_ptr = nullptr; + const class_type_data& from_data = *type_access(from); - 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 ) { - if ( void* new_base_ptr{iter->second.apply(ptr)}; base_ptr == nullptr ) { - base_ptr = new_base_ptr; - } else if ( base_ptr != new_base_ptr ) { - // ambiguous conversions - return nullptr; - } + if ( auto iter{from_data.deep_upcasts.find(to)}; iter != from_data.deep_upcasts.end() ) { + return iter->second.apply(ptr); } - return base_ptr; + return nullptr; } - [[nodiscard]] inline const void* pointer_upcast(const void* ptr, const class_type& from, const class_type& to) { + [[nodiscard]] inline const void* unchecked_pointer_upcast(const void* ptr, const class_type& from, const class_type& to) { // NOLINTNEXTLINE(*-const-cast) - return pointer_upcast(const_cast(ptr), from, to); + return unchecked_pointer_upcast(const_cast(ptr), from, to); } } namespace meta_hpp::detail { - [[nodiscard]] inline void* pointer_upcast(void* ptr, const any_type& from, const any_type& to) { + [[nodiscard]] inline void* unchecked_pointer_upcast(void* ptr, const any_type& from, const any_type& to) { if ( nullptr == ptr || !from || !to ) { return nullptr; } @@ -162,7 +165,7 @@ namespace meta_hpp::detail const class_type& from_class = from.as_class(); if ( to_class && from_class ) { - if ( void* base_ptr = pointer_upcast(ptr, from_class, to_class) ) { + if ( void* base_ptr = unchecked_pointer_upcast(ptr, from_class, to_class) ) { return base_ptr; } } @@ -170,17 +173,17 @@ namespace meta_hpp::detail return nullptr; } - [[nodiscard]] inline const void* pointer_upcast(const void* ptr, const any_type& from, const any_type& to) { + [[nodiscard]] inline const void* unchecked_pointer_upcast(const void* ptr, const any_type& from, const any_type& to) { // NOLINTNEXTLINE(*-const-cast) - return pointer_upcast(const_cast(ptr), from, to); + return unchecked_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( // + [[nodiscard]] To* unchecked_pointer_upcast(type_registry& registry, From* ptr) { + return static_cast(unchecked_pointer_upcast( // ptr, registry.resolve_type(), registry.resolve_type() @@ -188,8 +191,8 @@ namespace meta_hpp::detail } template < typename To, typename From > - [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { - return static_cast(pointer_upcast( // + [[nodiscard]] const To* unchecked_pointer_upcast(type_registry& registry, const From* ptr) { + return static_cast(unchecked_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 26661ac..c8aa04a 100644 --- a/headers/meta.hpp/meta_types.hpp +++ b/headers/meta.hpp/meta_types.hpp @@ -184,6 +184,10 @@ namespace meta_hpp [[nodiscard]] bool is_direct_base_of() const noexcept; [[nodiscard]] bool is_direct_base_of(const class_type& derived) const noexcept; + template < detail::class_kind Derived > + [[nodiscard]] bool is_virtual_base_of() const noexcept; + [[nodiscard]] bool is_virtual_base_of(const class_type& derived) const noexcept; + template < detail::class_kind Base > [[nodiscard]] bool is_derived_from() const noexcept; [[nodiscard]] bool is_derived_from(const class_type& base) const noexcept; @@ -192,6 +196,10 @@ namespace meta_hpp [[nodiscard]] bool is_direct_derived_from() const noexcept; [[nodiscard]] bool is_direct_derived_from(const class_type& base) const noexcept; + template < detail::class_kind Base > + [[nodiscard]] bool is_virtual_derived_from() const noexcept; + [[nodiscard]] bool is_virtual_derived_from(const class_type& base) const noexcept; + [[nodiscard]] function get_function(std::string_view name) const noexcept; [[nodiscard]] member get_member(std::string_view name) const noexcept; [[nodiscard]] method get_method(std::string_view name) const noexcept; @@ -410,17 +418,28 @@ namespace meta_hpp::detail typedef_map typedefs; variable_set variables; - using upcast_func_t = void* (*)(void*); + struct upcast_func_t final { + void* (*upcast)(void*){}; + bool is_virtual_upcast{}; + class_type target_class{}; + + template < typename Derived, typename Base > + requires std::is_base_of_v + upcast_func_t(std::in_place_type_t, std::in_place_type_t); + + [[nodiscard]] void* apply(void* ptr) const noexcept; + [[nodiscard]] const void* apply(const void* ptr) const noexcept; + }; struct upcast_func_list_t final { + class_set vbases; std::vector upcasts; - upcast_func_list_t() = default; - upcast_func_list_t(upcast_func_t _upcast); - upcast_func_list_t(std::vector _upcasts); + upcast_func_list_t(const upcast_func_t& _upcast); + upcast_func_list_t(class_set _vbases, std::vector _upcasts); - void* apply(void* ptr) const noexcept; - const void* apply(const void* ptr) const noexcept; + [[nodiscard]] void* apply(void* ptr) const noexcept; + [[nodiscard]] const void* apply(const void* ptr) const noexcept; friend upcast_func_list_t operator+(const upcast_func_list_t& l, const upcast_func_list_t& r); }; diff --git a/headers/meta.hpp/meta_types/class_type.hpp b/headers/meta.hpp/meta_types/class_type.hpp index bdff667..9984ca0 100644 --- a/headers/meta.hpp/meta_types/class_type.hpp +++ b/headers/meta.hpp/meta_types/class_type.hpp @@ -30,16 +30,45 @@ namespace meta_hpp::detail , size{class_traits::size} , align{class_traits::align} , argument_types{resolve_types(typename class_traits::argument_types{})} {} +} - inline class_type_data::upcast_func_list_t::upcast_func_list_t(upcast_func_t _upcast) - : upcasts{_upcast} {} +namespace meta_hpp::detail +{ + template < typename Derived, typename Base > + requires std::is_base_of_v + inline class_type_data::upcast_func_t::upcast_func_t(std::in_place_type_t, std::in_place_type_t) + : upcast{[](void* from) -> void* { return static_cast(static_cast(from)); }} + , is_virtual_upcast{is_virtual_base_of_v} + , target_class{resolve_type()} {} - inline class_type_data::upcast_func_list_t::upcast_func_list_t(std::vector _upcasts) - : upcasts{std::move(_upcasts)} {} + inline void* class_type_data::upcast_func_t::apply(void* ptr) const noexcept { + return upcast(ptr); + } + + inline const void* class_type_data::upcast_func_t::apply(const void* ptr) const noexcept { + // NOLINTNEXTLINE(*-const-cast) + return apply(const_cast(ptr)); + } +} + +namespace meta_hpp::detail +{ + inline class_type_data::upcast_func_list_t::upcast_func_list_t(const upcast_func_t& _upcast) + : upcasts{_upcast} { + for ( const upcast_func_t& upcast : upcasts ) { + if ( upcast.is_virtual_upcast ) { + vbases.emplace(upcast.target_class); + } + } + } + + inline class_type_data::upcast_func_list_t::upcast_func_list_t(class_set _vbases, std::vector _upcasts) + : vbases{std::move(_vbases)} + , upcasts{std::move(_upcasts)} {} inline void* class_type_data::upcast_func_list_t::apply(void* ptr) const noexcept { - for ( upcast_func_t upcast : upcasts ) { - ptr = upcast(ptr); + for ( const upcast_func_t& upcast : upcasts ) { + ptr = upcast.apply(ptr); } return ptr; } @@ -53,11 +82,16 @@ namespace meta_hpp::detail const class_type_data::upcast_func_list_t& l, const class_type_data::upcast_func_list_t& r ) { + class_set new_vbases; + new_vbases.insert(l.vbases.begin(), l.vbases.end()); + new_vbases.insert(r.vbases.begin(), r.vbases.end()); + std::vector new_upcasts; new_upcasts.reserve(l.upcasts.size() + r.upcasts.size()); new_upcasts.insert(new_upcasts.end(), l.upcasts.begin(), l.upcasts.end()); new_upcasts.insert(new_upcasts.end(), r.upcasts.begin(), r.upcasts.end()); - return class_type_data::upcast_func_list_t{std::move(new_upcasts)}; + + return class_type_data::upcast_func_list_t{std::move(new_vbases), std::move(new_upcasts)}; } } @@ -180,13 +214,38 @@ namespace meta_hpp return is_valid() && derived.is_valid() && derived.data_->base_upcasts.contains(*this); } + template < detail::class_kind Derived > + bool class_type::is_virtual_base_of() const noexcept { + return is_virtual_base_of(resolve_type()); + } + + inline bool class_type::is_virtual_base_of(const class_type& derived) const noexcept { + if ( !is_valid() || !derived.is_valid() ) { + return false; + } + + const detail::class_type_data& derived_data = *derived.data_; + const auto upcasts_range = derived_data.deep_upcasts.equal_range(*this); + + if ( upcasts_range.first == upcasts_range.second ) { + return false; + } + + const class_set& first_vbases = upcasts_range.first->second.vbases; + return std::any_of(first_vbases.begin(), first_vbases.end(), [&upcasts_range](const class_type& vbase) { + return std::all_of(std::next(upcasts_range.first), upcasts_range.second, [&vbase](auto&& upcasts_p) { + return upcasts_p.second.vbases.contains(vbase); + }); + }); + } + template < detail::class_kind Base > bool class_type::is_derived_from() const noexcept { return is_derived_from(resolve_type()); } inline bool class_type::is_derived_from(const class_type& base) const noexcept { - return is_valid() && base.is_valid() && data_->deep_upcasts.contains(base); + return base.is_base_of(*this); } template < detail::class_kind Base > @@ -195,7 +254,16 @@ namespace meta_hpp } inline bool class_type::is_direct_derived_from(const class_type& base) const noexcept { - return is_valid() && base.is_valid() && data_->base_upcasts.contains(base); + return base.is_direct_base_of(*this); + } + + template < detail::class_kind Base > + bool class_type::is_virtual_derived_from() const noexcept { + return is_virtual_derived_from(resolve_type()); + } + + inline bool class_type::is_virtual_derived_from(const class_type& base) const noexcept { + return base.is_virtual_base_of(*this); } inline function class_type::get_function(std::string_view name) const noexcept {