diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index a9765e0..882d773 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +30,8 @@ #include #include #include +#include +#include #include #include #include @@ -40,24 +44,19 @@ # define META_HPP_NO_RTTI #endif -#if !defined(META_HPP_NO_EXCEPTIONS) -# include -#endif - -#if !defined(META_HPP_NO_RTTI) -# include -# include -#endif - #if !defined(META_HPP_FWD) # define META_HPP_FWD(v) std::forward(v) #endif #if !defined(META_HPP_ASSERT) -# include # define META_HPP_ASSERT(...) assert(__VA_ARGS__) // NOLINT #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 +#endif + namespace meta_hpp::detail { template < typename Enum > @@ -303,6 +302,141 @@ 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 +{ + template < typename F, typename... Args > + class defer final : public impl::defer_impl { + public: + defer() = delete; + defer(defer&&) = delete; + defer(const defer&) = delete; + defer& operator=(defer&&) = delete; + defer& operator=(const defer&) = delete; + + template < typename UF > + explicit defer(UF&& f, std::tuple&& args) + : impl::defer_impl{std::forward(f), std::move(args)} {} + + ~defer() noexcept = default; + }; + + template < typename F, typename... Args > + class error_defer final : public impl::defer_impl { + public: + error_defer() = delete; + error_defer(error_defer&&) = delete; + error_defer(const error_defer&) = delete; + error_defer& operator=(error_defer&&) = delete; + error_defer& operator=(const error_defer&) = delete; + + template < typename UF > + explicit error_defer(UF&& f, std::tuple&& args) + : impl::defer_impl{std::forward(f), std::move(args)} + , exceptions_{std::uncaught_exceptions()} {} + + ~error_defer() noexcept { + if ( exceptions_ == std::uncaught_exceptions() ) { + this->dismiss(); + } + } + + private: + int exceptions_{}; + }; + + template < typename F, typename... Args > + class return_defer final : public impl::defer_impl { + public: + return_defer() = delete; + return_defer(return_defer&&) = delete; + return_defer(const return_defer&) = delete; + return_defer& operator=(return_defer&&) = delete; + return_defer& operator=(const return_defer&) = delete; + + template < typename UF > + explicit return_defer(UF&& f, std::tuple&& args) + : impl::defer_impl{std::forward(f), std::move(args)} + , exceptions_{std::uncaught_exceptions()} {} + + ~return_defer() noexcept { + if ( exceptions_ != std::uncaught_exceptions() ) { + this->dismiss(); + } + } + + private: + int exceptions_{}; + }; + + template < typename F, typename... Args > + auto make_defer(F&& f, Args&&... args) { + using defer_t = defer, std::decay_t...>; + return defer_t{std::forward(f), std::make_tuple(std::forward(args)...)}; + } + + template < typename F, typename... Args > + auto make_error_defer(F&& f, Args&&... args) { + using defer_t = error_defer, std::decay_t...>; + return defer_t{std::forward(f), std::make_tuple(std::forward(args)...)}; + } + + template < typename F, typename... Args > + auto make_return_defer(F&& f, Args&&... args) { + using defer_t = return_defer, std::decay_t...>; + return defer_t{std::forward(f), std::make_tuple(std::forward(args)...)}; + } +} + +#ifdef __COUNTER__ +# define META_HPP_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_defer_, __COUNTER__) { ::meta_hpp::detail::make_defer(__VA_ARGS__) } +# define META_HPP_ERROR_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_error_defer_, __COUNTER__) { ::meta_hpp::detail::make_error_defer(__VA_ARGS__) } +# define META_HPP_RETURN_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_return_defer_, __COUNTER__) { ::meta_hpp::detail::make_return_defer(__VA_ARGS__) } +#else +# define META_HPP_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_defer_, __LINE__) { ::meta_hpp::detail::make_defer(__VA_ARGS__) } +# define META_HPP_ERROR_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_error_defer_, __LINE__) { ::meta_hpp::detail::make_error_defer(__VA_ARGS__) } +# define META_HPP_RETURN_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_return_defer_, __LINE__) { ::meta_hpp::detail::make_return_defer(__VA_ARGS__) } +#endif + #if !defined(META_HPP_NO_EXCEPTIONS) # define META_HPP_TRY try # define META_HPP_CATCH(...) catch ( __VA_ARGS__ ) @@ -2375,7 +2509,8 @@ namespace meta_hpp [[nodiscard]] any_type get_argument_type(std::size_t position) const noexcept; [[nodiscard]] const any_type_list& get_argument_types() const noexcept; - [[nodiscard]] const class_set& get_bases() const noexcept; + [[nodiscard]] const class_set& get_base_classes() const noexcept; + [[nodiscard]] const class_set& get_derived_classes() const noexcept; [[nodiscard]] const constructor_set& get_constructors() const noexcept; [[nodiscard]] const destructor_set& get_destructors() const noexcept; [[nodiscard]] const function_set& get_functions() const noexcept; @@ -2609,7 +2744,8 @@ namespace meta_hpp::detail const std::size_t align; const any_type_list argument_types; - class_set bases; + class_set base_classes; + class_set derived_classes; constructor_set constructors; destructor_set destructors; function_set functions; @@ -2618,13 +2754,14 @@ namespace meta_hpp::detail typedef_map typedefs; variable_set variables; - struct base_info final { - using upcast_fptr = void* (*)(void*); - const upcast_fptr upcast; - }; + using upcast_func_t = void* (*)(void*); + using upcast_func_list_t = std::vector; - using base_info_map = std::map>; - base_info_map bases_info; + using base_upcasts_t = std::map>; + using deep_upcasts_t = std::multimap>; + + base_upcasts_t base_upcasts; + deep_upcasts_t deep_upcasts; template < class_kind Class > explicit class_type_data(type_list); @@ -4653,6 +4790,73 @@ namespace meta_hpp : type_bind_base{resolve_type(), std::move(metadata)} {} } +namespace meta_hpp::detail::class_bind_impl +{ + using base_upcasts_t = class_type_data::base_upcasts_t; + using deep_upcasts_t = class_type_data::deep_upcasts_t; + + using upcast_func_t = class_type_data::upcast_func_t; + using upcast_func_list_t = class_type_data::upcast_func_list_t; + + using new_bases_db_t = std::map; + using deep_upcasts_db_t = std::map>; + using derived_classes_db_t = std::map>; + + template < class_kind Class, class_kind Base > + requires detail::class_bind_base_kind + void update_new_bases_db( // + new_bases_db_t& new_bases_db + ) { + new_bases_db.emplace(resolve_type(), [](void* from) { + return static_cast(static_cast(static_cast(from))); + }); + } + + inline void update_deep_upcasts_db( // + const class_type& derived_class, + const class_type& new_base_class, + const upcast_func_list_t& derived_to_new_base, + deep_upcasts_db_t& deep_upcasts_db + ) { + const class_type_data& derived_class_data = *type_access(derived_class); + const class_type_data& new_base_class_data = *type_access(new_base_class); + + const auto [deep_upcasts_db_iter, _] = deep_upcasts_db.try_emplace(derived_class, derived_class_data.deep_upcasts); + deep_upcasts_t& derived_deep_upcasts = deep_upcasts_db_iter->second; + derived_deep_upcasts.emplace(new_base_class, derived_to_new_base); + + for ( auto&& [new_deep_class, new_base_to_deep] : new_base_class_data.deep_upcasts ) { + upcast_func_list_t derived_to_new_deep; + derived_to_new_deep.reserve(derived_to_new_base.size() + new_base_to_deep.size()); + derived_to_new_deep.insert(derived_to_new_deep.end(), derived_to_new_base.begin(), derived_to_new_base.end()); + derived_to_new_deep.insert(derived_to_new_deep.end(), new_base_to_deep.begin(), new_base_to_deep.end()); + derived_deep_upcasts.emplace(new_deep_class, std::move(derived_to_new_deep)); + } + + for ( const class_type& subderived_class : derived_class_data.derived_classes ) { + const class_type_data& subderived_data = *type_access(subderived_class); + + upcast_func_list_t subderived_to_new_base; + subderived_to_new_base.reserve(derived_to_new_base.size() + 1); + subderived_to_new_base.insert(subderived_to_new_base.end(), subderived_data.base_upcasts.at(derived_class)); + subderived_to_new_base.insert(subderived_to_new_base.end(), derived_to_new_base.begin(), derived_to_new_base.end()); + + update_deep_upcasts_db(subderived_class, new_base_class, subderived_to_new_base, deep_upcasts_db); + } + } + + inline void updata_derived_classes_db( // + const class_type& self_class, + const class_type& new_base_class, + derived_classes_db_t& derived_classes_db + ) { + const class_type_data& base_class_data = *type_access(new_base_class); + class_set new_derived_classes{base_class_data.derived_classes}; + new_derived_classes.emplace(self_class); + derived_classes_db.emplace(new_base_class, std::move(new_derived_classes)); + } +} + namespace meta_hpp { template < detail::class_kind Class > @@ -4671,28 +4875,47 @@ namespace meta_hpp template < detail::class_kind... Bases > requires(... && detail::class_bind_base_kind) class_bind& class_bind::base_() { - const auto register_base{[this](std::in_place_type_t) { - const class_type& base_type = resolve_type(); + using namespace detail; + using namespace detail::class_bind_impl; - auto&& [position, emplaced] = get_data().bases.emplace(base_type); - if ( !emplaced ) { - return; + if ( (... && get_data().base_classes.contains(resolve_type())) ) { + return *this; + } + + new_bases_db_t new_bases_db; + (update_new_bases_db(new_bases_db), ...); + + deep_upcasts_db_t deep_upcasts_db; + derived_classes_db_t derived_classes_db; + + class_set new_base_classes{get_data().base_classes}; + base_upcasts_t new_base_upcasts{get_data().base_upcasts}; + + for ( auto&& [new_base_class, self_to_new_base] : new_bases_db ) { + if ( new_base_classes.contains(new_base_class) ) { + continue; } - META_HPP_TRY { - get_data().bases_info.emplace( // - base_type, - detail::class_type_data::base_info{ - .upcast{[](void* derived) -> void* { return static_cast(static_cast(derived)); }}} - ); - } - META_HPP_CATCH(...) { - get_data().bases.erase(position); - META_HPP_RETHROW(); - } - }}; + update_deep_upcasts_db(*this, new_base_class, {self_to_new_base}, deep_upcasts_db); + updata_derived_classes_db(*this, new_base_class, derived_classes_db); + + new_base_classes.emplace(new_base_class); + new_base_upcasts.emplace(new_base_class, self_to_new_base); + } + + get_data().base_classes.swap(new_base_classes); + get_data().base_upcasts.swap(new_base_upcasts); + + for ( auto&& [derived_class, new_deep_upcasts] : deep_upcasts_db ) { + class_type_data& derived_data = *type_access(derived_class); + derived_data.deep_upcasts.swap(new_deep_upcasts); + } + + for ( auto&& [base_class, new_derived_classes] : derived_classes_db ) { + class_type_data& base_data = *type_access(base_class); + base_data.derived_classes.swap(new_derived_classes); + } - (register_base(std::in_place_type), ...); return *this; } @@ -5466,37 +5689,34 @@ namespace meta_hpp::detail return ptr; } - for ( auto&& [base_type, base_info] : type_access(from)->bases_info ) { - if ( base_type == to ) { - return base_info.upcast(ptr); - } + void* base_ptr = nullptr; - if ( base_type.is_derived_from(to) ) { - return pointer_upcast(base_info.upcast(ptr), base_type, to); + class_type_data& from_data = *type_access(from); + class_type_data::deep_upcasts_t& deep_upcasts = from_data.deep_upcasts; + + for ( auto iter{deep_upcasts.lower_bound(to)}; iter != deep_upcasts.end() && iter->first == to; ++iter ) { + void* new_base_ptr = [ptr, iter]() mutable { + for ( class_type_data::upcast_func_t upcast : iter->second ) { + ptr = upcast(ptr); + } + return ptr; + }(); + + if ( base_ptr == nullptr ) { + base_ptr = new_base_ptr; + } else if ( base_ptr != new_base_ptr ) { + // ambiguous conversions + return nullptr; } } - return nullptr; + return base_ptr; } [[nodiscard]] inline const void* pointer_upcast(const void* ptr, const class_type& from, const class_type& to) { // NOLINTNEXTLINE(*-const-cast) return pointer_upcast(const_cast(ptr), from, to); } - - template < class_kind To, class_kind From > - [[nodiscard]] To* pointer_upcast(type_registry& registry, From* ptr) { - const class_type& to_class = registry.resolve_type(); - const class_type& from_class = registry.resolve_type(); - return static_cast(pointer_upcast(ptr, from_class, to_class)); - } - - template < class_kind To, class_kind From > - [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { - const class_type& to_class = registry.resolve_type(); - const class_type& from_class = registry.resolve_type(); - return static_cast(pointer_upcast(ptr, from_class, to_class)); - } } namespace meta_hpp::detail @@ -5513,8 +5733,10 @@ namespace meta_hpp::detail const class_type& to_class = to.as_class(); const class_type& from_class = from.as_class(); - if ( to_class && from_class && from_class.is_derived_from(to_class) ) { - return pointer_upcast(ptr, from_class, to_class); + if ( to_class && from_class ) { + if ( void* base_ptr = pointer_upcast(ptr, from_class, to_class) ) { + return base_ptr; + } } return nullptr; @@ -5526,6 +5748,27 @@ namespace meta_hpp::detail } } +namespace meta_hpp::detail +{ + template < typename To, typename From > + [[nodiscard]] To* pointer_upcast(type_registry& registry, From* ptr) { + return static_cast(pointer_upcast( // + ptr, + registry.resolve_type(), + registry.resolve_type() + )); + } + + template < typename To, typename From > + [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { + return static_cast(pointer_upcast( // + ptr, + registry.resolve_type(), + registry.resolve_type() + )); + } +} + namespace meta_hpp::detail { class uarg_base { @@ -7792,8 +8035,12 @@ namespace meta_hpp return data_->argument_types; } - inline const class_set& class_type::get_bases() const noexcept { - return data_->bases; + inline const class_set& class_type::get_base_classes() const noexcept { + return data_->base_classes; + } + + inline const class_set& class_type::get_derived_classes() const noexcept { + return data_->derived_classes; } inline const constructor_set& class_type::get_constructors() const noexcept { @@ -7873,16 +8120,10 @@ namespace meta_hpp return false; } - if ( derived.data_->bases.contains(*this) ) { + if ( derived.data_->deep_upcasts.contains(*this) ) { return true; } - for ( const class_type& derived_base : derived.data_->bases ) { - if ( is_base_of(derived_base) ) { - return true; - } - } - return false; } @@ -7896,16 +8137,10 @@ namespace meta_hpp return false; } - if ( data_->bases.contains(base) ) { + if ( data_->deep_upcasts.contains(base) ) { return true; } - for ( const class_type& self_base : data_->bases ) { - if ( self_base.is_derived_from(base) ) { - return true; - } - } - return false; } @@ -7916,7 +8151,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const function& function = base.get_function(name) ) { return function; } @@ -7932,7 +8167,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const member& member = base.get_member(name) ) { return member; } @@ -7948,7 +8183,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const method& method = base.get_method(name) ) { return method; } @@ -7962,7 +8197,7 @@ namespace meta_hpp return iter->second; } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const any_type& type = base.get_typedef(name) ) { return type; } @@ -7978,7 +8213,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const variable& variable = base.get_variable(name) ) { return variable; } @@ -8050,7 +8285,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const function& function = base.get_function_with(name, first, last) ) { return function; } @@ -8090,7 +8325,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const method& method = base.get_method_with(name, first, last) ) { return method; } diff --git a/develop/unbench/cast_function_bench.cpp b/develop/unbench/cast_function_bench.cpp new file mode 100644 index 0000000..6dc0d8d --- /dev/null +++ b/develop/unbench/cast_function_bench.cpp @@ -0,0 +1,292 @@ +/******************************************************************************* + * 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 + +#include +#include + +#include + +namespace +{ + namespace meta = meta_hpp; + namespace vmath = vmath_hpp; + + volatile unsigned static_acc{}; + + struct base1 { + unsigned b1{1}; + + base1() = default; + virtual ~base1() = default; + + base1(const base1&) = default; + base1& operator=(const base1&) = default; + + RTTR_ENABLE() + }; + + struct base2 : base1 { + unsigned b2{2}; + RTTR_ENABLE(base1) + }; + + struct base3 : base2 { + unsigned b3{3}; + RTTR_ENABLE(base2) + }; + + struct base4 : base3 { + unsigned b4{4}; + RTTR_ENABLE(base3) + }; + + unsigned static_function_1(base1* b1) { + unsigned r = b1->b1; + static_acc = static_acc + r; + return r; + } + + unsigned static_function_2(base1* b1, base2* b2) { + unsigned r = b1->b1 + b2->b2; + static_acc = static_acc + r; + return r; + } + + unsigned static_function_3(base1* b1, base2* b2, base3* b3) { + unsigned r = b1->b1 + b2->b2 + b3->b3; + static_acc = static_acc + r; + return r; + } + + unsigned static_function_4(base1* b1, base2* b2, base3* b3, base4* b4) { + unsigned r = b1->b1 + b2->b2 + b3->b3 + b4->b4; + static_acc = static_acc + r; + return r; + } + + [[maybe_unused]] + void static_function_reset(const benchmark::State&) { + static_acc = 0; + } + + const bool registered = [](){ + meta::class_(); + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); + return true; + }(); + + meta::scope meta_bench_scope = meta::local_scope_("") + .function_("cast_function_1", &static_function_1) + .function_("cast_function_2", &static_function_2) + .function_("cast_function_3", &static_function_3) + .function_("cast_function_4", &static_function_4); +} + +RTTR_REGISTRATION +{ + using namespace rttr; + + registration::method("cast_function_1", &static_function_1); + registration::method("cast_function_2", &static_function_2); + registration::method("cast_function_3", &static_function_3); + registration::method("cast_function_4", &static_function_4); +} + +// +// native +// + +namespace +{ + [[maybe_unused]] + void cast_function_1(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + unsigned r = static_function_1(&b4); + META_HPP_ASSERT(r == 1); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void cast_function_2(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + unsigned r = static_function_2(&b4, &b4); + META_HPP_ASSERT(r == 3); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void cast_function_3(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + unsigned r = static_function_3(&b4, &b4, &b4); + META_HPP_ASSERT(r == 6); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void cast_function_4(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + unsigned r = static_function_4(&b4, &b4, &b4, &b4); + META_HPP_ASSERT(r == 10); + benchmark::DoNotOptimize(r); + } + } +} + +// +// meta +// + +namespace +{ + [[maybe_unused]] + void meta_cast_function_1(benchmark::State &state) { + meta::function f = meta_bench_scope.get_function("cast_function_1"); + META_HPP_ASSERT(f.is_valid()); + + base4 b4; + for ( auto _ : state ) { + unsigned r = f(&b4).as(); + META_HPP_ASSERT(r == 1); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void meta_cast_function_2(benchmark::State &state) { + meta::function f = meta_bench_scope.get_function("cast_function_2"); + META_HPP_ASSERT(f.is_valid()); + + base4 b4; + for ( auto _ : state ) { + unsigned r = f(&b4, &b4).as(); + META_HPP_ASSERT(r == 3); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void meta_cast_function_3(benchmark::State &state) { + meta::function f = meta_bench_scope.get_function("cast_function_3"); + META_HPP_ASSERT(f.is_valid()); + + base4 b4; + for ( auto _ : state ) { + unsigned r = f(&b4, &b4, &b4).as(); + META_HPP_ASSERT(r == 6); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void meta_cast_function_4(benchmark::State &state) { + meta::function f = meta_bench_scope.get_function("cast_function_4"); + META_HPP_ASSERT(f.is_valid()); + + base4 b4; + for ( auto _ : state ) { + unsigned r = f(&b4, &b4, &b4, &b4).as(); + META_HPP_ASSERT(r == 10); + benchmark::DoNotOptimize(r); + } + } +} + +// +// rttr +// + +namespace +{ + [[maybe_unused]] + void rttr_cast_function_1(benchmark::State &state) { + rttr::method m = rttr::type::get_global_method("cast_function_1"); + META_HPP_ASSERT(m.is_valid()); + + base4 b4; + for ( auto _ : state ) { + unsigned r = m.invoke({}, + rttr::rttr_cast(&b4)).get_value(); + META_HPP_ASSERT(r == 1); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void rttr_cast_function_2(benchmark::State &state) { + rttr::method m = rttr::type::get_global_method("cast_function_2"); + META_HPP_ASSERT(m.is_valid()); + + base4 b4; + for ( auto _ : state ) { + unsigned r = m.invoke({}, + rttr::rttr_cast(&b4), + rttr::rttr_cast(&b4)).get_value(); + META_HPP_ASSERT(r == 3); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void rttr_cast_function_3(benchmark::State &state) { + rttr::method m = rttr::type::get_global_method("cast_function_3"); + META_HPP_ASSERT(m.is_valid()); + + base4 b4; + for ( auto _ : state ) { + unsigned r = m.invoke({}, + rttr::rttr_cast(&b4), + rttr::rttr_cast(&b4), + rttr::rttr_cast(&b4)).get_value(); + META_HPP_ASSERT(r == 6); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void rttr_cast_function_4(benchmark::State &state) { + rttr::method m = rttr::type::get_global_method("cast_function_4"); + META_HPP_ASSERT(m.is_valid()); + + base4 b4; + for ( auto _ : state ) { + unsigned r = m.invoke({}, + rttr::rttr_cast(&b4), + rttr::rttr_cast(&b4), + rttr::rttr_cast(&b4), + rttr::rttr_cast(&b4)).get_value(); + META_HPP_ASSERT(r == 10); + benchmark::DoNotOptimize(r); + } + } +} + +BENCHMARK(cast_function_1)->Teardown(static_function_reset); +BENCHMARK(meta_cast_function_1)->Teardown(static_function_reset); +BENCHMARK(rttr_cast_function_1)->Teardown(static_function_reset); + +BENCHMARK(cast_function_2)->Teardown(static_function_reset); +BENCHMARK(meta_cast_function_2)->Teardown(static_function_reset); +BENCHMARK(rttr_cast_function_2)->Teardown(static_function_reset); + +BENCHMARK(cast_function_3)->Teardown(static_function_reset); +BENCHMARK(meta_cast_function_3)->Teardown(static_function_reset); +BENCHMARK(rttr_cast_function_3)->Teardown(static_function_reset); + +BENCHMARK(cast_function_4)->Teardown(static_function_reset); +BENCHMARK(meta_cast_function_4)->Teardown(static_function_reset); +BENCHMARK(rttr_cast_function_4)->Teardown(static_function_reset); diff --git a/develop/unbench/invoke_function_bench.cpp b/develop/unbench/invoke_function_bench.cpp index 2d58ef2..2f13f0c 100644 --- a/develop/unbench/invoke_function_bench.cpp +++ b/develop/unbench/invoke_function_bench.cpp @@ -45,28 +45,29 @@ namespace static_angle = static_angle + 0.00001f; } + [[maybe_unused]] void static_function_reset(const benchmark::State&) { static_acc = 0.f; static_angle = 0.f; } meta::scope meta_bench_scope = meta::local_scope_("") - .function_("static_function_0", &static_function_0) - .function_("static_function_1", &static_function_1) - .function_("static_function_2", &static_function_2) - .function_("static_function_3", &static_function_3) - .function_("static_function_4", &static_function_4); + .function_("invoke_function_0", &static_function_0) + .function_("invoke_function_1", &static_function_1) + .function_("invoke_function_2", &static_function_2) + .function_("invoke_function_3", &static_function_3) + .function_("invoke_function_4", &static_function_4); } RTTR_REGISTRATION { using namespace rttr; - registration::method("static_function_0", &static_function_0); - registration::method("static_function_1", &static_function_1); - registration::method("static_function_2", &static_function_2); - registration::method("static_function_3", &static_function_3); - registration::method("static_function_4", &static_function_4); + registration::method("invoke_function_0", &static_function_0); + registration::method("invoke_function_1", &static_function_1); + registration::method("invoke_function_2", &static_function_2); + registration::method("invoke_function_3", &static_function_3); + registration::method("invoke_function_4", &static_function_4); } // @@ -119,7 +120,7 @@ namespace { [[maybe_unused]] void meta_invoke_function_0(benchmark::State &state) { - meta::function f = meta_bench_scope.get_function("static_function_0"); + meta::function f = meta_bench_scope.get_function("invoke_function_0"); for ( auto _ : state ) { f(); } @@ -127,7 +128,7 @@ namespace [[maybe_unused]] void meta_invoke_function_1(benchmark::State &state) { - meta::function f = meta_bench_scope.get_function("static_function_1"); + meta::function f = meta_bench_scope.get_function("invoke_function_1"); META_HPP_ASSERT(f.is_valid()); for ( auto _ : state ) { @@ -137,7 +138,7 @@ namespace [[maybe_unused]] void meta_invoke_function_2(benchmark::State &state) { - meta::function f = meta_bench_scope.get_function("static_function_2"); + meta::function f = meta_bench_scope.get_function("invoke_function_2"); META_HPP_ASSERT(f.is_valid()); for ( auto _ : state ) { @@ -147,7 +148,7 @@ namespace [[maybe_unused]] void meta_invoke_function_3(benchmark::State &state) { - meta::function f = meta_bench_scope.get_function("static_function_3"); + meta::function f = meta_bench_scope.get_function("invoke_function_3"); META_HPP_ASSERT(f.is_valid()); for ( auto _ : state ) { @@ -157,7 +158,7 @@ namespace [[maybe_unused]] void meta_invoke_function_4(benchmark::State &state) { - meta::function f = meta_bench_scope.get_function("static_function_4"); + meta::function f = meta_bench_scope.get_function("invoke_function_4"); META_HPP_ASSERT(f.is_valid()); for ( auto _ : state ) { @@ -174,7 +175,7 @@ namespace { [[maybe_unused]] void rttr_invoke_function_0(benchmark::State &state) { - rttr::method m = rttr::type::get_global_method("static_function_0"); + rttr::method m = rttr::type::get_global_method("invoke_function_0"); META_HPP_ASSERT(m.is_valid()); for ( auto _ : state ) { @@ -184,7 +185,7 @@ namespace [[maybe_unused]] void rttr_invoke_function_1(benchmark::State &state) { - rttr::method m = rttr::type::get_global_method("static_function_1"); + rttr::method m = rttr::type::get_global_method("invoke_function_1"); META_HPP_ASSERT(m.is_valid()); for ( auto _ : state ) { @@ -194,7 +195,7 @@ namespace [[maybe_unused]] void rttr_invoke_function_2(benchmark::State &state) { - rttr::method m = rttr::type::get_global_method("static_function_2"); + rttr::method m = rttr::type::get_global_method("invoke_function_2"); META_HPP_ASSERT(m.is_valid()); for ( auto _ : state ) { @@ -204,7 +205,7 @@ namespace [[maybe_unused]] void rttr_invoke_function_3(benchmark::State &state) { - rttr::method m = rttr::type::get_global_method("static_function_3"); + rttr::method m = rttr::type::get_global_method("invoke_function_3"); META_HPP_ASSERT(m.is_valid()); for ( auto _ : state ) { @@ -214,7 +215,7 @@ namespace [[maybe_unused]] void rttr_invoke_function_4(benchmark::State &state) { - rttr::method m = rttr::type::get_global_method("static_function_4"); + rttr::method m = rttr::type::get_global_method("invoke_function_4"); META_HPP_ASSERT(m.is_valid()); for ( auto _ : state ) { diff --git a/develop/untests/meta_features/multiple2_tests.cpp b/develop/untests/meta_features/multiple2_tests.cpp new file mode 100644 index 0000000..88cc309 --- /dev/null +++ b/develop/untests/meta_features/multiple2_tests.cpp @@ -0,0 +1,204 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/meta.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2021-2023, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include +#include + +namespace +{ + struct A { std::string a{"a"}; }; + struct B0 : virtual A { std::string b0{"b0"}; }; + struct B1 : virtual A { std::string b1{"b1"}; }; + + struct C : B0, B1 { std::string c{"c"}; }; + + struct D0 : C { std::string d0{"d0"}; }; + struct E0 : D0 { std::string e0{"e0"}; }; + + struct D1 : C { std::string d1{"d1"}; }; + struct E1 : D1 { std::string e1{"e1"}; }; +} + +TEST_CASE("meta/meta_features/multiple2/_") { + namespace meta = meta_hpp; + + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); + + meta::class_().base_(); + meta::class_().base_(); + + meta::class_().base_(); + meta::class_().base_(); + + // * <- B0 <- * * <- D0 * <- E0 + // A C + // * <- B1 <- * * <- D1 * <- E1 +} + +TEST_CASE("meta/meta_features/multiple2") { + namespace meta = meta_hpp; + + const meta::class_type A_type = meta::resolve_type(); + const meta::class_type B0_type = meta::resolve_type(); + const meta::class_type B1_type = meta::resolve_type(); + const meta::class_type C_type = meta::resolve_type(); + const meta::class_type D0_type = meta::resolve_type(); + const meta::class_type D1_type = meta::resolve_type(); + const meta::class_type E0_type = meta::resolve_type(); + const meta::class_type E1_type = meta::resolve_type(); + + REQUIRE(A_type); + REQUIRE(B0_type); + REQUIRE(B1_type); + REQUIRE(C_type); + REQUIRE(D0_type); + REQUIRE(D1_type); + REQUIRE(E0_type); + REQUIRE(E1_type); + + SUBCASE("is_base_of") { + CHECK(!A_type.is_base_of(A_type)); + CHECK(A_type.is_base_of(B0_type)); + CHECK(A_type.is_base_of(B1_type)); + CHECK(A_type.is_base_of(C_type)); + CHECK(A_type.is_base_of(D0_type)); + CHECK(A_type.is_base_of(D1_type)); + CHECK(A_type.is_base_of(E0_type)); + CHECK(A_type.is_base_of(E1_type)); + + CHECK(!B0_type.is_base_of(A_type)); + CHECK(!B0_type.is_base_of(B0_type)); + CHECK(!B0_type.is_base_of(B1_type)); + CHECK(B0_type.is_base_of(C_type)); + CHECK(B0_type.is_base_of(D0_type)); + CHECK(B0_type.is_base_of(D1_type)); + CHECK(B0_type.is_base_of(E0_type)); + CHECK(B0_type.is_base_of(E1_type)); + + CHECK(!B1_type.is_base_of(A_type)); + CHECK(!B1_type.is_base_of(B0_type)); + CHECK(!B1_type.is_base_of(B1_type)); + CHECK(B1_type.is_base_of(C_type)); + CHECK(B1_type.is_base_of(D0_type)); + CHECK(B1_type.is_base_of(D1_type)); + CHECK(B1_type.is_base_of(E0_type)); + CHECK(B1_type.is_base_of(E1_type)); + + CHECK(!C_type.is_base_of(A_type)); + CHECK(!C_type.is_base_of(B0_type)); + CHECK(!C_type.is_base_of(B1_type)); + CHECK(!C_type.is_base_of(C_type)); + CHECK(C_type.is_base_of(D0_type)); + CHECK(C_type.is_base_of(D1_type)); + CHECK(C_type.is_base_of(E0_type)); + CHECK(C_type.is_base_of(E1_type)); + + CHECK(!D0_type.is_base_of(A_type)); + CHECK(!D0_type.is_base_of(B0_type)); + CHECK(!D0_type.is_base_of(B1_type)); + CHECK(!D0_type.is_base_of(C_type)); + CHECK(!D0_type.is_base_of(D0_type)); + CHECK(!D0_type.is_base_of(D1_type)); + CHECK(D0_type.is_base_of(E0_type)); + CHECK(!D0_type.is_base_of(E1_type)); + + CHECK(!D1_type.is_base_of(A_type)); + CHECK(!D1_type.is_base_of(B0_type)); + CHECK(!D1_type.is_base_of(B1_type)); + CHECK(!D1_type.is_base_of(C_type)); + CHECK(!D1_type.is_base_of(D0_type)); + CHECK(!D1_type.is_base_of(D1_type)); + CHECK(!D1_type.is_base_of(E0_type)); + CHECK(D1_type.is_base_of(E1_type)); + + CHECK(!E0_type.is_base_of(A_type)); + CHECK(!E0_type.is_base_of(B0_type)); + CHECK(!E0_type.is_base_of(B1_type)); + CHECK(!E0_type.is_base_of(C_type)); + CHECK(!E0_type.is_base_of(D0_type)); + CHECK(!E0_type.is_base_of(D1_type)); + CHECK(!E0_type.is_base_of(E0_type)); + CHECK(!E0_type.is_base_of(E1_type)); + + CHECK(!E1_type.is_base_of(A_type)); + CHECK(!E1_type.is_base_of(B0_type)); + CHECK(!E1_type.is_base_of(B1_type)); + CHECK(!E1_type.is_base_of(C_type)); + CHECK(!E1_type.is_base_of(D0_type)); + CHECK(!E1_type.is_base_of(D1_type)); + CHECK(!E1_type.is_base_of(E0_type)); + CHECK(!E1_type.is_base_of(E1_type)); + } + + SUBCASE("pointer_upcast") { + using meta::detail::type_registry; + using meta::detail::pointer_upcast; + + type_registry& r{type_registry::instance()}; + + { + A a; + CHECK(pointer_upcast(r, &a) == &a); CHECK(pointer_upcast(r, &a)->a == "a"); + CHECK_FALSE(pointer_upcast(r, &a)); + CHECK_FALSE(pointer_upcast(r, &a)); + CHECK_FALSE(pointer_upcast(r, &a)); + } + + { + B0 b0; + CHECK(pointer_upcast(r, &b0) == &b0); CHECK(pointer_upcast(r, &b0)->a == "a"); + CHECK(pointer_upcast(r, &b0) == &b0); CHECK(pointer_upcast(r, &b0)->b0 == "b0"); + CHECK_FALSE(pointer_upcast(r, &b0)); + CHECK_FALSE(pointer_upcast(r, &b0)); + } + + { + B1 b1; + CHECK(pointer_upcast(r, &b1) == &b1); CHECK(pointer_upcast(r, &b1)->a == "a"); + CHECK_FALSE(pointer_upcast(r, &b1)); + CHECK(pointer_upcast(r, &b1) == &b1); CHECK(pointer_upcast(r, &b1)->b1 == "b1"); + CHECK_FALSE(pointer_upcast(r, &b1)); + } + + { + C c; + CHECK(pointer_upcast(r, &c) == &c); CHECK(pointer_upcast(r, &c)->a == "a"); + CHECK(pointer_upcast(r, &c) == &c); CHECK(pointer_upcast(r, &c)->b0 == "b0"); + CHECK(pointer_upcast(r, &c) == &c); CHECK(pointer_upcast(r, &c)->b1 == "b1"); + CHECK(pointer_upcast(r, &c) == &c); CHECK(pointer_upcast(r, &c)->c == "c"); + CHECK_FALSE(pointer_upcast(r, &c)); + CHECK_FALSE(pointer_upcast(r, &c)); + CHECK_FALSE(pointer_upcast(r, &c)); + CHECK_FALSE(pointer_upcast(r, &c)); + } + + { + E0 e0; + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->a == "a"); + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->b0 == "b0"); + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->b1 == "b1"); + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->c == "c"); + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->d0 == "d0"); + CHECK_FALSE(pointer_upcast(r, &e0)); + CHECK(pointer_upcast(r, &e0) == &e0); CHECK(pointer_upcast(r, &e0)->e0 == "e0"); + CHECK_FALSE(pointer_upcast(r, &e0)); + } + + { + E1 e1; + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->a == "a"); + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->b0 == "b0"); + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->b1 == "b1"); + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->c == "c"); + CHECK_FALSE(pointer_upcast(r, &e1)); + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->d1 == "d1"); + CHECK_FALSE(pointer_upcast(r, &e1)); + CHECK(pointer_upcast(r, &e1) == &e1); CHECK(pointer_upcast(r, &e1)->e1 == "e1"); + } + } +} diff --git a/develop/untests/meta_features/multiple_tests.cpp b/develop/untests/meta_features/multiple_tests.cpp new file mode 100644 index 0000000..25481f8 --- /dev/null +++ b/develop/untests/meta_features/multiple_tests.cpp @@ -0,0 +1,214 @@ +/******************************************************************************* + * 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 { + A() = default; + + A(A&&) = default; + A(const A&) = default; + + A& operator=(A&&) = delete; + A& operator=(const A&) = delete; + + virtual ~A() = default; + }; + + struct B : A {}; + struct C : A {}; + struct D : B, C {}; + struct E : D {}; + struct F {}; +} + +TEST_CASE("meta/meta_features/multiple/_") { + namespace meta = meta_hpp; + + meta::class_().base_(); + meta::class_().base_(); + + meta::class_().base_(); + meta::class_().base_(); + + // A * <- B <- * + // D * <- E + // A * <- C <- * +} + +TEST_CASE("meta/meta_features/multiple") { + namespace meta = meta_hpp; + using meta::detail::uarg; + using meta::detail::uinst; + using meta::detail::type_registry; + type_registry& r{type_registry::instance()}; + + const meta::class_type A_type = meta::resolve_type(); + const meta::class_type B_type = meta::resolve_type(); + const meta::class_type C_type = meta::resolve_type(); + const meta::class_type D_type = meta::resolve_type(); + const meta::class_type E_type = meta::resolve_type(); + const meta::class_type F_type = meta::resolve_type(); + + REQUIRE(A_type); + REQUIRE(B_type); + REQUIRE(C_type); + REQUIRE(D_type); + REQUIRE(E_type); + REQUIRE(F_type); + + SUBCASE("is_base_of") { + CHECK(!A_type.is_base_of(A_type)); + CHECK(A_type.is_base_of(B_type)); + CHECK(A_type.is_base_of(C_type)); + CHECK(A_type.is_base_of(D_type)); + CHECK(A_type.is_base_of(E_type)); + CHECK(!A_type.is_base_of(F_type)); + + CHECK(!B_type.is_base_of(A_type)); + CHECK(!B_type.is_base_of(B_type)); + CHECK(!B_type.is_base_of(C_type)); + CHECK(B_type.is_base_of(D_type)); + CHECK(B_type.is_base_of(E_type)); + CHECK(!B_type.is_base_of(F_type)); + + CHECK(!C_type.is_base_of(A_type)); + CHECK(!C_type.is_base_of(B_type)); + CHECK(!C_type.is_base_of(C_type)); + CHECK(C_type.is_base_of(D_type)); + CHECK(C_type.is_base_of(E_type)); + CHECK(!C_type.is_base_of(F_type)); + + CHECK(!D_type.is_base_of(A_type)); + CHECK(!D_type.is_base_of(B_type)); + CHECK(!D_type.is_base_of(C_type)); + CHECK(!D_type.is_base_of(D_type)); + CHECK(D_type.is_base_of(E_type)); + CHECK(!D_type.is_base_of(F_type)); + + CHECK(!E_type.is_base_of(A_type)); + CHECK(!E_type.is_base_of(B_type)); + CHECK(!E_type.is_base_of(C_type)); + CHECK(!E_type.is_base_of(D_type)); + CHECK(!E_type.is_base_of(E_type)); + CHECK(!E_type.is_base_of(F_type)); + + CHECK(!F_type.is_base_of(A_type)); + CHECK(!F_type.is_base_of(B_type)); + CHECK(!F_type.is_base_of(C_type)); + CHECK(!F_type.is_base_of(D_type)); + CHECK(!F_type.is_base_of(E_type)); + CHECK(!F_type.is_base_of(F_type)); + } + + SUBCASE("is_derived_from") { + CHECK(!A_type.is_derived_from(A_type)); + CHECK(!A_type.is_derived_from(B_type)); + CHECK(!A_type.is_derived_from(C_type)); + CHECK(!A_type.is_derived_from(D_type)); + CHECK(!A_type.is_derived_from(E_type)); + CHECK(!A_type.is_derived_from(F_type)); + + CHECK(B_type.is_derived_from(A_type)); + CHECK(!B_type.is_derived_from(B_type)); + CHECK(!B_type.is_derived_from(C_type)); + CHECK(!B_type.is_derived_from(D_type)); + CHECK(!B_type.is_derived_from(E_type)); + CHECK(!B_type.is_derived_from(F_type)); + + CHECK(C_type.is_derived_from(A_type)); + CHECK(!C_type.is_derived_from(B_type)); + CHECK(!C_type.is_derived_from(C_type)); + CHECK(!C_type.is_derived_from(D_type)); + CHECK(!C_type.is_derived_from(E_type)); + CHECK(!C_type.is_derived_from(F_type)); + + CHECK(D_type.is_derived_from(A_type)); + CHECK(D_type.is_derived_from(B_type)); + CHECK(D_type.is_derived_from(C_type)); + CHECK(!D_type.is_derived_from(D_type)); + CHECK(!D_type.is_derived_from(E_type)); + CHECK(!D_type.is_derived_from(F_type)); + + CHECK(E_type.is_derived_from(A_type)); + CHECK(E_type.is_derived_from(B_type)); + CHECK(E_type.is_derived_from(C_type)); + CHECK(E_type.is_derived_from(D_type)); + CHECK(!E_type.is_derived_from(E_type)); + CHECK(!E_type.is_derived_from(F_type)); + + CHECK(!F_type.is_derived_from(A_type)); + CHECK(!F_type.is_derived_from(B_type)); + CHECK(!F_type.is_derived_from(C_type)); + CHECK(!F_type.is_derived_from(D_type)); + CHECK(!F_type.is_derived_from(F_type)); + } + + SUBCASE("pointer_upcast") { + using meta::detail::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)); + } + { + 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)); + } + { + 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)); + } + { + 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)); + } + { + 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)); + } + { + 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); + } + } +} diff --git a/develop/untests/meta_types/class_type_tests.cpp b/develop/untests/meta_types/class_type_tests.cpp index a68c25e..7ab1db3 100644 --- a/develop/untests/meta_types/class_type_tests.cpp +++ b/develop/untests/meta_types/class_type_tests.cpp @@ -202,11 +202,18 @@ TEST_CASE("meta/meta_types/class_type") { CHECK(final_derived_clazz_type.get_constructors().size() == 1); } - SUBCASE("get_bases") { - CHECK(base_clazz_1_type.get_bases() == meta::class_set{}); - CHECK(base_clazz_2_type.get_bases() == meta::class_set{}); - CHECK(derived_clazz_type.get_bases() == meta::class_set{base_clazz_1_type, base_clazz_2_type}); - CHECK(final_derived_clazz_type.get_bases() == meta::class_set{derived_clazz_type}); + SUBCASE("get_base_classes") { + CHECK(base_clazz_1_type.get_base_classes() == meta::class_set{}); + CHECK(base_clazz_2_type.get_base_classes() == meta::class_set{}); + CHECK(derived_clazz_type.get_base_classes() == meta::class_set{base_clazz_1_type, base_clazz_2_type}); + CHECK(final_derived_clazz_type.get_base_classes() == meta::class_set{derived_clazz_type}); + } + + SUBCASE("get_derived_classes") { + CHECK(base_clazz_1_type.get_derived_classes() == meta::class_set{derived_clazz_type}); + CHECK(base_clazz_2_type.get_derived_classes() == meta::class_set{derived_clazz_type}); + CHECK(derived_clazz_type.get_derived_classes() == meta::class_set{final_derived_clazz_type}); + CHECK(final_derived_clazz_type.get_derived_classes() == meta::class_set{}); } SUBCASE("get_functions") { diff --git a/headers/meta.hpp/meta_base.hpp b/headers/meta.hpp/meta_base.hpp index db7104e..8aceaf6 100644 --- a/headers/meta.hpp/meta_base.hpp +++ b/headers/meta.hpp/meta_base.hpp @@ -10,6 +10,7 @@ #include "meta_base/bitflags.hpp" #include "meta_base/cv_traits.hpp" #include "meta_base/cvref_traits.hpp" +#include "meta_base/defer.hpp" #include "meta_base/exceptions.hpp" #include "meta_base/fixed_function.hpp" #include "meta_base/fnv1a_hash.hpp" diff --git a/headers/meta.hpp/meta_base/base.hpp b/headers/meta.hpp/meta_base/base.hpp index 07c9f06..5cae5f2 100644 --- a/headers/meta.hpp/meta_base/base.hpp +++ b/headers/meta.hpp/meta_base/base.hpp @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -41,20 +45,15 @@ # define META_HPP_NO_RTTI #endif -#if !defined(META_HPP_NO_EXCEPTIONS) -# include -#endif - -#if !defined(META_HPP_NO_RTTI) -# include -# include -#endif - #if !defined(META_HPP_FWD) # define META_HPP_FWD(v) std::forward(v) #endif #if !defined(META_HPP_ASSERT) -# include # define META_HPP_ASSERT(...) assert(__VA_ARGS__) // NOLINT #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 +#endif diff --git a/headers/meta.hpp/meta_base/defer.hpp b/headers/meta.hpp/meta_base/defer.hpp new file mode 100644 index 0000000..7764070 --- /dev/null +++ b/headers/meta.hpp/meta_base/defer.hpp @@ -0,0 +1,144 @@ +/******************************************************************************* + * 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::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 +{ + template < typename F, typename... Args > + class defer final : public impl::defer_impl { + public: + defer() = delete; + defer(defer&&) = delete; + defer(const defer&) = delete; + defer& operator=(defer&&) = delete; + defer& operator=(const defer&) = delete; + + template < typename UF > + explicit defer(UF&& f, std::tuple&& args) + : impl::defer_impl{std::forward(f), std::move(args)} {} + + ~defer() noexcept = default; + }; + + template < typename F, typename... Args > + class error_defer final : public impl::defer_impl { + public: + error_defer() = delete; + error_defer(error_defer&&) = delete; + error_defer(const error_defer&) = delete; + error_defer& operator=(error_defer&&) = delete; + error_defer& operator=(const error_defer&) = delete; + + template < typename UF > + explicit error_defer(UF&& f, std::tuple&& args) + : impl::defer_impl{std::forward(f), std::move(args)} + , exceptions_{std::uncaught_exceptions()} {} + + ~error_defer() noexcept { + if ( exceptions_ == std::uncaught_exceptions() ) { + this->dismiss(); + } + } + + private: + int exceptions_{}; + }; + + template < typename F, typename... Args > + class return_defer final : public impl::defer_impl { + public: + return_defer() = delete; + return_defer(return_defer&&) = delete; + return_defer(const return_defer&) = delete; + return_defer& operator=(return_defer&&) = delete; + return_defer& operator=(const return_defer&) = delete; + + template < typename UF > + explicit return_defer(UF&& f, std::tuple&& args) + : impl::defer_impl{std::forward(f), std::move(args)} + , exceptions_{std::uncaught_exceptions()} {} + + ~return_defer() noexcept { + if ( exceptions_ != std::uncaught_exceptions() ) { + this->dismiss(); + } + } + + private: + int exceptions_{}; + }; + + template < typename F, typename... Args > + auto make_defer(F&& f, Args&&... args) { + using defer_t = defer, std::decay_t...>; + return defer_t{std::forward(f), std::make_tuple(std::forward(args)...)}; + } + + template < typename F, typename... Args > + auto make_error_defer(F&& f, Args&&... args) { + using defer_t = error_defer, std::decay_t...>; + return defer_t{std::forward(f), std::make_tuple(std::forward(args)...)}; + } + + template < typename F, typename... Args > + auto make_return_defer(F&& f, Args&&... args) { + using defer_t = return_defer, std::decay_t...>; + return defer_t{std::forward(f), std::make_tuple(std::forward(args)...)}; + } +} + +#ifdef __COUNTER__ +# define META_HPP_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_defer_, __COUNTER__) { ::meta_hpp::detail::make_defer(__VA_ARGS__) } +# define META_HPP_ERROR_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_error_defer_, __COUNTER__) { ::meta_hpp::detail::make_error_defer(__VA_ARGS__) } +# define META_HPP_RETURN_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_return_defer_, __COUNTER__) { ::meta_hpp::detail::make_return_defer(__VA_ARGS__) } +#else +# define META_HPP_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_defer_, __LINE__) { ::meta_hpp::detail::make_defer(__VA_ARGS__) } +# define META_HPP_ERROR_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_error_defer_, __LINE__) { ::meta_hpp::detail::make_error_defer(__VA_ARGS__) } +# define META_HPP_RETURN_DEFER(...) \ + auto META_HPP_PP_CAT(meta_hpp_generated_return_defer_, __LINE__) { ::meta_hpp::detail::make_return_defer(__VA_ARGS__) } +#endif diff --git a/headers/meta.hpp/meta_binds/class_bind.hpp b/headers/meta.hpp/meta_binds/class_bind.hpp index 8515490..dcbaf52 100644 --- a/headers/meta.hpp/meta_binds/class_bind.hpp +++ b/headers/meta.hpp/meta_binds/class_bind.hpp @@ -10,6 +10,73 @@ #include "../meta_binds.hpp" #include "../meta_registry.hpp" +namespace meta_hpp::detail::class_bind_impl +{ + using base_upcasts_t = class_type_data::base_upcasts_t; + using deep_upcasts_t = class_type_data::deep_upcasts_t; + + using upcast_func_t = class_type_data::upcast_func_t; + using upcast_func_list_t = class_type_data::upcast_func_list_t; + + using new_bases_db_t = std::map; + using deep_upcasts_db_t = std::map>; + using derived_classes_db_t = std::map>; + + template < class_kind Class, class_kind Base > + requires detail::class_bind_base_kind + void update_new_bases_db( // + new_bases_db_t& new_bases_db + ) { + new_bases_db.emplace(resolve_type(), [](void* from) { + return static_cast(static_cast(static_cast(from))); + }); + } + + inline void update_deep_upcasts_db( // + const class_type& derived_class, + const class_type& new_base_class, + const upcast_func_list_t& derived_to_new_base, + deep_upcasts_db_t& deep_upcasts_db + ) { + const class_type_data& derived_class_data = *type_access(derived_class); + const class_type_data& new_base_class_data = *type_access(new_base_class); + + const auto [deep_upcasts_db_iter, _] = deep_upcasts_db.try_emplace(derived_class, derived_class_data.deep_upcasts); + deep_upcasts_t& derived_deep_upcasts = deep_upcasts_db_iter->second; + derived_deep_upcasts.emplace(new_base_class, derived_to_new_base); + + for ( auto&& [new_deep_class, new_base_to_deep] : new_base_class_data.deep_upcasts ) { + upcast_func_list_t derived_to_new_deep; + derived_to_new_deep.reserve(derived_to_new_base.size() + new_base_to_deep.size()); + derived_to_new_deep.insert(derived_to_new_deep.end(), derived_to_new_base.begin(), derived_to_new_base.end()); + derived_to_new_deep.insert(derived_to_new_deep.end(), new_base_to_deep.begin(), new_base_to_deep.end()); + derived_deep_upcasts.emplace(new_deep_class, std::move(derived_to_new_deep)); + } + + for ( const class_type& subderived_class : derived_class_data.derived_classes ) { + const class_type_data& subderived_data = *type_access(subderived_class); + + upcast_func_list_t subderived_to_new_base; + subderived_to_new_base.reserve(derived_to_new_base.size() + 1); + subderived_to_new_base.insert(subderived_to_new_base.end(), subderived_data.base_upcasts.at(derived_class)); + subderived_to_new_base.insert(subderived_to_new_base.end(), derived_to_new_base.begin(), derived_to_new_base.end()); + + update_deep_upcasts_db(subderived_class, new_base_class, subderived_to_new_base, deep_upcasts_db); + } + } + + inline void updata_derived_classes_db( // + const class_type& self_class, + const class_type& new_base_class, + derived_classes_db_t& derived_classes_db + ) { + const class_type_data& base_class_data = *type_access(new_base_class); + class_set new_derived_classes{base_class_data.derived_classes}; + new_derived_classes.emplace(self_class); + derived_classes_db.emplace(new_base_class, std::move(new_derived_classes)); + } +} + namespace meta_hpp { template < detail::class_kind Class > @@ -28,28 +95,47 @@ namespace meta_hpp template < detail::class_kind... Bases > requires(... && detail::class_bind_base_kind) class_bind& class_bind::base_() { - const auto register_base{[this](std::in_place_type_t) { - const class_type& base_type = resolve_type(); + using namespace detail; + using namespace detail::class_bind_impl; - auto&& [position, emplaced] = get_data().bases.emplace(base_type); - if ( !emplaced ) { - return; + if ( (... && get_data().base_classes.contains(resolve_type())) ) { + return *this; + } + + new_bases_db_t new_bases_db; + (update_new_bases_db(new_bases_db), ...); + + deep_upcasts_db_t deep_upcasts_db; + derived_classes_db_t derived_classes_db; + + class_set new_base_classes{get_data().base_classes}; + base_upcasts_t new_base_upcasts{get_data().base_upcasts}; + + for ( auto&& [new_base_class, self_to_new_base] : new_bases_db ) { + if ( new_base_classes.contains(new_base_class) ) { + continue; } - META_HPP_TRY { - get_data().bases_info.emplace( // - base_type, - detail::class_type_data::base_info{ - .upcast{[](void* derived) -> void* { return static_cast(static_cast(derived)); }}} - ); - } - META_HPP_CATCH(...) { - get_data().bases.erase(position); - META_HPP_RETHROW(); - } - }}; + update_deep_upcasts_db(*this, new_base_class, {self_to_new_base}, deep_upcasts_db); + updata_derived_classes_db(*this, new_base_class, derived_classes_db); + + new_base_classes.emplace(new_base_class); + new_base_upcasts.emplace(new_base_class, self_to_new_base); + } + + get_data().base_classes.swap(new_base_classes); + get_data().base_upcasts.swap(new_base_upcasts); + + for ( auto&& [derived_class, new_deep_upcasts] : deep_upcasts_db ) { + class_type_data& derived_data = *type_access(derived_class); + derived_data.deep_upcasts.swap(new_deep_upcasts); + } + + for ( auto&& [base_class, new_derived_classes] : derived_classes_db ) { + class_type_data& base_data = *type_access(base_class); + base_data.derived_classes.swap(new_derived_classes); + } - (register_base(std::in_place_type), ...); return *this; } diff --git a/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp b/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp index 57e0f6f..8d8ed5a 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp @@ -124,37 +124,34 @@ namespace meta_hpp::detail return ptr; } - for ( auto&& [base_type, base_info] : type_access(from)->bases_info ) { - if ( base_type == to ) { - return base_info.upcast(ptr); - } + void* base_ptr = nullptr; - if ( base_type.is_derived_from(to) ) { - return pointer_upcast(base_info.upcast(ptr), base_type, to); + class_type_data& from_data = *type_access(from); + class_type_data::deep_upcasts_t& deep_upcasts = from_data.deep_upcasts; + + for ( auto iter{deep_upcasts.lower_bound(to)}; iter != deep_upcasts.end() && iter->first == to; ++iter ) { + void* new_base_ptr = [ptr, iter]() mutable { + for ( class_type_data::upcast_func_t upcast : iter->second ) { + ptr = upcast(ptr); + } + return ptr; + }(); + + if ( base_ptr == nullptr ) { + base_ptr = new_base_ptr; + } else if ( base_ptr != new_base_ptr ) { + // ambiguous conversions + return nullptr; } } - return nullptr; + return base_ptr; } [[nodiscard]] inline const void* pointer_upcast(const void* ptr, const class_type& from, const class_type& to) { // NOLINTNEXTLINE(*-const-cast) return pointer_upcast(const_cast(ptr), from, to); } - - template < class_kind To, class_kind From > - [[nodiscard]] To* pointer_upcast(type_registry& registry, From* ptr) { - const class_type& to_class = registry.resolve_type(); - const class_type& from_class = registry.resolve_type(); - return static_cast(pointer_upcast(ptr, from_class, to_class)); - } - - template < class_kind To, class_kind From > - [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { - const class_type& to_class = registry.resolve_type(); - const class_type& from_class = registry.resolve_type(); - return static_cast(pointer_upcast(ptr, from_class, to_class)); - } } namespace meta_hpp::detail @@ -171,8 +168,10 @@ namespace meta_hpp::detail const class_type& to_class = to.as_class(); const class_type& from_class = from.as_class(); - if ( to_class && from_class && from_class.is_derived_from(to_class) ) { - return pointer_upcast(ptr, from_class, to_class); + if ( to_class && from_class ) { + if ( void* base_ptr = pointer_upcast(ptr, from_class, to_class) ) { + return base_ptr; + } } return nullptr; @@ -183,3 +182,24 @@ namespace meta_hpp::detail return pointer_upcast(const_cast(ptr), from, to); } } + +namespace meta_hpp::detail +{ + template < typename To, typename From > + [[nodiscard]] To* pointer_upcast(type_registry& registry, From* ptr) { + return static_cast(pointer_upcast( // + ptr, + registry.resolve_type(), + registry.resolve_type() + )); + } + + template < typename To, typename From > + [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { + return static_cast(pointer_upcast( // + ptr, + registry.resolve_type(), + registry.resolve_type() + )); + } +} diff --git a/headers/meta.hpp/meta_types.hpp b/headers/meta.hpp/meta_types.hpp index 1c179e7..9d109f0 100644 --- a/headers/meta.hpp/meta_types.hpp +++ b/headers/meta.hpp/meta_types.hpp @@ -157,7 +157,8 @@ namespace meta_hpp [[nodiscard]] any_type get_argument_type(std::size_t position) const noexcept; [[nodiscard]] const any_type_list& get_argument_types() const noexcept; - [[nodiscard]] const class_set& get_bases() const noexcept; + [[nodiscard]] const class_set& get_base_classes() const noexcept; + [[nodiscard]] const class_set& get_derived_classes() const noexcept; [[nodiscard]] const constructor_set& get_constructors() const noexcept; [[nodiscard]] const destructor_set& get_destructors() const noexcept; [[nodiscard]] const function_set& get_functions() const noexcept; @@ -391,7 +392,8 @@ namespace meta_hpp::detail const std::size_t align; const any_type_list argument_types; - class_set bases; + class_set base_classes; + class_set derived_classes; constructor_set constructors; destructor_set destructors; function_set functions; @@ -400,13 +402,14 @@ namespace meta_hpp::detail typedef_map typedefs; variable_set variables; - struct base_info final { - using upcast_fptr = void* (*)(void*); - const upcast_fptr upcast; - }; + using upcast_func_t = void* (*)(void*); + using upcast_func_list_t = std::vector; - using base_info_map = std::map>; - base_info_map bases_info; + using base_upcasts_t = std::map>; + using deep_upcasts_t = std::multimap>; + + base_upcasts_t base_upcasts; + deep_upcasts_t deep_upcasts; template < class_kind Class > explicit class_type_data(type_list); diff --git a/headers/meta.hpp/meta_types/class_type.hpp b/headers/meta.hpp/meta_types/class_type.hpp index cbea19d..5718aeb 100644 --- a/headers/meta.hpp/meta_types/class_type.hpp +++ b/headers/meta.hpp/meta_types/class_type.hpp @@ -58,8 +58,12 @@ namespace meta_hpp return data_->argument_types; } - inline const class_set& class_type::get_bases() const noexcept { - return data_->bases; + inline const class_set& class_type::get_base_classes() const noexcept { + return data_->base_classes; + } + + inline const class_set& class_type::get_derived_classes() const noexcept { + return data_->derived_classes; } inline const constructor_set& class_type::get_constructors() const noexcept { @@ -139,16 +143,10 @@ namespace meta_hpp return false; } - if ( derived.data_->bases.contains(*this) ) { + if ( derived.data_->deep_upcasts.contains(*this) ) { return true; } - for ( const class_type& derived_base : derived.data_->bases ) { - if ( is_base_of(derived_base) ) { - return true; - } - } - return false; } @@ -162,16 +160,10 @@ namespace meta_hpp return false; } - if ( data_->bases.contains(base) ) { + if ( data_->deep_upcasts.contains(base) ) { return true; } - for ( const class_type& self_base : data_->bases ) { - if ( self_base.is_derived_from(base) ) { - return true; - } - } - return false; } @@ -182,7 +174,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const function& function = base.get_function(name) ) { return function; } @@ -198,7 +190,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const member& member = base.get_member(name) ) { return member; } @@ -214,7 +206,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const method& method = base.get_method(name) ) { return method; } @@ -228,7 +220,7 @@ namespace meta_hpp return iter->second; } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const any_type& type = base.get_typedef(name) ) { return type; } @@ -244,7 +236,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const variable& variable = base.get_variable(name) ) { return variable; } @@ -316,7 +308,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const function& function = base.get_function_with(name, first, last) ) { return function; } @@ -356,7 +348,7 @@ namespace meta_hpp } } - for ( const class_type& base : data_->bases ) { + for ( const class_type& base : data_->base_classes ) { if ( const method& method = base.get_method_with(name, first, last) ) { return method; }