diff --git a/headers/meta.hpp/meta_utilities.hpp b/headers/meta.hpp/meta_utilities.hpp index f056f48..109590b 100644 --- a/headers/meta.hpp/meta_utilities.hpp +++ b/headers/meta.hpp/meta_utilities.hpp @@ -68,7 +68,9 @@ namespace meta_hpp::detail template < typename T > concept uvalue_kind = + std::same_as || std::same_as || + std::same_as || std::same_as || std::same_as; @@ -218,20 +220,20 @@ namespace meta_hpp::detail public: arg_base() = delete; - arg_base(arg_base&&) = delete; - arg_base& operator=(arg_base&&) = delete; + arg_base(arg_base&&) = default; + arg_base(const arg_base&) = default; - arg_base(const arg_base&) = delete; + arg_base& operator=(arg_base&&) = delete; arg_base& operator=(const arg_base&) = delete; - template < typename T, std::enable_if_t< - (std::is_lvalue_reference_v) - , int> = 0 > + virtual ~arg_base() = default; + + template < arg_lvalue_ref_kind T > + requires decay_non_uvalue_kind explicit arg_base(type_list); - template < typename T, std::enable_if_t< - (std::is_rvalue_reference_v || !std::is_reference_v) - , int> = 0 > + template < arg_rvalue_ref_kind T > + requires decay_non_uvalue_kind explicit arg_base(type_list); explicit arg_base(value& v); @@ -240,15 +242,15 @@ namespace meta_hpp::detail explicit arg_base(value&& v); explicit arg_base(const value&& v); - bool is_const() const noexcept; - bool is_lvalue() const noexcept; - bool is_rvalue() const noexcept; + [[nodiscard]] bool is_const() const noexcept; + [[nodiscard]] bool is_lvalue() const noexcept; + [[nodiscard]] bool is_rvalue() const noexcept; - ref_types get_ref_type() const noexcept; - const any_type& get_raw_type() const noexcept; + [[nodiscard]] ref_types get_ref_type() const noexcept; + [[nodiscard]] const any_type& get_raw_type() const noexcept; template < typename To > - bool can_cast_to() const noexcept; + [[nodiscard]] bool can_cast_to() const noexcept; private: ref_types ref_type_{}; any_type raw_type_{}; @@ -261,24 +263,22 @@ namespace meta_hpp::detail public: arg() = delete; - arg(arg&&) = delete; - arg& operator=(arg&&) = delete; + arg(arg&&) = default; + arg(const arg&) = default; - arg(const arg&) = delete; + arg& operator=(arg&&) = delete; arg& operator=(const arg&) = delete; - template < typename T, typename Tp = std::decay_t - , std::enable_if_t, int> = 0 > + ~arg() override = default; + + template < decay_value_kind T > explicit arg(T&& v); - template < typename T, typename Tp = std::decay_t - , std::enable_if_t, int> = 0 - , std::enable_if_t, int> = 0 - , std::enable_if_t, int> = 0 > + template < decay_non_uvalue_kind T > explicit arg(T&& v); template < typename To > - To cast() const; + [[nodiscard]] To cast() const; private: void* data_{}; }; @@ -297,21 +297,18 @@ namespace meta_hpp::detail public: inst_base() = delete; - inst_base(inst_base&&) = delete; - inst_base& operator=(inst_base&&) = delete; + inst_base(inst_base&&) = default; + inst_base(const inst_base&) = default; - inst_base(const inst_base&) = delete; + inst_base& operator=(inst_base&&) = delete; inst_base& operator=(const inst_base&) = delete; - template < typename T, std::enable_if_t< - (std::is_lvalue_reference_v && std::is_class_v>) - , int> = 0> + virtual ~inst_base() = default; + + template < inst_class_lvalue_ref_kind T > explicit inst_base(type_list); - template < typename T, std::enable_if_t< - (std::is_class_v) || - (std::is_rvalue_reference_v && std::is_class_v>) - , int> = 0> + template < inst_class_rvalue_ref_kind T > explicit inst_base(type_list); explicit inst_base(value& v); @@ -320,18 +317,15 @@ namespace meta_hpp::detail explicit inst_base(value&& v); explicit inst_base(const value&& v); - bool is_const() const noexcept; - bool is_lvalue() const noexcept; - bool is_rvalue() const noexcept; + [[nodiscard]] bool is_const() const noexcept; + [[nodiscard]] bool is_lvalue() const noexcept; + [[nodiscard]] bool is_rvalue() const noexcept; - ref_types get_ref_type() const noexcept; - const class_type& get_raw_type() const noexcept; + [[nodiscard]] ref_types get_ref_type() const noexcept; + [[nodiscard]] const class_type& get_raw_type() const noexcept; - template < typename To, std::enable_if_t< - (std::is_class_v) || - (std::is_reference_v && std::is_class_v>) - , int> = 0> - bool can_cast_to() const noexcept; + template < inst_class_ref_kind Q > + [[nodiscard]] bool can_cast_to() const noexcept; private: ref_types ref_type_{}; class_type raw_type_{}; @@ -344,27 +338,22 @@ namespace meta_hpp::detail public: inst() = delete; - inst(inst&&) = delete; - inst& operator=(inst&&) = delete; + inst(inst&&) = default; + inst(const inst&) = default; - inst(const inst&) = delete; + inst& operator=(inst&&) = delete; inst& operator=(const inst&) = delete; - template < typename T, typename Tp = std::decay_t - , std::enable_if_t, int> = 0 > + ~inst() override = default; + + template < decay_value_kind T > explicit inst(T&& v); - template < typename T, class_kind Tp = std::decay_t - , std::enable_if_t, int> = 0 - , std::enable_if_t, int> = 0 - , std::enable_if_t, int> = 0 > + template < decay_non_uvalue_kind T > explicit inst(T&& v); - template < typename To, std::enable_if_t< - (std::is_class_v) || - (std::is_reference_v && std::is_class_v>) - , int> = 0> - decltype(auto) cast() const; + template < inst_class_ref_kind Q > + [[nodiscard]] decltype(auto) cast() const; private: void* data_{}; }; diff --git a/headers/meta.hpp/meta_utilities/arg.hpp b/headers/meta.hpp/meta_utilities/arg.hpp index 19450fa..65bd5f8 100644 --- a/headers/meta.hpp/meta_utilities/arg.hpp +++ b/headers/meta.hpp/meta_utilities/arg.hpp @@ -11,18 +11,22 @@ namespace meta_hpp::detail { - template < typename T, std::enable_if_t< - (std::is_lvalue_reference_v) - , int> > + template < arg_lvalue_ref_kind T > + requires decay_non_uvalue_kind + // NOLINTNEXTLINE(readability-named-parameter) arg_base::arg_base(type_list) - : ref_type_{std::is_const_v> ? ref_types::const_lvalue : ref_types::lvalue} + : ref_type_{std::is_const_v> + ? ref_types::const_lvalue + : ref_types::lvalue} , raw_type_{resolve_type>()} {} - template < typename T, std::enable_if_t< - (std::is_rvalue_reference_v || !std::is_reference_v) - , int> > + template < arg_rvalue_ref_kind T > + requires decay_non_uvalue_kind + // NOLINTNEXTLINE(readability-named-parameter) arg_base::arg_base(type_list) - : ref_type_{std::is_const_v> ? ref_types::const_rvalue : ref_types::rvalue} + : ref_type_{std::is_const_v> + ? ref_types::const_rvalue + : ref_types::rvalue} , raw_type_{resolve_type>()} {} inline arg_base::arg_base(value& v) @@ -65,6 +69,7 @@ namespace meta_hpp::detail } template < typename To > + // NOLINTNEXTLINE(readability-function-cognitive-complexity) bool arg_base::can_cast_to() const noexcept { using to_raw_type_cv = std::remove_reference_t; using to_raw_type = std::remove_cv_t; @@ -73,112 +78,176 @@ namespace meta_hpp::detail !(std::is_reference_v && std::is_pointer_v), "references to pointers are not supported yet"); - const auto check_qualifiers = [this](){ - switch ( get_ref_type() ) { - case ref_types::lvalue: return std::is_convertible_v; - case ref_types::const_lvalue: return std::is_convertible_v; - case ref_types::rvalue: return std::is_convertible_v; - case ref_types::const_rvalue: return std::is_convertible_v; - } + const any_type& from_type = get_raw_type(); + const any_type& to_type = resolve_type(); + + const auto is_a = [](const any_type& base, const any_type& derived){ + return (base == derived) + || (base.is_class() && derived.is_class() && base.as_class().is_base_of(derived.as_class())); }; - const auto check_convertible = [this](){ - const auto is_a = [](const any_type& base, const any_type& derived){ - return (base == derived) - || (base.is_class() && derived.is_class() && base.as_class().is_base_of(derived.as_class())); - }; - - const any_type& from_type = get_raw_type(); - const any_type& to_type = resolve_type(); - - if ( is_a(to_type, from_type) ) { - return true; - } - - if ( std::is_lvalue_reference_v && !std::is_const_v ) { - return false; - } - - if ( from_type.is_pointer() && to_type.is_pointer() ) { - const pointer_type& from_type_ptr = from_type.as_pointer(); - const bool from_type_ptr_readonly = from_type_ptr.get_flags().has(pointer_flags::is_readonly); - + if constexpr ( std::is_pointer_v ) { + if ( to_type.is_pointer() && from_type.is_pointer() ) { const pointer_type& to_type_ptr = to_type.as_pointer(); const bool to_type_ptr_readonly = to_type_ptr.get_flags().has(pointer_flags::is_readonly); - return to_type_ptr_readonly >= from_type_ptr_readonly - && is_a(to_type_ptr.get_data_type(), from_type_ptr.get_data_type()); + const pointer_type& from_type_ptr = from_type.as_pointer(); + const bool from_type_ptr_readonly = from_type_ptr.get_flags().has(pointer_flags::is_readonly); + + const any_type& to_data_type = to_type_ptr.get_data_type(); + const any_type& from_data_type = from_type_ptr.get_data_type(); + + if ( is_a(to_data_type, from_data_type) && to_type_ptr_readonly >= from_type_ptr_readonly ) { + return true; + } } + } - return false; - }; + if constexpr ( std::is_reference_v ) { + const auto is_convertible = [this](){ + switch ( get_ref_type() ) { + case ref_types::lvalue: + return std::is_convertible_v>; + case ref_types::const_lvalue: + return std::is_convertible_v>; + case ref_types::rvalue: + return std::is_convertible_v>; + case ref_types::const_rvalue: + return std::is_convertible_v>; + } + }; - return check_qualifiers() && check_convertible(); + if ( is_a(to_type, from_type) && is_convertible() ) { + return true; + } + } + + if constexpr ( !std::is_pointer_v && !std::is_reference_v ) { + const auto is_constructible = [this](){ + switch ( get_ref_type() ) { + case ref_types::lvalue: + return std::is_constructible_v && can_cast_to(); + case ref_types::const_lvalue: + return std::is_constructible_v && can_cast_to(); + case ref_types::rvalue: + return std::is_constructible_v && can_cast_to(); + case ref_types::const_rvalue: + return std::is_constructible_v && can_cast_to(); + } + }; + + if ( is_a(to_type, from_type) && is_constructible() ) { + return true; + } + } + + return false; } } namespace meta_hpp::detail { - template < typename T, typename Tp - , std::enable_if_t, int> > + template < decay_value_kind T > arg::arg(T&& v) : arg_base{std::forward(v)} + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) , data_{const_cast(v.data())} {} - template < typename T, typename Tp - , std::enable_if_t, int> - , std::enable_if_t, int> - , std::enable_if_t, int> > + template < decay_non_uvalue_kind T > arg::arg(T&& v) : arg_base{type_list{}} + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) , data_{const_cast*>(std::addressof(v))} {} template < typename To > + // NOLINTNEXTLINE(readability-function-cognitive-complexity) To arg::cast() const { if ( !can_cast_to() ) { throw std::logic_error("bad argument cast"); } + using to_raw_type_cv = std::remove_reference_t; + using to_raw_type = std::remove_cv_t; + + const any_type& from_type = get_raw_type(); + const any_type& to_type = resolve_type(); + if constexpr ( std::is_pointer_v ) { - return *static_cast(data_); + if ( to_type.is_pointer() && from_type.is_pointer() ) { + const pointer_type& to_type_ptr = to_type.as_pointer(); + const pointer_type& from_type_ptr = from_type.as_pointer(); + + const any_type& to_data_type = to_type_ptr.get_data_type(); + const any_type& from_data_type = from_type_ptr.get_data_type(); + + void** from_data_ptr = static_cast(data_); + + if ( to_data_type == from_data_type ) { + void* to_data_ptr = *from_data_ptr; + return static_cast(to_data_ptr); + } + + if ( to_data_type.is_class() && from_data_type.is_class() ) { + const class_type& to_data_class = to_data_type.as_class(); + const class_type& from_data_class = from_data_type.as_class(); + + void* to_data_ptr = detail::pointer_upcast(*from_data_ptr, from_data_class, to_data_class); + return static_cast(to_data_ptr); + } + } } if constexpr ( std::is_reference_v ) { - using raw_type = std::remove_cvref_t; + if ( to_type == from_type ) { + void* to_ptr = static_cast(data_); - if constexpr ( std::is_lvalue_reference_v ) { - return *static_cast(data_); + if constexpr ( std::is_lvalue_reference_v ) { + return *static_cast(to_ptr); + } + + if constexpr ( std::is_rvalue_reference_v ) { + return std::move(*static_cast(to_ptr)); + } } - if constexpr ( std::is_rvalue_reference_v ) { - return std::move(*static_cast(data_)); + if ( to_type.is_class() && from_type.is_class() ) { + const class_type& to_class = to_type.as_class(); + const class_type& from_class = from_type.as_class(); + + void* to_ptr = detail::pointer_upcast(data_, from_class, to_class); + + if constexpr ( std::is_lvalue_reference_v ) { + return *static_cast(to_ptr); + } + + if constexpr ( std::is_rvalue_reference_v ) { + return std::move(*static_cast(to_ptr)); + } } } if constexpr ( !std::is_pointer_v && !std::is_reference_v ) { - using raw_type = std::remove_cv_t; - - if ( get_ref_type() == ref_types::lvalue ) { - if constexpr ( std::is_constructible_v ) { - return To{*static_cast(data_)}; + if constexpr ( std::is_constructible_v ) { + if ( get_ref_type() == ref_types::lvalue ) { + return To{cast()}; } } - if ( get_ref_type() == ref_types::const_lvalue ) { - if constexpr ( std::is_constructible_v ) { - return To{std::as_const(*static_cast(data_))}; + if constexpr ( std::is_constructible_v ) { + if ( get_ref_type() == ref_types::const_lvalue ) { + return To{cast()}; } } - if ( get_ref_type() == ref_types::rvalue ) { - if constexpr ( std::is_constructible_v ) { - return To{std::move(*static_cast(data_))}; + if constexpr ( std::is_constructible_v ) { + if ( get_ref_type() == ref_types::rvalue ) { + return To{cast()}; } } - if ( get_ref_type() == ref_types::const_rvalue ) { - if constexpr ( std::is_constructible_v ) { - return To{std::move(std::as_const(*static_cast(data_)))}; + if constexpr ( std::is_constructible_v ) { + if ( get_ref_type() == ref_types::const_rvalue ) { + return To{cast()}; } } } diff --git a/headers/meta.hpp/meta_utilities/inst.hpp b/headers/meta.hpp/meta_utilities/inst.hpp index 754140e..d982371 100644 --- a/headers/meta.hpp/meta_utilities/inst.hpp +++ b/headers/meta.hpp/meta_utilities/inst.hpp @@ -11,19 +11,71 @@ namespace meta_hpp::detail { - template < typename T, std::enable_if_t< - (std::is_lvalue_reference_v && std::is_class_v>) - , int> > + namespace impl + { + template < inst_class_ref_kind Q, bool is_const, bool is_lvalue, bool is_rvalue > + struct inst_traits_impl; + + template < inst_class_ref_kind Q > + struct inst_traits_impl { + using class_type = std::remove_cvref_t; + using method_type = void(class_type::*)(); + }; + + template < inst_class_ref_kind Q > + struct inst_traits_impl { + using class_type = std::remove_cvref_t; + using method_type = void(class_type::*)() &; + }; + + template < inst_class_ref_kind Q > + struct inst_traits_impl { + using class_type = std::remove_cvref_t; + using method_type = void(class_type::*)() &&; + }; + + template < inst_class_ref_kind Q > + struct inst_traits_impl { + using class_type = std::remove_cvref_t; + using method_type = void(class_type::*)() const; + }; + + template < inst_class_ref_kind Q > + struct inst_traits_impl { + using class_type = std::remove_cvref_t; + using method_type = void(class_type::*)() const &; + }; + + template < inst_class_ref_kind Q > + struct inst_traits_impl { + using class_type = std::remove_cvref_t; + using method_type = void(class_type::*)() const &&; + }; + } + + template < inst_class_ref_kind Q > + struct inst_traits final : impl::inst_traits_impl::is_const, + cvref_traits::is_lvalue, + cvref_traits::is_rvalue> {}; +} + +namespace meta_hpp::detail +{ + template < inst_class_lvalue_ref_kind T > + // NOLINTNEXTLINE(readability-named-parameter) inst_base::inst_base(type_list) - : ref_type_{std::is_const_v> ? ref_types::const_lvalue : ref_types::lvalue} + : ref_type_{std::is_const_v> + ? ref_types::const_lvalue + : ref_types::lvalue} , raw_type_{resolve_type>()} {} - template < typename T, std::enable_if_t< - (std::is_class_v) || - (std::is_rvalue_reference_v && std::is_class_v>) - , int> > + template < inst_class_rvalue_ref_kind T > + // NOLINTNEXTLINE(readability-named-parameter) inst_base::inst_base(type_list) - : ref_type_{std::is_const_v> ? ref_types::const_rvalue : ref_types::rvalue} + : ref_type_{std::is_const_v> + ? ref_types::const_rvalue + : ref_types::rvalue} , raw_type_{resolve_type>()} {} inline inst_base::inst_base(value& v) @@ -81,80 +133,73 @@ namespace meta_hpp::detail return raw_type_; } - template < typename To, std::enable_if_t< - (std::is_class_v) || - (std::is_reference_v && std::is_class_v>) - , int> > + template < inst_class_ref_kind Q > bool inst_base::can_cast_to() const noexcept { - using to_raw_type = std::remove_cvref_t; - using to_raw_type_cv = std::remove_reference_t; + using inst_class = typename inst_traits::class_type; + using inst_method = typename inst_traits::method_type; - if constexpr ( !std::is_const_v ) { - if ( is_const() ) { - return false; + const class_type& from_type = get_raw_type(); + const class_type& to_type = resolve_type(); + + const auto is_a = [](const class_type& base, const class_type& derived){ + return base == derived || base.is_base_of(derived); + }; + + const auto is_invocable = [this](){ + switch ( get_ref_type() ) { + case ref_types::lvalue: + return std::is_invocable_v; + case ref_types::const_lvalue: + return std::is_invocable_v; + case ref_types::rvalue: + return std::is_invocable_v; + case ref_types::const_rvalue: + return std::is_invocable_v; } - } + }; - if constexpr ( std::is_reference_v ) { - const auto check_qualifiers = [](ref_types self_ref_type){ - switch ( self_ref_type ) { - case ref_types::lvalue: return std::is_convertible_v; - case ref_types::const_lvalue: return std::is_convertible_v; - case ref_types::rvalue: return std::is_convertible_v; - case ref_types::const_rvalue: return std::is_convertible_v; - } - }; - - if ( !check_qualifiers(get_ref_type()) ) { - return false; - } - } - - return get_raw_type() == resolve_type() - || get_raw_type().is_derived_from(resolve_type()); + return is_a(to_type, from_type) && is_invocable(); } } namespace meta_hpp::detail { - template < typename T, typename Tp - , std::enable_if_t, int> > + template < decay_value_kind T > inst::inst(T&& v) : inst_base{std::forward(v)} + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) , data_{const_cast(v.data())} {} - template < typename T, class_kind Tp - , std::enable_if_t, int> - , std::enable_if_t, int> - , std::enable_if_t, int> > + template < decay_non_uvalue_kind T > inst::inst(T&& v) : inst_base{type_list{}} + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) , data_{const_cast*>(std::addressof(v))} {} - template < typename To, std::enable_if_t< - (std::is_class_v) || - (std::is_reference_v && std::is_class_v>) - , int> > + template < inst_class_ref_kind Q > decltype(auto) inst::cast() const { - if ( !can_cast_to() ) { + if ( !can_cast_to() ) { throw std::logic_error("bad instance cast"); } - if constexpr ( std::is_reference_v ) { - using raw_type_with_cv = std::remove_reference_t; + using inst_class_cv = std::remove_reference_t; + using inst_class = std::remove_cv_t; - if constexpr ( std::is_lvalue_reference_v ) { - return *static_cast(data_); - } + const class_type& from_type = get_raw_type(); + const class_type& to_type = resolve_type(); - if constexpr ( std::is_rvalue_reference_v ) { - return std::move(*static_cast(data_)); - } + void* to_ptr = detail::pointer_upcast(data_, from_type, to_type); + + if constexpr ( !std::is_reference_v ) { + return *static_cast(to_ptr); } - if constexpr ( !std::is_reference_v) { - using raw_type_with_cv = To; - return *static_cast(data_); + if constexpr ( std::is_lvalue_reference_v ) { + return *static_cast(to_ptr); + } + + if constexpr ( std::is_rvalue_reference_v ) { + return std::move(*static_cast(to_ptr)); } } }