diff --git a/develop/cmake/SetupTargets.cmake b/develop/cmake/SetupTargets.cmake index 5348cdb..87b5573 100644 --- a/develop/cmake/SetupTargets.cmake +++ b/develop/cmake/SetupTargets.cmake @@ -5,7 +5,8 @@ target_compile_options(${PROJECT_NAME}.setup_targets INTERFACE $<$: /WX /W4 /bigobj> $<$: - -Werror -Wall -Wextra -Wpedantic> + -Werror -Wall -Wextra -Wpedantic + -Wno-inaccessible-base> $<$,$>: -Werror -Weverything -Wconversion -Wno-c++98-compat @@ -30,3 +31,6 @@ target_link_libraries(${PROJECT_NAME}.setup_targets INTERFACE meta.hpp::disable_exceptions> $<$: meta.hpp::disable_rtti>) + +target_compile_definitions(${PROJECT_NAME}.setup_targets INTERFACE + $<$:META_HPP_SANITIZERS>) diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index 882d773..9ccd8e4 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -52,6 +52,12 @@ # define META_HPP_ASSERT(...) assert(__VA_ARGS__) // NOLINT #endif +#if defined(META_HPP_SANITIZERS) +# define META_HPP_DEV_ASSERT(...) META_HPP_ASSERT(__VA_ARGS__) +#else +# define META_HPP_DEV_ASSERT(...) (void)0 +#endif + #if !defined(META_HPP_PP_CAT) # define META_HPP_PP_CAT(x, y) META_HPP_PP_CAT_I(x, y) # define META_HPP_PP_CAT_I(x, y) x##y @@ -302,42 +308,42 @@ namespace meta_hpp::detail using copy_cvref_t = typename copy_cvref::type; } -namespace meta_hpp::detail::impl -{ - template < typename F, typename... Args > - class defer_impl { - public: - defer_impl() = delete; - defer_impl(defer_impl&&) = delete; - defer_impl(const defer_impl&) = delete; - defer_impl& operator=(defer_impl&&) = delete; - defer_impl& operator=(const defer_impl&) = delete; - - template < typename UF > - explicit defer_impl(UF&& f, std::tuple&& args) - : f_{std::forward(f)} - , args_{std::move(args)} {} - - void dismiss() noexcept { - dismissed_ = true; - } - - protected: - ~defer_impl() noexcept { - if ( !dismissed_ ) { - std::apply(std::move(f_), std::move(args_)); - } - } - - private: - F f_; - std::tuple args_; - bool dismissed_{}; - }; -} - namespace meta_hpp::detail { + namespace impl + { + template < typename F, typename... Args > + class defer_impl { + public: + defer_impl() = delete; + defer_impl(defer_impl&&) = delete; + defer_impl(const defer_impl&) = delete; + defer_impl& operator=(defer_impl&&) = delete; + defer_impl& operator=(const defer_impl&) = delete; + + template < typename UF > + explicit defer_impl(UF&& f, std::tuple&& args) + : f_{std::forward(f)} + , args_{std::move(args)} {} + + void dismiss() noexcept { + dismissed_ = true; + } + + protected: + ~defer_impl() noexcept { + if ( !dismissed_ ) { + std::apply(std::move(f_), std::move(args_)); + } + } + + private: + F f_; + std::tuple args_; + bool dismissed_{}; + }; + } + template < typename F, typename... Args > class defer final : public impl::defer_impl { public: @@ -634,15 +640,15 @@ namespace meta_hpp::detail static vtable_t table{ .call{[](const fixed_function& self, Args... args) -> R { - META_HPP_ASSERT(self); + META_HPP_DEV_ASSERT(self); const Fp& src = *buffer_cast(self.buffer_); return std::invoke(src, std::forward(args)...); }}, .move{[](fixed_function& from, fixed_function& to) noexcept { - META_HPP_ASSERT(!to); - META_HPP_ASSERT(from); + META_HPP_DEV_ASSERT(!to); + META_HPP_DEV_ASSERT(from); Fp& src = *buffer_cast(from.buffer_); std::construct_at(buffer_cast(to.buffer_), std::move(src)); @@ -653,7 +659,7 @@ namespace meta_hpp::detail }}, .destroy{[](fixed_function& self) { - META_HPP_ASSERT(self); + META_HPP_DEV_ASSERT(self); Fp& src = *buffer_cast(self.buffer_); std::destroy_at(&src); @@ -666,7 +672,7 @@ namespace meta_hpp::detail template < typename F, typename Fp = std::decay_t > static void construct(fixed_function& dst, F&& fun) { - META_HPP_ASSERT(!dst); + META_HPP_DEV_ASSERT(!dst); static_assert(sizeof(Fp) <= sizeof(buffer_t)); static_assert(alignof(buffer_t) % alignof(Fp) == 0); @@ -1162,6 +1168,32 @@ namespace std }; } +namespace meta_hpp::detail +{ + template < typename SortedContainerL, typename SortedContainerR, typename Compare > + bool is_disjoint(const SortedContainerL& l, const SortedContainerR& r, Compare compare) { + using std::begin; + using std::end; + + for ( auto iter_l{begin(l)}, iter_r{begin(r)}; iter_l != end(l) && iter_r != end(r); ) { + if ( compare(*iter_l, *iter_r) ) { + ++iter_l; + } else if ( compare(*iter_r, *iter_l) ) { + ++iter_r; + } else { + return false; + } + } + + return true; + } + + template < typename SortedContainerL, typename SortedContainerR > + bool is_disjoint(const SortedContainerL& l, const SortedContainerR& r) { + return is_disjoint(l, r, std::less<>{}); + } +} + namespace meta_hpp::detail { template < typename T > @@ -1174,6 +1206,38 @@ namespace meta_hpp::detail inline constexpr bool is_in_place_type_v = is_in_place_type::value; } +namespace meta_hpp::detail +{ + namespace impl + { + template < typename From, typename To > + constexpr bool is_virtual_base_of_impl(...) noexcept { + return false; + } + + template < + typename From, + typename To, + decltype(static_cast(std::declval())) = nullptr > + constexpr bool is_virtual_base_of_impl(int) noexcept { + return true; + } + } + + // clang-format off + + template < typename Base, typename Derived > + struct is_virtual_base_of : std::integral_constant && + impl::is_virtual_base_of_impl(0) && + !impl::is_virtual_base_of_impl(0)> {}; + + // clang-format on + + template < typename Base, typename Derived > + inline constexpr bool is_virtual_base_of_v = is_virtual_base_of::value; +} + namespace meta_hpp::detail { class memory_buffer final { @@ -1813,7 +1877,7 @@ namespace meta_hpp::detail namespace impl { template < class_kind Class > - struct class_traits_base { + struct class_traits_impl { static constexpr std::size_t arity{0}; using argument_types = type_list<>; @@ -1824,7 +1888,7 @@ namespace meta_hpp::detail }; template < template < typename... > typename Class, typename... Args > - struct class_traits_base> { + struct class_traits_impl> { static constexpr std::size_t arity{sizeof...(Args)}; using argument_types = type_list; @@ -1836,7 +1900,7 @@ namespace meta_hpp::detail } template < class_kind Class > - struct class_traits : impl::class_traits_base { + struct class_traits : impl::class_traits_impl { static constexpr std::size_t size{sizeof(Class)}; static constexpr std::size_t align{alignof(Class)}; @@ -1859,7 +1923,7 @@ namespace meta_hpp::detail flags.set(class_flags::is_polymorphic); } - return flags | impl::class_traits_base::make_flags(); + return flags | impl::class_traits_impl::make_flags(); } }; } @@ -2532,10 +2596,26 @@ namespace meta_hpp [[nodiscard]] bool is_base_of() const noexcept; [[nodiscard]] bool is_base_of(const class_type& derived) const noexcept; + template < detail::class_kind Derived > + [[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; + template < detail::class_kind Base > + [[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; @@ -2754,11 +2834,39 @@ namespace meta_hpp::detail typedef_map typedefs; variable_set variables; - using upcast_func_t = void* (*)(void*); - using upcast_func_list_t = std::vector; + struct upcast_func_t final { + using upcast_t = void* (*)(void*); + + upcast_t upcast{}; + class_type target{}; + bool is_virtual{}; + + 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 { + using upcasts_t = std::vector; + + upcasts_t upcasts{}; + class_set vbases{}; + bool is_ambiguous{}; + + upcast_func_list_t(const upcast_func_t& _upcast); + upcast_func_list_t(upcasts_t _upcasts, class_set _vbases); + + [[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); + }; using base_upcasts_t = std::map>; - using deep_upcasts_t = std::multimap>; + using deep_upcasts_t = std::map>; base_upcasts_t base_upcasts; deep_upcasts_t deep_upcasts; @@ -4803,19 +4911,20 @@ 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( // const class_type& derived_class, const class_type& new_base_class, - const upcast_func_list_t& derived_to_new_base, + 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); @@ -4823,26 +4932,27 @@ namespace meta_hpp::detail::class_bind_impl 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); + + const auto add_derived_deep_upcast = [&derived_deep_upcasts](const class_type& deep_class, upcast_func_list_t&& upcasts) { + auto&& [position, emplaced] = derived_deep_upcasts.try_emplace(deep_class, std::move(upcasts)); + if ( !emplaced ) { + position->second.is_ambiguous = is_disjoint(position->second.vbases, upcasts.vbases); + } + }; 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)); + upcast_func_list_t derived_to_new_deep = derived_to_new_base + new_base_to_deep; + add_derived_deep_upcast(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); + upcast_func_t subderived_to_derived = subderived_data.base_upcasts.at(derived_class); + upcast_func_list_t subderived_to_new_base = subderived_to_derived + derived_to_new_base; + update_deep_upcasts_db(subderived_class, new_base_class, std::move(subderived_to_new_base), deep_upcasts_db); } + + add_derived_deep_upcast(new_base_class, std::move(derived_to_new_base)); } inline void updata_derived_classes_db( // @@ -4896,7 +5006,7 @@ namespace meta_hpp continue; } - update_deep_upcasts_db(*this, new_base_class, {self_to_new_base}, deep_upcasts_db); + 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); @@ -5670,8 +5780,13 @@ 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 ( auto iter{deep_upcasts.find(base)}; iter != deep_upcasts.end() && !iter->second.is_ambiguous ) { + return true; + } } return false; @@ -5689,28 +5804,14 @@ namespace meta_hpp::detail return ptr; } - void* base_ptr = nullptr; + const class_type_data& from_data = *type_access(from); + const class_type_data::deep_upcasts_t& deep_upcasts = from_data.deep_upcasts; - 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; - } + if ( auto iter{deep_upcasts.find(to)}; iter != deep_upcasts.end() && !iter->second.is_ambiguous ) { + 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) { @@ -6000,7 +6101,7 @@ namespace meta_hpp::detail { template < pointer_kind To > [[nodiscard]] decltype(auto) uarg::cast(type_registry& registry) const { - META_HPP_ASSERT(can_cast_to(registry) && "bad argument cast"); + META_HPP_DEV_ASSERT(can_cast_to(registry) && "bad argument cast"); using to_raw_type = std::remove_cv_t; @@ -6014,21 +6115,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 = 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 = 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); @@ -6036,7 +6143,7 @@ namespace meta_hpp::detail template < non_pointer_kind To > [[nodiscard]] decltype(auto) uarg::cast(type_registry& registry) const { - META_HPP_ASSERT(can_cast_to(registry) && "bad argument cast"); + META_HPP_DEV_ASSERT(can_cast_to(registry) && "bad argument cast"); using to_raw_type_cv = std::remove_reference_t; using to_raw_type = std::remove_cv_t; @@ -6050,6 +6157,7 @@ namespace meta_hpp::detail const any_type& to_type = registry.resolve_type(); void* to_ptr = pointer_upcast(data_, from_type, to_type); + META_HPP_ASSERT(to_ptr); if constexpr ( std::is_lvalue_reference_v ) { return *static_cast(to_ptr); @@ -6089,18 +6197,11 @@ namespace meta_hpp::detail namespace meta_hpp::detail { - template < typename ArgTypeList, typename F > - auto call_with_uargs(type_registry& registry, std::span args, F&& f) { - META_HPP_ASSERT(args.size() == type_list_arity_v); - return [ args, ®istry, &f ](std::index_sequence) { - return f(args[Is].cast>(registry)...); - } - (std::make_index_sequence>()); - } - template < typename ArgTypeList > bool can_cast_all_uargs(type_registry& registry, std::span args) { - META_HPP_ASSERT(args.size() == type_list_arity_v); + if ( args.size() != type_list_arity_v ) { + return false; + } return [ args, ®istry ](std::index_sequence) { return (... && args[Is].can_cast_to>(registry)); } @@ -6109,12 +6210,23 @@ namespace meta_hpp::detail template < typename ArgTypeList > bool can_cast_all_uargs(type_registry& registry, std::span args) { - META_HPP_ASSERT(args.size() == type_list_arity_v); + if ( args.size() != type_list_arity_v ) { + return false; + } return [ args, ®istry ](std::index_sequence) { return (... && args[Is].can_cast_to>(registry)); } (std::make_index_sequence>()); } + + template < typename ArgTypeList, typename F > + auto unchecked_call_with_uargs(type_registry& registry, std::span args, F&& f) { + META_HPP_DEV_ASSERT(args.size() == type_list_arity_v); + return [ args, ®istry, &f ](std::index_sequence) { + return f(args[Is].cast>(registry)...); + } + (std::make_index_sequence>()); + } } namespace meta_hpp::detail @@ -6185,7 +6297,7 @@ namespace meta_hpp::detail && "an attempt to call a function with incorrect argument types" ); - return call_with_uargs(registry, args, [function_ptr](auto&&... all_args) { + return unchecked_call_with_uargs(registry, args, [function_ptr](auto&&... all_args) { if constexpr ( std::is_void_v ) { function_ptr(META_HPP_FWD(all_args)...); return uvalue{}; @@ -6503,7 +6615,7 @@ namespace meta_hpp::detail { template < inst_class_ref_kind Q > decltype(auto) uinst::cast(type_registry& registry) const { - META_HPP_ASSERT(can_cast_to(registry) && "bad instance cast"); + META_HPP_DEV_ASSERT(can_cast_to(registry) && "bad instance cast"); using inst_class_cv = std::remove_reference_t; using inst_class = std::remove_cv_t; @@ -6512,10 +6624,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 = 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); @@ -6535,11 +6649,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 = 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); @@ -6961,7 +7076,7 @@ namespace meta_hpp::detail && "an attempt to call a method with incorrect argument types" ); - return call_with_uargs(registry, args, [method_ptr, &inst, ®istry](auto&&... all_args) { + return unchecked_call_with_uargs(registry, args, [method_ptr, &inst, ®istry](auto&&... all_args) { if constexpr ( std::is_void_v ) { (inst.cast(registry).*method_ptr)(META_HPP_FWD(all_args)...); return uvalue{}; @@ -7126,6 +7241,11 @@ namespace meta_hpp return function.invoke(std::forward(args)...); } + template < typename... Args > + uresult try_invoke(const function& function, Args&&... args) { + return function.try_invoke(std::forward(args)...); + } + template < detail::function_pointer_kind Function, typename... Args > uvalue invoke(Function function_ptr, Args&&... args) { using namespace detail; @@ -7133,6 +7253,22 @@ namespace meta_hpp const std::array vargs{uarg{registry, std::forward(args)}...}; return raw_function_invoke(registry, function_ptr, vargs); } + + template < detail::function_pointer_kind Function, typename... Args > + uresult try_invoke(Function function_ptr, Args&&... args) { + using namespace detail; + type_registry& registry{type_registry::instance()}; + + { + const std::array vargs{uarg_base{registry, std::forward(args)}...}; + if ( const uerror err = raw_function_invoke_error(registry, vargs) ) { + return err; + } + } + + const std::array vargs{uarg{registry, std::forward(args)}...}; + return raw_function_invoke(registry, function_ptr, vargs); + } } namespace meta_hpp @@ -7142,6 +7278,11 @@ namespace meta_hpp return member.get(std::forward(instance)); } + template < typename Instance > + uresult try_invoke(const member& member, Instance&& instance) { + return member.try_get(std::forward(instance)); + } + template < detail::member_pointer_kind Member, typename Instance > uvalue invoke(Member member_ptr, Instance&& instance) { using namespace detail; @@ -7149,6 +7290,22 @@ namespace meta_hpp const uinst vinst{registry, std::forward(instance)}; return raw_member_getter(registry, member_ptr, vinst); } + + template < detail::member_pointer_kind Member, typename Instance > + uresult try_invoke(Member member_ptr, Instance&& instance) { + using namespace detail; + type_registry& registry{type_registry::instance()}; + + { + const uinst_base vinst{registry, std::forward(instance)}; + if ( const uerror err = raw_member_getter_error(registry, vinst) ) { + return err; + } + } + + const uinst vinst{registry, std::forward(instance)}; + return raw_member_getter(registry, member_ptr, vinst); + } } namespace meta_hpp @@ -7158,6 +7315,11 @@ namespace meta_hpp return method.invoke(std::forward(instance), std::forward(args)...); } + template < typename Instance, typename... Args > + uresult try_invoke(const method& method, Instance&& instance, Args&&... args) { + return method.try_invoke(std::forward(instance), std::forward(args)...); + } + template < detail::method_pointer_kind Method, typename Instance, typename... Args > uvalue invoke(Method method_ptr, Instance&& instance, Args&&... args) { using namespace detail; @@ -7166,6 +7328,24 @@ namespace meta_hpp const std::array vargs{uarg{registry, std::forward(args)}...}; return raw_method_invoke(registry, method_ptr, vinst, vargs); } + + template < detail::method_pointer_kind Method, typename Instance, typename... Args > + uresult try_invoke(Method method_ptr, Instance&& instance, Args&&... args) { + using namespace detail; + type_registry& registry{type_registry::instance()}; + + { + const uinst_base vinst{registry, std::forward(instance)}; + const std::array vargs{uarg_base{registry, std::forward(args)}...}; + if ( const uerror err = raw_method_invoke_error(registry, vinst, vargs) ) { + return err; + } + } + + const uinst vinst{registry, std::forward(instance)}; + const std::array vargs{uarg{registry, std::forward(args)}...}; + return raw_method_invoke(registry, method_ptr, vinst, vargs); + } } namespace meta_hpp @@ -7352,7 +7532,7 @@ namespace meta_hpp::detail && "an attempt to call a constructor with incorrect argument types" ); - return call_with_uargs(registry, args, [](auto&&... all_args) -> uvalue { + return unchecked_call_with_uargs(registry, args, [](auto&&... all_args) -> uvalue { if constexpr ( as_object ) { return make_uvalue(META_HPP_FWD(all_args)...); } @@ -7383,7 +7563,7 @@ namespace meta_hpp::detail && "an attempt to call a constructor with incorrect argument types" ); - return call_with_uargs(registry, args, [mem](auto&&... all_args) { + return unchecked_call_with_uargs(registry, args, [mem](auto&&... all_args) { return std::construct_at(static_cast(mem), META_HPP_FWD(all_args)...); }); } @@ -8009,6 +8189,69 @@ namespace meta_hpp::detail , argument_types{resolve_types(typename class_traits::argument_types{})} {} } +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)); }} + , target{resolve_type()} + , is_virtual{is_virtual_base_of_v} {} + + 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 ) { + vbases.emplace(upcast.target); + } + } + } + + inline class_type_data::upcast_func_list_t::upcast_func_list_t(upcasts_t _upcasts, class_set _vbases) + : upcasts{std::move(_upcasts)} + , vbases{std::move(_vbases)} {} + + inline void* class_type_data::upcast_func_list_t::apply(void* ptr) const noexcept { + for ( const upcast_func_t& upcast : upcasts ) { + ptr = upcast.apply(ptr); + } + return ptr; + } + + inline const void* class_type_data::upcast_func_list_t::apply(const void* ptr) const noexcept { + // NOLINTNEXTLINE(*-const-cast) + return apply(const_cast(ptr)); + } + + inline class_type_data::upcast_func_list_t operator+( // + const class_type_data::upcast_func_list_t& l, + const class_type_data::upcast_func_list_t& r + ) { + class_type_data::upcast_func_list_t::upcasts_t 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()); + + class_set new_vbases; + new_vbases.insert(l.vbases.begin(), l.vbases.end()); + new_vbases.insert(r.vbases.begin(), r.vbases.end()); + + return class_type_data::upcast_func_list_t{std::move(new_upcasts), std::move(new_vbases)}; + } +} + namespace meta_hpp { inline class_bitflags class_type::get_flags() const noexcept { @@ -8116,12 +8359,33 @@ namespace meta_hpp } inline bool class_type::is_base_of(const class_type& derived) const noexcept { + return is_valid() && derived.is_valid() && derived.data_->deep_upcasts.contains(*this); + } + + template < detail::class_kind Derived > + bool class_type::is_direct_base_of() const noexcept { + return is_direct_base_of(resolve_type()); + } + + inline bool class_type::is_direct_base_of(const class_type& derived) const noexcept { + 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; } - if ( derived.data_->deep_upcasts.contains(*this) ) { - return true; + using deep_upcasts_t = detail::class_type_data::deep_upcasts_t; + const deep_upcasts_t& deep_upcasts = derived.data_->deep_upcasts; + + if ( auto iter{deep_upcasts.find(*this)}; iter != deep_upcasts.end() ) { + return !iter->second.is_ambiguous && !iter->second.vbases.empty(); } return false; @@ -8133,15 +8397,25 @@ namespace meta_hpp } inline bool class_type::is_derived_from(const class_type& base) const noexcept { - if ( !is_valid() || !base.is_valid() ) { - return false; - } + return base.is_base_of(*this); + } - if ( data_->deep_upcasts.contains(base) ) { - return true; - } + template < detail::class_kind Base > + bool class_type::is_direct_derived_from() const noexcept { + return is_direct_derived_from(resolve_type()); + } - return false; + inline bool class_type::is_direct_derived_from(const class_type& base) const noexcept { + 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 { @@ -8870,7 +9144,7 @@ namespace meta_hpp template < typename T, typename... Args, typename Tp = std::decay_t > static Tp& do_ctor(uvalue& dst, Args&&... args) { - META_HPP_ASSERT(!dst); + META_HPP_DEV_ASSERT(!dst); if constexpr ( in_internal_v ) { std::construct_at(storage_cast(dst.storage_), std::forward(args)...); @@ -8888,7 +9162,7 @@ namespace meta_hpp } static void do_move(uvalue&& self, uvalue& to) noexcept { - META_HPP_ASSERT(!to); + META_HPP_DEV_ASSERT(!to); auto&& [tag, vtable] = unpack_vtag(self); @@ -8907,7 +9181,7 @@ namespace meta_hpp } static void do_copy(const uvalue& self, uvalue& to) noexcept { - META_HPP_ASSERT(!to); + META_HPP_DEV_ASSERT(!to); auto&& [tag, vtable] = unpack_vtag(self); @@ -8969,8 +9243,8 @@ namespace meta_hpp .type = resolve_type(), .move{[](uvalue&& self, uvalue& to) noexcept { - META_HPP_ASSERT(!to); - META_HPP_ASSERT(self); + META_HPP_DEV_ASSERT(!to); + META_HPP_DEV_ASSERT(self); Tp* src = storage_cast(self.storage_); @@ -8985,8 +9259,8 @@ namespace meta_hpp }}, .copy{[](const uvalue& self, uvalue& to) { - META_HPP_ASSERT(!to); - META_HPP_ASSERT(self); + META_HPP_DEV_ASSERT(!to); + META_HPP_DEV_ASSERT(self); const Tp* src = storage_cast(self.storage_); @@ -9000,7 +9274,7 @@ namespace meta_hpp }}, .reset{[](uvalue& self) noexcept { - META_HPP_ASSERT(self); + META_HPP_DEV_ASSERT(self); Tp* src = storage_cast(self.storage_); diff --git a/develop/untests/meta_base/is_disjoint.cpp b/develop/untests/meta_base/is_disjoint.cpp new file mode 100644 index 0000000..c4ea291 --- /dev/null +++ b/develop/untests/meta_base/is_disjoint.cpp @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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 + +TEST_CASE("meta/meta_base/is_disjoint") { + namespace meta = meta_hpp; + using meta::detail::is_disjoint; + + CHECK(is_disjoint(std::set{}, std::set{})); + + CHECK(is_disjoint(std::set{1}, std::set{})); + CHECK(is_disjoint(std::set{}, std::set{1})); + + CHECK_FALSE(is_disjoint(std::set{1}, std::set{1})); + + CHECK_FALSE(is_disjoint(std::set{1,2}, std::set{1})); + CHECK_FALSE(is_disjoint(std::set{1}, std::set{1,2})); + + CHECK_FALSE(is_disjoint(std::set{1,2}, std::set{1,3})); + CHECK_FALSE(is_disjoint(std::set{1,3}, std::set{1,2})); + + CHECK(is_disjoint(std::set{1,2}, std::set{3,4})); + CHECK(is_disjoint(std::set{2,3}, std::set{1,4,5})); + CHECK(is_disjoint(std::set{2,4}, std::set{1,3,5})); +} diff --git a/develop/untests/meta_base/is_virtual_base_of_tests.cpp b/develop/untests/meta_base/is_virtual_base_of_tests.cpp new file mode 100644 index 0000000..a9f88b1 --- /dev/null +++ b/develop/untests/meta_base/is_virtual_base_of_tests.cpp @@ -0,0 +1,77 @@ +/******************************************************************************* + * 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 {}; + struct B : virtual A {}; + struct C : virtual A {}; + struct D : B, C {}; + struct E {}; + + struct I {}; + struct J : virtual I {}; + struct K : J {}; +} + +TEST_CASE("meta/meta_base/is_virtual_base_of") { + namespace meta = meta_hpp; + using meta::detail::is_virtual_base_of; + using meta::detail::is_virtual_base_of_v; + + { + static_assert(!is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + + static_assert(is_virtual_base_of_v); + static_assert(!is_virtual_base_of_v); + static_assert(!is_virtual_base_of_v); + + static_assert(!is_virtual_base_of_v); + static_assert(!is_virtual_base_of_v); + + static_assert(!is_virtual_base_of_v); + static_assert(!is_virtual_base_of_v); + } + + { + static_assert(!is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + + static_assert(!is_virtual_base_of_v); + static_assert(!is_virtual_base_of_v); + static_assert(!is_virtual_base_of_v); + + static_assert(!is_virtual_base_of_v); + static_assert(!is_virtual_base_of_v); + static_assert(!is_virtual_base_of_v); + } + + { + static_assert(is_virtual_base_of_v); + + static_assert(is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + + static_assert(is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + + static_assert(is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + + static_assert(is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + static_assert(is_virtual_base_of_v); + } +} diff --git a/develop/untests/meta_features/ambiguous_tests.cpp b/develop/untests/meta_features/ambiguous_tests.cpp new file mode 100644 index 0000000..65267fa --- /dev/null +++ b/develop/untests/meta_features/ambiguous_tests.cpp @@ -0,0 +1,111 @@ +/******************************************************************************* + * 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()); + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + 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()); + CHECK(meta::resolve_type().is_virtual_base_of()); + CHECK(meta::resolve_type().is_virtual_base_of()); + + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + CHECK_FALSE(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(meta::resolve_type().is_virtual_base_of()); + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + CHECK_FALSE(meta::resolve_type().is_virtual_base_of()); + } +} diff --git a/develop/untests/meta_issues/random_issue_1.cpp b/develop/untests/meta_issues/random_issue_1.cpp new file mode 100644 index 0000000..39c918e --- /dev/null +++ b/develop/untests/meta_issues/random_issue_1.cpp @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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 {}; + 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 <- * + +} + +TEST_CASE("meta/meta_issues/random/1") { + namespace meta = meta_hpp; + + { + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); + + 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 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/develop/untests/meta_types/class_type_tests.cpp b/develop/untests/meta_types/class_type_tests.cpp index 7ab1db3..e31eb6a 100644 --- a/develop/untests/meta_types/class_type_tests.cpp +++ b/develop/untests/meta_types/class_type_tests.cpp @@ -248,40 +248,62 @@ TEST_CASE("meta/meta_types/class_type") { { CHECK_FALSE(base_clazz_1_type.is_base_of()); CHECK_FALSE(base_clazz_1_type.is_base_of(base_clazz_1_type)); + CHECK_FALSE(base_clazz_1_type.is_direct_base_of()); + CHECK_FALSE(base_clazz_1_type.is_direct_base_of(base_clazz_1_type)); CHECK_FALSE(base_clazz_1_type.is_base_of()); CHECK_FALSE(base_clazz_1_type.is_base_of(base_clazz_2_type)); + CHECK_FALSE(base_clazz_1_type.is_direct_base_of()); + CHECK_FALSE(base_clazz_1_type.is_direct_base_of(base_clazz_2_type)); CHECK(base_clazz_1_type.is_base_of()); CHECK(base_clazz_1_type.is_base_of(derived_clazz_type)); + CHECK(base_clazz_1_type.is_direct_base_of()); + CHECK(base_clazz_1_type.is_direct_base_of(derived_clazz_type)); CHECK(base_clazz_1_type.is_base_of()); CHECK(base_clazz_1_type.is_base_of(final_derived_clazz_type)); + CHECK_FALSE(base_clazz_1_type.is_direct_base_of()); + CHECK_FALSE(base_clazz_1_type.is_direct_base_of(final_derived_clazz_type)); } { CHECK_FALSE(base_clazz_2_type.is_base_of()); CHECK_FALSE(base_clazz_2_type.is_base_of(base_clazz_1_type)); + CHECK_FALSE(base_clazz_2_type.is_direct_base_of()); + CHECK_FALSE(base_clazz_2_type.is_direct_base_of(base_clazz_1_type)); CHECK_FALSE(base_clazz_2_type.is_base_of()); CHECK_FALSE(base_clazz_2_type.is_base_of(base_clazz_2_type)); + CHECK_FALSE(base_clazz_2_type.is_direct_base_of()); + CHECK_FALSE(base_clazz_2_type.is_direct_base_of(base_clazz_2_type)); CHECK(base_clazz_2_type.is_base_of()); CHECK(base_clazz_2_type.is_base_of(derived_clazz_type)); + CHECK(base_clazz_2_type.is_direct_base_of()); + CHECK(base_clazz_2_type.is_direct_base_of(derived_clazz_type)); CHECK(base_clazz_2_type.is_base_of()); CHECK(base_clazz_2_type.is_base_of(final_derived_clazz_type)); + CHECK_FALSE(base_clazz_2_type.is_direct_base_of()); + CHECK_FALSE(base_clazz_2_type.is_direct_base_of(final_derived_clazz_type)); } { CHECK_FALSE(derived_clazz_type.is_base_of()); CHECK_FALSE(derived_clazz_type.is_base_of(base_clazz_1_type)); + CHECK_FALSE(derived_clazz_type.is_direct_base_of()); + CHECK_FALSE(derived_clazz_type.is_direct_base_of(base_clazz_1_type)); CHECK_FALSE(derived_clazz_type.is_base_of()); CHECK_FALSE(derived_clazz_type.is_base_of(base_clazz_2_type)); + CHECK_FALSE(derived_clazz_type.is_direct_base_of()); + CHECK_FALSE(derived_clazz_type.is_direct_base_of(base_clazz_2_type)); CHECK_FALSE(derived_clazz_type.is_base_of()); CHECK_FALSE(derived_clazz_type.is_base_of(derived_clazz_type)); + CHECK_FALSE(derived_clazz_type.is_direct_base_of()); + CHECK_FALSE(derived_clazz_type.is_direct_base_of(derived_clazz_type)); } } @@ -289,45 +311,69 @@ TEST_CASE("meta/meta_types/class_type") { { CHECK_FALSE(base_clazz_1_type.is_derived_from()); CHECK_FALSE(base_clazz_1_type.is_derived_from(base_clazz_1_type)); + CHECK_FALSE(base_clazz_1_type.is_direct_derived_from()); + CHECK_FALSE(base_clazz_1_type.is_direct_derived_from(base_clazz_1_type)); CHECK_FALSE(base_clazz_1_type.is_derived_from()); CHECK_FALSE(base_clazz_1_type.is_derived_from(base_clazz_2_type)); + CHECK_FALSE(base_clazz_1_type.is_direct_derived_from()); + CHECK_FALSE(base_clazz_1_type.is_direct_derived_from(base_clazz_2_type)); CHECK_FALSE(base_clazz_1_type.is_derived_from()); CHECK_FALSE(base_clazz_1_type.is_derived_from(derived_clazz_type)); + CHECK_FALSE(base_clazz_1_type.is_direct_derived_from()); + CHECK_FALSE(base_clazz_1_type.is_direct_derived_from(derived_clazz_type)); } { CHECK_FALSE(base_clazz_2_type.is_derived_from()); CHECK_FALSE(base_clazz_2_type.is_derived_from(base_clazz_1_type)); + CHECK_FALSE(base_clazz_2_type.is_direct_derived_from()); + CHECK_FALSE(base_clazz_2_type.is_direct_derived_from(base_clazz_1_type)); CHECK_FALSE(base_clazz_2_type.is_derived_from()); CHECK_FALSE(base_clazz_2_type.is_derived_from(base_clazz_2_type)); + CHECK_FALSE(base_clazz_2_type.is_direct_derived_from()); + CHECK_FALSE(base_clazz_2_type.is_direct_derived_from(base_clazz_2_type)); CHECK_FALSE(base_clazz_2_type.is_derived_from()); CHECK_FALSE(base_clazz_2_type.is_derived_from(derived_clazz_type)); + CHECK_FALSE(base_clazz_2_type.is_direct_derived_from()); + CHECK_FALSE(base_clazz_2_type.is_direct_derived_from(derived_clazz_type)); } { CHECK(derived_clazz_type.is_derived_from()); CHECK(derived_clazz_type.is_derived_from(base_clazz_1_type)); + CHECK(derived_clazz_type.is_direct_derived_from()); + CHECK(derived_clazz_type.is_direct_derived_from(base_clazz_1_type)); CHECK(derived_clazz_type.is_derived_from()); CHECK(derived_clazz_type.is_derived_from(base_clazz_2_type)); + CHECK(derived_clazz_type.is_direct_derived_from()); + CHECK(derived_clazz_type.is_direct_derived_from(base_clazz_2_type)); CHECK_FALSE(derived_clazz_type.is_derived_from()); CHECK_FALSE(derived_clazz_type.is_derived_from(derived_clazz_type)); + CHECK_FALSE(derived_clazz_type.is_direct_derived_from()); + CHECK_FALSE(derived_clazz_type.is_direct_derived_from(derived_clazz_type)); } { CHECK(final_derived_clazz_type.is_derived_from()); CHECK(final_derived_clazz_type.is_derived_from(base_clazz_1_type)); + CHECK_FALSE(final_derived_clazz_type.is_direct_derived_from()); + CHECK_FALSE(final_derived_clazz_type.is_direct_derived_from(base_clazz_1_type)); CHECK(final_derived_clazz_type.is_derived_from()); CHECK(final_derived_clazz_type.is_derived_from(base_clazz_2_type)); + CHECK_FALSE(final_derived_clazz_type.is_direct_derived_from()); + CHECK_FALSE(final_derived_clazz_type.is_direct_derived_from(base_clazz_2_type)); CHECK(final_derived_clazz_type.is_derived_from()); CHECK(final_derived_clazz_type.is_derived_from(derived_clazz_type)); + CHECK(final_derived_clazz_type.is_direct_derived_from()); + CHECK(final_derived_clazz_type.is_direct_derived_from(derived_clazz_type)); } } diff --git a/develop/untests/meta_utilities/invoke_tests.cpp b/develop/untests/meta_utilities/invoke_tests.cpp index ccc3f2c..bb40a4c 100644 --- a/develop/untests/meta_utilities/invoke_tests.cpp +++ b/develop/untests/meta_utilities/invoke_tests.cpp @@ -40,6 +40,13 @@ TEST_CASE("meta/meta_utilities/invoke") { CHECK(meta::invoke(clazz_function, meta::uvalue{3}).as() == 3); CHECK(meta::invoke(clazz_function, meta::uresult{3}).as() == 3); + CHECK(meta::try_invoke(&clazz::function, 3)->as() == 3); + CHECK(meta::try_invoke(&clazz::function, meta::uvalue{3})->as() == 3); + CHECK(meta::try_invoke(&clazz::function, meta::uresult{3})->as() == 3); + CHECK(meta::try_invoke(clazz_function, 3)->as() == 3); + CHECK(meta::try_invoke(clazz_function, meta::uvalue{3})->as() == 3); + CHECK(meta::try_invoke(clazz_function, meta::uresult{3})->as() == 3); + CHECK(meta::is_invocable_with(clazz_function, 3)); CHECK(meta::is_invocable_with(clazz_function, meta::uvalue{3})); CHECK(meta::is_invocable_with(clazz_function, meta::uresult{3})); @@ -61,6 +68,13 @@ TEST_CASE("meta/meta_utilities/invoke") { CHECK(meta::invoke(clazz_member, meta::uvalue{cl}).as() == 1); CHECK(meta::invoke(clazz_member, meta::uresult{cl}).as() == 1); + CHECK(meta::try_invoke(&clazz::member, cl)->as() == 1); + CHECK(meta::try_invoke(&clazz::member, meta::uvalue{cl})->as() == 1); + CHECK(meta::try_invoke(&clazz::member, meta::uresult{cl})->as() == 1); + CHECK(meta::try_invoke(clazz_member, cl)->as() == 1); + CHECK(meta::try_invoke(clazz_member, meta::uvalue{cl})->as() == 1); + CHECK(meta::try_invoke(clazz_member, meta::uresult{cl})->as() == 1); + CHECK(meta::is_invocable_with(clazz_member, cl)); CHECK(meta::is_invocable_with(clazz_member, meta::uvalue{cl})); CHECK(meta::is_invocable_with(clazz_member, meta::uresult{cl})); @@ -82,6 +96,13 @@ TEST_CASE("meta/meta_utilities/invoke") { CHECK(meta::invoke(clazz_method, meta::uvalue{cl}, meta::uvalue{2}).as() == 2); CHECK(meta::invoke(clazz_method, meta::uresult{cl}, meta::uresult{2}).as() == 2); + CHECK(meta::try_invoke(&clazz::method, cl, 2)->as() == 2); + CHECK(meta::try_invoke(&clazz::method, meta::uvalue{cl}, meta::uvalue{2})->as() == 2); + CHECK(meta::try_invoke(&clazz::method, meta::uresult{cl}, meta::uresult{2})->as() == 2); + CHECK(meta::try_invoke(clazz_method, cl, 2)->as() == 2); + CHECK(meta::try_invoke(clazz_method, meta::uvalue{cl}, meta::uvalue{2})->as() == 2); + CHECK(meta::try_invoke(clazz_method, meta::uresult{cl}, meta::uresult{2})->as() == 2); + CHECK(meta::is_invocable_with(clazz_method, cl, 2)); CHECK(meta::is_invocable_with(clazz_method, meta::uvalue{cl}, meta::uvalue{2})); CHECK(meta::is_invocable_with(clazz_method, meta::uresult{cl}, meta::uresult{2})); diff --git a/headers/meta.hpp/meta_base.hpp b/headers/meta.hpp/meta_base.hpp index 8aceaf6..150baae 100644 --- a/headers/meta.hpp/meta_base.hpp +++ b/headers/meta.hpp/meta_base.hpp @@ -18,7 +18,9 @@ #include "meta_base/hashed_string.hpp" #include "meta_base/insert_or_assign.hpp" #include "meta_base/intrusive_ptr.hpp" +#include "meta_base/is_disjoint.hpp" #include "meta_base/is_in_place_type.hpp" +#include "meta_base/is_virtual_base_of.hpp" #include "meta_base/memory_buffer.hpp" #include "meta_base/noncopyable.hpp" #include "meta_base/nonesuch.hpp" diff --git a/headers/meta.hpp/meta_base/base.hpp b/headers/meta.hpp/meta_base/base.hpp index 5cae5f2..f07e52d 100644 --- a/headers/meta.hpp/meta_base/base.hpp +++ b/headers/meta.hpp/meta_base/base.hpp @@ -53,6 +53,12 @@ # define META_HPP_ASSERT(...) assert(__VA_ARGS__) // NOLINT #endif +#if defined(META_HPP_SANITIZERS) +# define META_HPP_DEV_ASSERT(...) META_HPP_ASSERT(__VA_ARGS__) +#else +# define META_HPP_DEV_ASSERT(...) (void)0 +#endif + #if !defined(META_HPP_PP_CAT) # define META_HPP_PP_CAT(x, y) META_HPP_PP_CAT_I(x, y) # define META_HPP_PP_CAT_I(x, y) x##y diff --git a/headers/meta.hpp/meta_base/defer.hpp b/headers/meta.hpp/meta_base/defer.hpp index 7764070..7450244 100644 --- a/headers/meta.hpp/meta_base/defer.hpp +++ b/headers/meta.hpp/meta_base/defer.hpp @@ -8,42 +8,42 @@ #include "base.hpp" -namespace meta_hpp::detail::impl -{ - template < typename F, typename... Args > - class defer_impl { - public: - defer_impl() = delete; - defer_impl(defer_impl&&) = delete; - defer_impl(const defer_impl&) = delete; - defer_impl& operator=(defer_impl&&) = delete; - defer_impl& operator=(const defer_impl&) = delete; - - template < typename UF > - explicit defer_impl(UF&& f, std::tuple&& args) - : f_{std::forward(f)} - , args_{std::move(args)} {} - - void dismiss() noexcept { - dismissed_ = true; - } - - protected: - ~defer_impl() noexcept { - if ( !dismissed_ ) { - std::apply(std::move(f_), std::move(args_)); - } - } - - private: - F f_; - std::tuple args_; - bool dismissed_{}; - }; -} - namespace meta_hpp::detail { + namespace impl + { + template < typename F, typename... Args > + class defer_impl { + public: + defer_impl() = delete; + defer_impl(defer_impl&&) = delete; + defer_impl(const defer_impl&) = delete; + defer_impl& operator=(defer_impl&&) = delete; + defer_impl& operator=(const defer_impl&) = delete; + + template < typename UF > + explicit defer_impl(UF&& f, std::tuple&& args) + : f_{std::forward(f)} + , args_{std::move(args)} {} + + void dismiss() noexcept { + dismissed_ = true; + } + + protected: + ~defer_impl() noexcept { + if ( !dismissed_ ) { + std::apply(std::move(f_), std::move(args_)); + } + } + + private: + F f_; + std::tuple args_; + bool dismissed_{}; + }; + } + template < typename F, typename... Args > class defer final : public impl::defer_impl { public: diff --git a/headers/meta.hpp/meta_base/fixed_function.hpp b/headers/meta.hpp/meta_base/fixed_function.hpp index a39275f..47c0727 100644 --- a/headers/meta.hpp/meta_base/fixed_function.hpp +++ b/headers/meta.hpp/meta_base/fixed_function.hpp @@ -126,15 +126,15 @@ namespace meta_hpp::detail static vtable_t table{ .call{[](const fixed_function& self, Args... args) -> R { - META_HPP_ASSERT(self); + META_HPP_DEV_ASSERT(self); const Fp& src = *buffer_cast(self.buffer_); return std::invoke(src, std::forward(args)...); }}, .move{[](fixed_function& from, fixed_function& to) noexcept { - META_HPP_ASSERT(!to); - META_HPP_ASSERT(from); + META_HPP_DEV_ASSERT(!to); + META_HPP_DEV_ASSERT(from); Fp& src = *buffer_cast(from.buffer_); std::construct_at(buffer_cast(to.buffer_), std::move(src)); @@ -145,7 +145,7 @@ namespace meta_hpp::detail }}, .destroy{[](fixed_function& self) { - META_HPP_ASSERT(self); + META_HPP_DEV_ASSERT(self); Fp& src = *buffer_cast(self.buffer_); std::destroy_at(&src); @@ -158,7 +158,7 @@ namespace meta_hpp::detail template < typename F, typename Fp = std::decay_t > static void construct(fixed_function& dst, F&& fun) { - META_HPP_ASSERT(!dst); + META_HPP_DEV_ASSERT(!dst); static_assert(sizeof(Fp) <= sizeof(buffer_t)); static_assert(alignof(buffer_t) % alignof(Fp) == 0); diff --git a/headers/meta.hpp/meta_base/is_disjoint.hpp b/headers/meta.hpp/meta_base/is_disjoint.hpp new file mode 100644 index 0000000..b7e7435 --- /dev/null +++ b/headers/meta.hpp/meta_base/is_disjoint.hpp @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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) + ******************************************************************************/ + +#pragma once + +#include "base.hpp" + +namespace meta_hpp::detail +{ + template < typename SortedContainerL, typename SortedContainerR, typename Compare > + bool is_disjoint(const SortedContainerL& l, const SortedContainerR& r, Compare compare) { + using std::begin; + using std::end; + + for ( auto iter_l{begin(l)}, iter_r{begin(r)}; iter_l != end(l) && iter_r != end(r); ) { + if ( compare(*iter_l, *iter_r) ) { + ++iter_l; + } else if ( compare(*iter_r, *iter_l) ) { + ++iter_r; + } else { + return false; + } + } + + return true; + } + + template < typename SortedContainerL, typename SortedContainerR > + bool is_disjoint(const SortedContainerL& l, const SortedContainerR& r) { + return is_disjoint(l, r, std::less<>{}); + } +} diff --git a/headers/meta.hpp/meta_base/is_virtual_base_of.hpp b/headers/meta.hpp/meta_base/is_virtual_base_of.hpp new file mode 100644 index 0000000..99b8f44 --- /dev/null +++ b/headers/meta.hpp/meta_base/is_virtual_base_of.hpp @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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) + ******************************************************************************/ + +#pragma once + +#include "base.hpp" + +namespace meta_hpp::detail +{ + namespace impl + { + template < typename From, typename To > + constexpr bool is_virtual_base_of_impl(...) noexcept { + return false; + } + + template < + typename From, + typename To, + decltype(static_cast(std::declval())) = nullptr > + constexpr bool is_virtual_base_of_impl(int) noexcept { + return true; + } + } + + // clang-format off + + template < typename Base, typename Derived > + struct is_virtual_base_of : std::integral_constant && + impl::is_virtual_base_of_impl(0) && + !impl::is_virtual_base_of_impl(0)> {}; + + // clang-format on + + template < typename Base, typename Derived > + inline constexpr bool is_virtual_base_of_v = is_virtual_base_of::value; +} diff --git a/headers/meta.hpp/meta_binds/class_bind.hpp b/headers/meta.hpp/meta_binds/class_bind.hpp index dcbaf52..2419364 100644 --- a/headers/meta.hpp/meta_binds/class_bind.hpp +++ b/headers/meta.hpp/meta_binds/class_bind.hpp @@ -23,19 +23,20 @@ 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( // const class_type& derived_class, const class_type& new_base_class, - const upcast_func_list_t& derived_to_new_base, + 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); @@ -43,26 +44,27 @@ namespace meta_hpp::detail::class_bind_impl 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); + + const auto add_derived_deep_upcast = [&derived_deep_upcasts](const class_type& deep_class, upcast_func_list_t&& upcasts) { + auto&& [position, emplaced] = derived_deep_upcasts.try_emplace(deep_class, std::move(upcasts)); + if ( !emplaced ) { + position->second.is_ambiguous = is_disjoint(position->second.vbases, upcasts.vbases); + } + }; 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)); + upcast_func_list_t derived_to_new_deep = derived_to_new_base + new_base_to_deep; + add_derived_deep_upcast(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); + upcast_func_t subderived_to_derived = subderived_data.base_upcasts.at(derived_class); + upcast_func_list_t subderived_to_new_base = subderived_to_derived + derived_to_new_base; + update_deep_upcasts_db(subderived_class, new_base_class, std::move(subderived_to_new_base), deep_upcasts_db); } + + add_derived_deep_upcast(new_base_class, std::move(derived_to_new_base)); } inline void updata_derived_classes_db( // @@ -116,7 +118,7 @@ namespace meta_hpp continue; } - update_deep_upcasts_db(*this, new_base_class, {self_to_new_base}, deep_upcasts_db); + 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); diff --git a/headers/meta.hpp/meta_detail/type_traits/class_traits.hpp b/headers/meta.hpp/meta_detail/type_traits/class_traits.hpp index 3e8eb70..faef0f4 100644 --- a/headers/meta.hpp/meta_detail/type_traits/class_traits.hpp +++ b/headers/meta.hpp/meta_detail/type_traits/class_traits.hpp @@ -27,7 +27,7 @@ namespace meta_hpp::detail namespace impl { template < class_kind Class > - struct class_traits_base { + struct class_traits_impl { static constexpr std::size_t arity{0}; using argument_types = type_list<>; @@ -38,7 +38,7 @@ namespace meta_hpp::detail }; template < template < typename... > typename Class, typename... Args > - struct class_traits_base> { + struct class_traits_impl> { static constexpr std::size_t arity{sizeof...(Args)}; using argument_types = type_list; @@ -50,7 +50,7 @@ namespace meta_hpp::detail } template < class_kind Class > - struct class_traits : impl::class_traits_base { + struct class_traits : impl::class_traits_impl { static constexpr std::size_t size{sizeof(Class)}; static constexpr std::size_t align{alignof(Class)}; @@ -73,7 +73,7 @@ namespace meta_hpp::detail flags.set(class_flags::is_polymorphic); } - return flags | impl::class_traits_base::make_flags(); + return flags | impl::class_traits_impl::make_flags(); } }; } diff --git a/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp b/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp index d772f28..a943201 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp @@ -244,7 +244,7 @@ namespace meta_hpp::detail { template < pointer_kind To > [[nodiscard]] decltype(auto) uarg::cast(type_registry& registry) const { - META_HPP_ASSERT(can_cast_to(registry) && "bad argument cast"); + META_HPP_DEV_ASSERT(can_cast_to(registry) && "bad argument cast"); using to_raw_type = std::remove_cv_t; @@ -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 = 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 = 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); @@ -280,7 +286,7 @@ namespace meta_hpp::detail template < non_pointer_kind To > [[nodiscard]] decltype(auto) uarg::cast(type_registry& registry) const { - META_HPP_ASSERT(can_cast_to(registry) && "bad argument cast"); + META_HPP_DEV_ASSERT(can_cast_to(registry) && "bad argument cast"); using to_raw_type_cv = std::remove_reference_t; using to_raw_type = std::remove_cv_t; @@ -294,6 +300,7 @@ namespace meta_hpp::detail const any_type& to_type = registry.resolve_type(); void* to_ptr = pointer_upcast(data_, from_type, to_type); + META_HPP_ASSERT(to_ptr); if constexpr ( std::is_lvalue_reference_v ) { return *static_cast(to_ptr); @@ -333,18 +340,11 @@ namespace meta_hpp::detail namespace meta_hpp::detail { - template < typename ArgTypeList, typename F > - auto call_with_uargs(type_registry& registry, std::span args, F&& f) { - META_HPP_ASSERT(args.size() == type_list_arity_v); - return [ args, ®istry, &f ](std::index_sequence) { - return f(args[Is].cast>(registry)...); - } - (std::make_index_sequence>()); - } - template < typename ArgTypeList > bool can_cast_all_uargs(type_registry& registry, std::span args) { - META_HPP_ASSERT(args.size() == type_list_arity_v); + if ( args.size() != type_list_arity_v ) { + return false; + } return [ args, ®istry ](std::index_sequence) { return (... && args[Is].can_cast_to>(registry)); } @@ -353,10 +353,21 @@ namespace meta_hpp::detail template < typename ArgTypeList > bool can_cast_all_uargs(type_registry& registry, std::span args) { - META_HPP_ASSERT(args.size() == type_list_arity_v); + if ( args.size() != type_list_arity_v ) { + return false; + } return [ args, ®istry ](std::index_sequence) { return (... && args[Is].can_cast_to>(registry)); } (std::make_index_sequence>()); } + + template < typename ArgTypeList, typename F > + auto unchecked_call_with_uargs(type_registry& registry, std::span args, F&& f) { + META_HPP_DEV_ASSERT(args.size() == type_list_arity_v); + return [ args, ®istry, &f ](std::index_sequence) { + return f(args[Is].cast>(registry)...); + } + (std::make_index_sequence>()); + } } diff --git a/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp b/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp index 89475f4..493e6c6 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp @@ -183,7 +183,7 @@ namespace meta_hpp::detail { template < inst_class_ref_kind Q > decltype(auto) uinst::cast(type_registry& registry) const { - META_HPP_ASSERT(can_cast_to(registry) && "bad instance cast"); + META_HPP_DEV_ASSERT(can_cast_to(registry) && "bad instance cast"); using inst_class_cv = std::remove_reference_t; using inst_class = std::remove_cv_t; @@ -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 = 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 = 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 8d8ed5a..68c78d0 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp @@ -105,8 +105,13 @@ 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 ( auto iter{deep_upcasts.find(base)}; iter != deep_upcasts.end() && !iter->second.is_ambiguous ) { + return true; + } } return false; @@ -124,28 +129,14 @@ namespace meta_hpp::detail return ptr; } - void* base_ptr = nullptr; + const class_type_data& from_data = *type_access(from); + const class_type_data::deep_upcasts_t& deep_upcasts = from_data.deep_upcasts; - 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; - } + if ( auto iter{deep_upcasts.find(to)}; iter != deep_upcasts.end() && !iter->second.is_ambiguous ) { + 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) { diff --git a/headers/meta.hpp/meta_invoke/invoke.hpp b/headers/meta.hpp/meta_invoke/invoke.hpp index cefa5c2..2a079b4 100644 --- a/headers/meta.hpp/meta_invoke/invoke.hpp +++ b/headers/meta.hpp/meta_invoke/invoke.hpp @@ -24,6 +24,11 @@ namespace meta_hpp return function.invoke(std::forward(args)...); } + template < typename... Args > + uresult try_invoke(const function& function, Args&&... args) { + return function.try_invoke(std::forward(args)...); + } + template < detail::function_pointer_kind Function, typename... Args > uvalue invoke(Function function_ptr, Args&&... args) { using namespace detail; @@ -31,6 +36,22 @@ namespace meta_hpp const std::array vargs{uarg{registry, std::forward(args)}...}; return raw_function_invoke(registry, function_ptr, vargs); } + + template < detail::function_pointer_kind Function, typename... Args > + uresult try_invoke(Function function_ptr, Args&&... args) { + using namespace detail; + type_registry& registry{type_registry::instance()}; + + { + const std::array vargs{uarg_base{registry, std::forward(args)}...}; + if ( const uerror err = raw_function_invoke_error(registry, vargs) ) { + return err; + } + } + + const std::array vargs{uarg{registry, std::forward(args)}...}; + return raw_function_invoke(registry, function_ptr, vargs); + } } namespace meta_hpp @@ -40,6 +61,11 @@ namespace meta_hpp return member.get(std::forward(instance)); } + template < typename Instance > + uresult try_invoke(const member& member, Instance&& instance) { + return member.try_get(std::forward(instance)); + } + template < detail::member_pointer_kind Member, typename Instance > uvalue invoke(Member member_ptr, Instance&& instance) { using namespace detail; @@ -47,6 +73,22 @@ namespace meta_hpp const uinst vinst{registry, std::forward(instance)}; return raw_member_getter(registry, member_ptr, vinst); } + + template < detail::member_pointer_kind Member, typename Instance > + uresult try_invoke(Member member_ptr, Instance&& instance) { + using namespace detail; + type_registry& registry{type_registry::instance()}; + + { + const uinst_base vinst{registry, std::forward(instance)}; + if ( const uerror err = raw_member_getter_error(registry, vinst) ) { + return err; + } + } + + const uinst vinst{registry, std::forward(instance)}; + return raw_member_getter(registry, member_ptr, vinst); + } } namespace meta_hpp @@ -56,6 +98,11 @@ namespace meta_hpp return method.invoke(std::forward(instance), std::forward(args)...); } + template < typename Instance, typename... Args > + uresult try_invoke(const method& method, Instance&& instance, Args&&... args) { + return method.try_invoke(std::forward(instance), std::forward(args)...); + } + template < detail::method_pointer_kind Method, typename Instance, typename... Args > uvalue invoke(Method method_ptr, Instance&& instance, Args&&... args) { using namespace detail; @@ -64,6 +111,24 @@ namespace meta_hpp const std::array vargs{uarg{registry, std::forward(args)}...}; return raw_method_invoke(registry, method_ptr, vinst, vargs); } + + template < detail::method_pointer_kind Method, typename Instance, typename... Args > + uresult try_invoke(Method method_ptr, Instance&& instance, Args&&... args) { + using namespace detail; + type_registry& registry{type_registry::instance()}; + + { + const uinst_base vinst{registry, std::forward(instance)}; + const std::array vargs{uarg_base{registry, std::forward(args)}...}; + if ( const uerror err = raw_method_invoke_error(registry, vinst, vargs) ) { + return err; + } + } + + const uinst vinst{registry, std::forward(instance)}; + const std::array vargs{uarg{registry, std::forward(args)}...}; + return raw_method_invoke(registry, method_ptr, vinst, vargs); + } } namespace meta_hpp diff --git a/headers/meta.hpp/meta_states/constructor.hpp b/headers/meta.hpp/meta_states/constructor.hpp index 42d9890..99bead5 100644 --- a/headers/meta.hpp/meta_states/constructor.hpp +++ b/headers/meta.hpp/meta_states/constructor.hpp @@ -43,7 +43,7 @@ namespace meta_hpp::detail && "an attempt to call a constructor with incorrect argument types" ); - return call_with_uargs(registry, args, [](auto&&... all_args) -> uvalue { + return unchecked_call_with_uargs(registry, args, [](auto&&... all_args) -> uvalue { if constexpr ( as_object ) { return make_uvalue(META_HPP_FWD(all_args)...); } @@ -74,7 +74,7 @@ namespace meta_hpp::detail && "an attempt to call a constructor with incorrect argument types" ); - return call_with_uargs(registry, args, [mem](auto&&... all_args) { + return unchecked_call_with_uargs(registry, args, [mem](auto&&... all_args) { return std::construct_at(static_cast(mem), META_HPP_FWD(all_args)...); }); } diff --git a/headers/meta.hpp/meta_states/function.hpp b/headers/meta.hpp/meta_states/function.hpp index 7408383..92dc4dd 100644 --- a/headers/meta.hpp/meta_states/function.hpp +++ b/headers/meta.hpp/meta_states/function.hpp @@ -45,7 +45,7 @@ namespace meta_hpp::detail && "an attempt to call a function with incorrect argument types" ); - return call_with_uargs(registry, args, [function_ptr](auto&&... all_args) { + return unchecked_call_with_uargs(registry, args, [function_ptr](auto&&... all_args) { if constexpr ( std::is_void_v ) { function_ptr(META_HPP_FWD(all_args)...); return uvalue{}; diff --git a/headers/meta.hpp/meta_states/method.hpp b/headers/meta.hpp/meta_states/method.hpp index e9e385d..685e20b 100644 --- a/headers/meta.hpp/meta_states/method.hpp +++ b/headers/meta.hpp/meta_states/method.hpp @@ -52,7 +52,7 @@ namespace meta_hpp::detail && "an attempt to call a method with incorrect argument types" ); - return call_with_uargs(registry, args, [method_ptr, &inst, ®istry](auto&&... all_args) { + return unchecked_call_with_uargs(registry, args, [method_ptr, &inst, ®istry](auto&&... all_args) { if constexpr ( std::is_void_v ) { (inst.cast(registry).*method_ptr)(META_HPP_FWD(all_args)...); return uvalue{}; diff --git a/headers/meta.hpp/meta_types.hpp b/headers/meta.hpp/meta_types.hpp index 9d109f0..6a55006 100644 --- a/headers/meta.hpp/meta_types.hpp +++ b/headers/meta.hpp/meta_types.hpp @@ -180,10 +180,26 @@ namespace meta_hpp [[nodiscard]] bool is_base_of() const noexcept; [[nodiscard]] bool is_base_of(const class_type& derived) const noexcept; + template < detail::class_kind Derived > + [[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; + template < detail::class_kind Base > + [[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; @@ -402,11 +418,39 @@ namespace meta_hpp::detail typedef_map typedefs; variable_set variables; - using upcast_func_t = void* (*)(void*); - using upcast_func_list_t = std::vector; + struct upcast_func_t final { + using upcast_t = void* (*)(void*); + + upcast_t upcast{}; + class_type target{}; + bool is_virtual{}; + + 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 { + using upcasts_t = std::vector; + + upcasts_t upcasts{}; + class_set vbases{}; + bool is_ambiguous{}; + + upcast_func_list_t(const upcast_func_t& _upcast); + upcast_func_list_t(upcasts_t _upcasts, class_set _vbases); + + [[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); + }; using base_upcasts_t = std::map>; - using deep_upcasts_t = std::multimap>; + using deep_upcasts_t = std::map>; base_upcasts_t base_upcasts; deep_upcasts_t deep_upcasts; diff --git a/headers/meta.hpp/meta_types/class_type.hpp b/headers/meta.hpp/meta_types/class_type.hpp index 5718aeb..4842ed4 100644 --- a/headers/meta.hpp/meta_types/class_type.hpp +++ b/headers/meta.hpp/meta_types/class_type.hpp @@ -32,6 +32,69 @@ namespace meta_hpp::detail , argument_types{resolve_types(typename class_traits::argument_types{})} {} } +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)); }} + , target{resolve_type()} + , is_virtual{is_virtual_base_of_v} {} + + 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 ) { + vbases.emplace(upcast.target); + } + } + } + + inline class_type_data::upcast_func_list_t::upcast_func_list_t(upcasts_t _upcasts, class_set _vbases) + : upcasts{std::move(_upcasts)} + , vbases{std::move(_vbases)} {} + + inline void* class_type_data::upcast_func_list_t::apply(void* ptr) const noexcept { + for ( const upcast_func_t& upcast : upcasts ) { + ptr = upcast.apply(ptr); + } + return ptr; + } + + inline const void* class_type_data::upcast_func_list_t::apply(const void* ptr) const noexcept { + // NOLINTNEXTLINE(*-const-cast) + return apply(const_cast(ptr)); + } + + inline class_type_data::upcast_func_list_t operator+( // + const class_type_data::upcast_func_list_t& l, + const class_type_data::upcast_func_list_t& r + ) { + class_type_data::upcast_func_list_t::upcasts_t 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()); + + class_set new_vbases; + new_vbases.insert(l.vbases.begin(), l.vbases.end()); + new_vbases.insert(r.vbases.begin(), r.vbases.end()); + + return class_type_data::upcast_func_list_t{std::move(new_upcasts), std::move(new_vbases)}; + } +} + namespace meta_hpp { inline class_bitflags class_type::get_flags() const noexcept { @@ -139,12 +202,33 @@ namespace meta_hpp } inline bool class_type::is_base_of(const class_type& derived) const noexcept { + return is_valid() && derived.is_valid() && derived.data_->deep_upcasts.contains(*this); + } + + template < detail::class_kind Derived > + bool class_type::is_direct_base_of() const noexcept { + return is_direct_base_of(resolve_type()); + } + + inline bool class_type::is_direct_base_of(const class_type& derived) const noexcept { + 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; } - if ( derived.data_->deep_upcasts.contains(*this) ) { - return true; + using deep_upcasts_t = detail::class_type_data::deep_upcasts_t; + const deep_upcasts_t& deep_upcasts = derived.data_->deep_upcasts; + + if ( auto iter{deep_upcasts.find(*this)}; iter != deep_upcasts.end() ) { + return !iter->second.is_ambiguous && !iter->second.vbases.empty(); } return false; @@ -156,15 +240,25 @@ namespace meta_hpp } inline bool class_type::is_derived_from(const class_type& base) const noexcept { - if ( !is_valid() || !base.is_valid() ) { - return false; - } + return base.is_base_of(*this); + } - if ( data_->deep_upcasts.contains(base) ) { - return true; - } + template < detail::class_kind Base > + bool class_type::is_direct_derived_from() const noexcept { + return is_direct_derived_from(resolve_type()); + } - return false; + inline bool class_type::is_direct_derived_from(const class_type& base) const noexcept { + 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/headers/meta.hpp/meta_uvalue/uvalue.hpp b/headers/meta.hpp/meta_uvalue/uvalue.hpp index 4ee0bf6..0232a82 100644 --- a/headers/meta.hpp/meta_uvalue/uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue/uvalue.hpp @@ -73,7 +73,7 @@ namespace meta_hpp template < typename T, typename... Args, typename Tp = std::decay_t > static Tp& do_ctor(uvalue& dst, Args&&... args) { - META_HPP_ASSERT(!dst); + META_HPP_DEV_ASSERT(!dst); if constexpr ( in_internal_v ) { std::construct_at(storage_cast(dst.storage_), std::forward(args)...); @@ -91,7 +91,7 @@ namespace meta_hpp } static void do_move(uvalue&& self, uvalue& to) noexcept { - META_HPP_ASSERT(!to); + META_HPP_DEV_ASSERT(!to); auto&& [tag, vtable] = unpack_vtag(self); @@ -110,7 +110,7 @@ namespace meta_hpp } static void do_copy(const uvalue& self, uvalue& to) noexcept { - META_HPP_ASSERT(!to); + META_HPP_DEV_ASSERT(!to); auto&& [tag, vtable] = unpack_vtag(self); @@ -172,8 +172,8 @@ namespace meta_hpp .type = resolve_type(), .move{[](uvalue&& self, uvalue& to) noexcept { - META_HPP_ASSERT(!to); - META_HPP_ASSERT(self); + META_HPP_DEV_ASSERT(!to); + META_HPP_DEV_ASSERT(self); Tp* src = storage_cast(self.storage_); @@ -188,8 +188,8 @@ namespace meta_hpp }}, .copy{[](const uvalue& self, uvalue& to) { - META_HPP_ASSERT(!to); - META_HPP_ASSERT(self); + META_HPP_DEV_ASSERT(!to); + META_HPP_DEV_ASSERT(self); const Tp* src = storage_cast(self.storage_); @@ -203,7 +203,7 @@ namespace meta_hpp }}, .reset{[](uvalue& self) noexcept { - META_HPP_ASSERT(self); + META_HPP_DEV_ASSERT(self); Tp* src = storage_cast(self.storage_);