diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index f111054..836d96c 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -2932,27 +2932,44 @@ namespace meta_hpp [[nodiscard]] bool is() const noexcept; template < typename T > + requires detail::pointer_kind + [[nodiscard]] T as(); + + template < typename T > + requires detail::pointer_kind + [[nodiscard]] T as() const; + + template < typename T > + requires(!detail::pointer_kind) [[nodiscard]] T as() &&; template < typename T > - [[nodiscard]] auto as() & // - -> std::conditional_t, T, T&>; + requires(!detail::pointer_kind) + [[nodiscard]] T& as() &; template < typename T > - [[nodiscard]] auto as() const& // - -> std::conditional_t, T, const T&>; + requires(!detail::pointer_kind) + [[nodiscard]] const T& as() const&; template < typename T > - [[nodiscard]] auto as() const&& // - -> std::conditional_t, T, const T&&>; + requires(!detail::pointer_kind) + [[nodiscard]] const T&& as() const&&; template < typename T > - [[nodiscard]] auto try_as() noexcept // - -> std::conditional_t, T, T*>; + requires detail::pointer_kind + [[nodiscard]] T try_as() noexcept; template < typename T > - [[nodiscard]] auto try_as() const noexcept // - -> std::conditional_t, T, const T*>; + requires detail::pointer_kind + [[nodiscard]] T try_as() const noexcept; + + template < typename T > + requires(!detail::pointer_kind) + [[nodiscard]] T* try_as() noexcept; + + template < typename T > + requires(!detail::pointer_kind) + [[nodiscard]] const T* try_as() const noexcept; private: struct vtable_t; @@ -5426,7 +5443,7 @@ namespace meta_hpp::detail namespace meta_hpp::detail { - [[nodiscard]] inline bool is_base_of(const any_type& base, const any_type& derived) noexcept { + [[nodiscard]] inline bool is_a(const any_type& base, const any_type& derived) noexcept { if ( base == derived ) { return true; } @@ -5439,7 +5456,7 @@ namespace meta_hpp::detail } return false; - }; + } } namespace meta_hpp::detail @@ -5486,6 +5503,33 @@ namespace meta_hpp::detail } } +namespace meta_hpp::detail +{ + [[nodiscard]] inline void* pointer_upcast(void* ptr, const any_type& from, const any_type& to) { + if ( nullptr == ptr || !from || !to ) { + return nullptr; + } + + if ( to.is_void() || from == to ) { + return ptr; + } + + 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); + } + + return nullptr; + } + + [[nodiscard]] inline const void* pointer_upcast(const void* ptr, const any_type& from, const any_type& to) { + // NOLINTNEXTLINE(*-const-cast) + return pointer_upcast(const_cast(ptr), from, to); + } +} + namespace meta_hpp::detail { class uarg_base { @@ -5552,11 +5596,11 @@ namespace meta_hpp::detail } template < typename To > - requires(std::is_pointer_v) + requires pointer_kind [[nodiscard]] bool can_cast_to(type_registry& registry) const noexcept; template < typename To > - requires(!std::is_pointer_v) + requires(!pointer_kind) [[nodiscard]] bool can_cast_to(type_registry& registry) const noexcept; private: @@ -5591,11 +5635,11 @@ namespace meta_hpp::detail , data_{const_cast*>(std::addressof(v))} {} // NOLINT(*-const-cast) template < typename To > - requires(std::is_pointer_v) + requires pointer_kind [[nodiscard]] decltype(auto) cast(type_registry& registry) const; template < typename To > - requires(!std::is_pointer_v) + requires(!pointer_kind) [[nodiscard]] decltype(auto) cast(type_registry& registry) const; private: @@ -5606,47 +5650,44 @@ namespace meta_hpp::detail namespace meta_hpp::detail { template < typename To > - requires(std::is_pointer_v) + requires pointer_kind [[nodiscard]] bool uarg_base::can_cast_to(type_registry& registry) const noexcept { - using to_raw_type_cv = std::remove_reference_t; - using to_raw_type = std::remove_cv_t; + using to_raw_type = std::remove_cv_t; const any_type& from_type = get_raw_type(); - const any_type& to_type = registry.resolve_type(); + const pointer_type& to_type_ptr = registry.resolve_type(); - if ( from_type.is_nullptr() && to_type.is_pointer() ) { + if ( from_type.is_nullptr() ) { return true; } - if ( to_type.is_pointer() && from_type.is_array() ) { - 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); - + if ( from_type.is_array() ) { const array_type& from_type_array = from_type.as_array(); + + const bool to_type_ptr_readonly = to_type_ptr.get_flags().has(pointer_flags::is_readonly); const bool from_type_array_readonly = is_ref_const(); const any_type& to_data_type = to_type_ptr.get_data_type(); const any_type& from_data_type = from_type_array.get_data_type(); if ( to_type_ptr_readonly >= from_type_array_readonly ) { - if ( to_data_type.is_void() || is_base_of(to_data_type, from_data_type) ) { + if ( to_data_type.is_void() || is_a(to_data_type, from_data_type) ) { return true; } } } - 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); - + if ( from_type.is_pointer() ) { const pointer_type& from_type_ptr = from_type.as_pointer(); + + const bool to_type_ptr_readonly = to_type_ptr.get_flags().has(pointer_flags::is_readonly); 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 ( to_type_ptr_readonly >= from_type_ptr_readonly ) { - if ( to_data_type.is_void() || is_base_of(to_data_type, from_data_type) ) { + if ( to_data_type.is_void() || is_a(to_data_type, from_data_type) ) { return true; } } @@ -5656,13 +5697,13 @@ namespace meta_hpp::detail } template < typename To > - requires(!std::is_pointer_v) + requires(!pointer_kind) [[nodiscard]] bool uarg_base::can_cast_to(type_registry& registry) const noexcept { using to_raw_type_cv = std::remove_reference_t; using to_raw_type = std::remove_cv_t; static_assert( // - !(std::is_reference_v && std::is_pointer_v), + !(std::is_reference_v && pointer_kind), "references to pointers are not supported yet" ); @@ -5698,13 +5739,13 @@ namespace meta_hpp::detail }; if constexpr ( std::is_reference_v ) { - if ( is_base_of(to_type, from_type) && is_convertible_to_ref(type_list{}) ) { + if ( is_a(to_type, from_type) && is_convertible_to_ref(type_list{}) ) { return true; } } - if constexpr ( !std::is_pointer_v && !std::is_reference_v ) { - if ( is_base_of(to_type, from_type) && is_constructible_from_type(type_list{}) ) { + if constexpr ( !pointer_kind && !std::is_reference_v ) { + if ( is_a(to_type, from_type) && is_constructible_from_type(type_list{}) ) { return true; } } @@ -5716,67 +5757,44 @@ namespace meta_hpp::detail namespace meta_hpp::detail { template < typename To > - requires(std::is_pointer_v) + requires pointer_kind [[nodiscard]] decltype(auto) uarg::cast(type_registry& registry) const { META_HPP_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; + using to_raw_type = std::remove_cv_t; const any_type& from_type = get_raw_type(); - const any_type& to_type = registry.resolve_type(); + const pointer_type& to_type_ptr = registry.resolve_type(); - if ( to_type.is_pointer() && from_type.is_nullptr() ) { - return static_cast(nullptr); + if ( from_type.is_nullptr() ) { + return static_cast(nullptr); } - if ( to_type.is_pointer() && from_type.is_array() ) { - const pointer_type& to_type_ptr = to_type.as_pointer(); + if ( from_type.is_array() ) { const array_type& from_type_array = from_type.as_array(); - const any_type& to_data_type = to_type_ptr.get_data_type(); - const any_type& from_data_type = from_type_array.get_data_type(); - - if ( to_data_type.is_void() || to_data_type == from_data_type ) { - return static_cast(data_); - } - - 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_ptr = pointer_upcast(data_, from_data_class, to_data_class); - return static_cast(to_ptr); - } + return static_cast(pointer_upcast( // + data_, + from_type_array.get_data_type(), + to_type_ptr.get_data_type() + )); } - if ( to_type.is_pointer() && from_type.is_pointer() ) { - const pointer_type& to_type_ptr = to_type.as_pointer(); + if ( from_type.is_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.is_void() || to_data_type == from_data_type ) { - return static_cast(*from_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_ptr = pointer_upcast(*from_data_ptr, from_data_class, to_data_class); - return static_cast(to_ptr); - } + return static_cast(pointer_upcast( // + *static_cast(data_), + from_type_ptr.get_data_type(), + to_type_ptr.get_data_type() + )); } throw_exception(error_code::bad_argument_cast); } template < typename To > - requires(!std::is_pointer_v) + requires(!pointer_kind) [[nodiscard]] decltype(auto) uarg::cast(type_registry& registry) const { META_HPP_ASSERT(can_cast_to(registry) && "bad argument cast"); @@ -5784,16 +5802,14 @@ namespace meta_hpp::detail using to_raw_type = std::remove_cv_t; static_assert( // - !(std::is_reference_v && std::is_pointer_v), + !(std::is_reference_v && pointer_kind), "references to pointers are not supported yet" ); const any_type& from_type = get_raw_type(); const any_type& to_type = registry.resolve_type(); - void* to_ptr = to_type == from_type // - ? data_ - : pointer_upcast(data_, from_type.as_class(), to_type.as_class()); + void* to_ptr = pointer_upcast(data_, from_type, to_type); if constexpr ( std::is_lvalue_reference_v ) { return *static_cast(to_ptr); @@ -6214,7 +6230,7 @@ namespace meta_hpp::detail return false; }; - return is_invocable() && is_base_of(to_type, from_type); + return is_invocable() && is_a(to_type, from_type); } if ( from_type.is_pointer() ) { @@ -6227,7 +6243,7 @@ namespace meta_hpp::detail : std::is_invocable_v; }; - return is_invocable() && is_base_of(to_type, from_data_type); + return is_invocable() && is_a(to_type, from_data_type); } return false; @@ -8976,198 +8992,136 @@ namespace meta_hpp template < typename T > bool uvalue::is() const noexcept { static_assert(std::is_same_v>); - return detail::is_base_of(resolve_type(), get_type()); + return detail::is_a(resolve_type(), get_type()); } template < typename T > + requires detail::pointer_kind + T uvalue::as() { + static_assert(std::is_same_v>); + + if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { + return ptr; + } + + throw_exception(error_code::bad_uvalue_access); + } + + template < typename T > + requires detail::pointer_kind + T uvalue::as() const { + static_assert(std::is_same_v>); + + if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { + return ptr; + } + + throw_exception(error_code::bad_uvalue_access); + } + + template < typename T > + requires(!detail::pointer_kind) T uvalue::as() && { static_assert(std::is_same_v>); - if constexpr ( detail::pointer_kind ) { - if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { - return ptr; - } - } else { - if ( T* ptr = try_as() ) { - return std::move(*ptr); - } + if ( T* ptr = try_as() ) { + return std::move(*ptr); } throw_exception(error_code::bad_uvalue_access); } template < typename T > - auto uvalue::as() & -> std::conditional_t, T, T&> { + requires(!detail::pointer_kind) + T& uvalue::as() & { static_assert(std::is_same_v>); - if constexpr ( detail::pointer_kind ) { - if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { - return ptr; - } - } else { - if ( T* ptr = try_as() ) { - return *ptr; - } + if ( T* ptr = try_as() ) { + return *ptr; } throw_exception(error_code::bad_uvalue_access); } template < typename T > - auto uvalue::as() const& -> std::conditional_t, T, const T&> { + requires(!detail::pointer_kind) + const T& uvalue::as() const& { static_assert(std::is_same_v>); - if constexpr ( detail::pointer_kind ) { - if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { - return ptr; - } - } else { - if ( const T* ptr = try_as() ) { - return *ptr; - } + if ( const T* ptr = try_as() ) { + return *ptr; } throw_exception(error_code::bad_uvalue_access); } template < typename T > - auto uvalue::as() const&& -> std::conditional_t, T, const T&&> { + requires(!detail::pointer_kind) + const T&& uvalue::as() const&& { static_assert(std::is_same_v>); - if constexpr ( detail::pointer_kind ) { - if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { - return ptr; - } - } else { - if ( const T* ptr = try_as() ) { - return std::move(*ptr); - } + if ( const T* ptr = try_as() ) { + return std::move(*ptr); } throw_exception(error_code::bad_uvalue_access); } template < typename T > - // NOLINTNEXTLINE(*-cognitive-complexity) - auto uvalue::try_as() noexcept -> std::conditional_t, T, T*> { + requires detail::pointer_kind + T uvalue::try_as() noexcept { static_assert(std::is_same_v>); - using detail::type_registry; + using namespace detail; type_registry& registry{type_registry::instance()}; - const any_type& from_type = get_type(); - const any_type& to_type = registry.resolve_type(); - - if constexpr ( detail::pointer_kind ) { - if ( to_type.is_pointer() && from_type.is_nullptr() ) { - return static_cast(nullptr); - } - - 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); - - 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 ( to_type_ptr_readonly >= from_type_ptr_readonly ) { - void** from_data_ptr = static_cast(get_data()); - - if ( to_data_type.is_void() || to_data_type == from_data_type ) { - void* to_ptr = *from_data_ptr; - return static_cast(to_ptr); - } - - if ( detail::is_base_of(to_data_type, from_data_type) ) { - const class_type& to_data_class = to_data_type.as_class(); - const class_type& from_data_class = from_data_type.as_class(); - - void* to_ptr = detail::pointer_upcast(*from_data_ptr, from_data_class, to_data_class); - return static_cast(to_ptr); - } - } - } - } - - if constexpr ( !detail::pointer_kind ) { - if ( from_type == to_type ) { - T* to_ptr = static_cast(get_data()); - return to_ptr; - } - - if ( detail::is_base_of(to_type, from_type) ) { - const class_type& to_class = to_type.as_class(); - const class_type& from_class = from_type.as_class(); - - T* to_ptr = static_cast(detail::pointer_upcast(get_data(), from_class, to_class)); - return to_ptr; - } + if ( const uarg varg{registry, *this}; varg.can_cast_to(registry) ) { + return varg.cast(registry); } return nullptr; } template < typename T > - // NOLINTNEXTLINE(*-cognitive-complexity) - auto uvalue::try_as() const noexcept -> std::conditional_t, T, const T*> { + requires detail::pointer_kind + T uvalue::try_as() const noexcept { static_assert(std::is_same_v>); - using detail::type_registry; + using namespace detail; type_registry& registry{type_registry::instance()}; - const any_type& from_type = get_type(); - const any_type& to_type = registry.resolve_type(); - - if constexpr ( detail::pointer_kind ) { - if ( to_type.is_pointer() && from_type.is_nullptr() ) { - return static_cast(nullptr); - } - - 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); - - 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 ( to_type_ptr_readonly >= from_type_ptr_readonly ) { - void* const* from_data_ptr = static_cast(get_data()); - - if ( to_data_type.is_void() || to_data_type == from_data_type ) { - void* to_ptr = *from_data_ptr; - return static_cast(to_ptr); - } - - if ( detail::is_base_of(to_data_type, from_data_type) ) { - const class_type& to_data_class = to_data_type.as_class(); - const class_type& from_data_class = from_data_type.as_class(); - - void* to_ptr = detail::pointer_upcast(*from_data_ptr, from_data_class, to_data_class); - return static_cast(to_ptr); - } - } - } + if ( const uarg varg{registry, *this}; varg.can_cast_to(registry) ) { + return varg.cast(registry); } - if constexpr ( !detail::pointer_kind ) { - if ( from_type == to_type ) { - const T* to_ptr = static_cast(get_data()); - return to_ptr; - } + return nullptr; + } - if ( detail::is_base_of(to_type, from_type) ) { - const class_type& to_class = to_type.as_class(); - const class_type& from_class = from_type.as_class(); + template < typename T > + requires(!detail::pointer_kind) + T* uvalue::try_as() noexcept { + static_assert(std::is_same_v>); - const T* to_ptr = static_cast(detail::pointer_upcast(get_data(), from_class, to_class)); - return to_ptr; - } + using namespace detail; + type_registry& registry{type_registry::instance()}; + + if ( const uarg varg{registry, *this}; varg.can_cast_to(registry) ) { + return std::addressof(varg.cast(registry)); + } + + return nullptr; + } + + template < typename T > + requires(!detail::pointer_kind) + const T* uvalue::try_as() const noexcept { + static_assert(std::is_same_v>); + + using namespace detail; + type_registry& registry{type_registry::instance()}; + + if ( const uarg varg{registry, *this}; varg.can_cast_to(registry) ) { + return std::addressof(varg.cast(registry)); } return nullptr; diff --git a/develop/untests/known_issues/uvalue_zero_ptr.cpp b/develop/untests/known_issues/uvalue_zero_ptr.cpp new file mode 100644 index 0000000..dad27ac --- /dev/null +++ b/develop/untests/known_issues/uvalue_zero_ptr.cpp @@ -0,0 +1,24 @@ +/******************************************************************************* + * 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/known_issues/uvalue_zero_ptr") { + namespace meta = meta_hpp; + + // { + // void* my_null = static_cast(0); + // meta::uvalue v{my_null}; + // CHECK(v.as() == my_null); + // CHECK(v.as() == my_null); + // } + // { + // const void* my_null = static_cast(0); + // meta::uvalue v{my_null}; + // CHECK(v.as() == my_null); + // } +} diff --git a/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp b/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp index 32ab3ef..4c7b7d1 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp @@ -78,11 +78,11 @@ namespace meta_hpp::detail } template < typename To > - requires(std::is_pointer_v) + requires pointer_kind [[nodiscard]] bool can_cast_to(type_registry& registry) const noexcept; template < typename To > - requires(!std::is_pointer_v) + requires(!pointer_kind) [[nodiscard]] bool can_cast_to(type_registry& registry) const noexcept; private: @@ -117,11 +117,11 @@ namespace meta_hpp::detail , data_{const_cast*>(std::addressof(v))} {} // NOLINT(*-const-cast) template < typename To > - requires(std::is_pointer_v) + requires pointer_kind [[nodiscard]] decltype(auto) cast(type_registry& registry) const; template < typename To > - requires(!std::is_pointer_v) + requires(!pointer_kind) [[nodiscard]] decltype(auto) cast(type_registry& registry) const; private: @@ -132,47 +132,44 @@ namespace meta_hpp::detail namespace meta_hpp::detail { template < typename To > - requires(std::is_pointer_v) + requires pointer_kind [[nodiscard]] bool uarg_base::can_cast_to(type_registry& registry) const noexcept { - using to_raw_type_cv = std::remove_reference_t; - using to_raw_type = std::remove_cv_t; + using to_raw_type = std::remove_cv_t; const any_type& from_type = get_raw_type(); - const any_type& to_type = registry.resolve_type(); + const pointer_type& to_type_ptr = registry.resolve_type(); - if ( from_type.is_nullptr() && to_type.is_pointer() ) { + if ( from_type.is_nullptr() ) { return true; } - if ( to_type.is_pointer() && from_type.is_array() ) { - 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); - + if ( from_type.is_array() ) { const array_type& from_type_array = from_type.as_array(); + + const bool to_type_ptr_readonly = to_type_ptr.get_flags().has(pointer_flags::is_readonly); const bool from_type_array_readonly = is_ref_const(); const any_type& to_data_type = to_type_ptr.get_data_type(); const any_type& from_data_type = from_type_array.get_data_type(); if ( to_type_ptr_readonly >= from_type_array_readonly ) { - if ( to_data_type.is_void() || is_base_of(to_data_type, from_data_type) ) { + if ( to_data_type.is_void() || is_a(to_data_type, from_data_type) ) { return true; } } } - 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); - + if ( from_type.is_pointer() ) { const pointer_type& from_type_ptr = from_type.as_pointer(); + + const bool to_type_ptr_readonly = to_type_ptr.get_flags().has(pointer_flags::is_readonly); 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 ( to_type_ptr_readonly >= from_type_ptr_readonly ) { - if ( to_data_type.is_void() || is_base_of(to_data_type, from_data_type) ) { + if ( to_data_type.is_void() || is_a(to_data_type, from_data_type) ) { return true; } } @@ -182,13 +179,13 @@ namespace meta_hpp::detail } template < typename To > - requires(!std::is_pointer_v) + requires(!pointer_kind) [[nodiscard]] bool uarg_base::can_cast_to(type_registry& registry) const noexcept { using to_raw_type_cv = std::remove_reference_t; using to_raw_type = std::remove_cv_t; static_assert( // - !(std::is_reference_v && std::is_pointer_v), + !(std::is_reference_v && pointer_kind), "references to pointers are not supported yet" ); @@ -224,13 +221,13 @@ namespace meta_hpp::detail }; if constexpr ( std::is_reference_v ) { - if ( is_base_of(to_type, from_type) && is_convertible_to_ref(type_list{}) ) { + if ( is_a(to_type, from_type) && is_convertible_to_ref(type_list{}) ) { return true; } } - if constexpr ( !std::is_pointer_v && !std::is_reference_v ) { - if ( is_base_of(to_type, from_type) && is_constructible_from_type(type_list{}) ) { + if constexpr ( !pointer_kind && !std::is_reference_v ) { + if ( is_a(to_type, from_type) && is_constructible_from_type(type_list{}) ) { return true; } } @@ -242,67 +239,44 @@ namespace meta_hpp::detail namespace meta_hpp::detail { template < typename To > - requires(std::is_pointer_v) + requires pointer_kind [[nodiscard]] decltype(auto) uarg::cast(type_registry& registry) const { META_HPP_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; + using to_raw_type = std::remove_cv_t; const any_type& from_type = get_raw_type(); - const any_type& to_type = registry.resolve_type(); + const pointer_type& to_type_ptr = registry.resolve_type(); - if ( to_type.is_pointer() && from_type.is_nullptr() ) { - return static_cast(nullptr); + if ( from_type.is_nullptr() ) { + return static_cast(nullptr); } - if ( to_type.is_pointer() && from_type.is_array() ) { - const pointer_type& to_type_ptr = to_type.as_pointer(); + if ( from_type.is_array() ) { const array_type& from_type_array = from_type.as_array(); - const any_type& to_data_type = to_type_ptr.get_data_type(); - const any_type& from_data_type = from_type_array.get_data_type(); - - if ( to_data_type.is_void() || to_data_type == from_data_type ) { - return static_cast(data_); - } - - 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_ptr = pointer_upcast(data_, from_data_class, to_data_class); - return static_cast(to_ptr); - } + return static_cast(pointer_upcast( // + data_, + from_type_array.get_data_type(), + to_type_ptr.get_data_type() + )); } - if ( to_type.is_pointer() && from_type.is_pointer() ) { - const pointer_type& to_type_ptr = to_type.as_pointer(); + if ( from_type.is_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.is_void() || to_data_type == from_data_type ) { - return static_cast(*from_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_ptr = pointer_upcast(*from_data_ptr, from_data_class, to_data_class); - return static_cast(to_ptr); - } + return static_cast(pointer_upcast( // + *static_cast(data_), + from_type_ptr.get_data_type(), + to_type_ptr.get_data_type() + )); } throw_exception(error_code::bad_argument_cast); } template < typename To > - requires(!std::is_pointer_v) + requires(!pointer_kind) [[nodiscard]] decltype(auto) uarg::cast(type_registry& registry) const { META_HPP_ASSERT(can_cast_to(registry) && "bad argument cast"); @@ -310,16 +284,14 @@ namespace meta_hpp::detail using to_raw_type = std::remove_cv_t; static_assert( // - !(std::is_reference_v && std::is_pointer_v), + !(std::is_reference_v && pointer_kind), "references to pointers are not supported yet" ); const any_type& from_type = get_raw_type(); const any_type& to_type = registry.resolve_type(); - void* to_ptr = to_type == from_type // - ? data_ - : pointer_upcast(data_, from_type.as_class(), to_type.as_class()); + void* to_ptr = pointer_upcast(data_, from_type, to_type); if constexpr ( std::is_lvalue_reference_v ) { return *static_cast(to_ptr); diff --git a/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp b/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp index 969835d..1ee40a0 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp @@ -149,7 +149,7 @@ namespace meta_hpp::detail return false; }; - return is_invocable() && is_base_of(to_type, from_type); + return is_invocable() && is_a(to_type, from_type); } if ( from_type.is_pointer() ) { @@ -162,7 +162,7 @@ namespace meta_hpp::detail : std::is_invocable_v; }; - return is_invocable() && is_base_of(to_type, from_data_type); + return is_invocable() && is_a(to_type, from_data_type); } return false; diff --git a/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp b/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp index 059913c..54a7016 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp @@ -110,7 +110,7 @@ namespace meta_hpp::detail namespace meta_hpp::detail { - [[nodiscard]] inline bool is_base_of(const any_type& base, const any_type& derived) noexcept { + [[nodiscard]] inline bool is_a(const any_type& base, const any_type& derived) noexcept { if ( base == derived ) { return true; } @@ -123,7 +123,7 @@ namespace meta_hpp::detail } return false; - }; + } } namespace meta_hpp::detail @@ -169,3 +169,30 @@ namespace meta_hpp::detail return static_cast(pointer_upcast(ptr, from_class, to_class)); } } + +namespace meta_hpp::detail +{ + [[nodiscard]] inline void* pointer_upcast(void* ptr, const any_type& from, const any_type& to) { + if ( nullptr == ptr || !from || !to ) { + return nullptr; + } + + if ( to.is_void() || from == to ) { + return ptr; + } + + 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); + } + + return nullptr; + } + + [[nodiscard]] inline const void* pointer_upcast(const void* ptr, const any_type& from, const any_type& to) { + // NOLINTNEXTLINE(*-const-cast) + return pointer_upcast(const_cast(ptr), from, to); + } +} diff --git a/headers/meta.hpp/meta_uvalue.hpp b/headers/meta.hpp/meta_uvalue.hpp index 0a8d8a0..5479e02 100644 --- a/headers/meta.hpp/meta_uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue.hpp @@ -84,27 +84,44 @@ namespace meta_hpp [[nodiscard]] bool is() const noexcept; template < typename T > + requires detail::pointer_kind + [[nodiscard]] T as(); + + template < typename T > + requires detail::pointer_kind + [[nodiscard]] T as() const; + + template < typename T > + requires(!detail::pointer_kind) [[nodiscard]] T as() &&; template < typename T > - [[nodiscard]] auto as() & // - -> std::conditional_t, T, T&>; + requires(!detail::pointer_kind) + [[nodiscard]] T& as() &; template < typename T > - [[nodiscard]] auto as() const& // - -> std::conditional_t, T, const T&>; + requires(!detail::pointer_kind) + [[nodiscard]] const T& as() const&; template < typename T > - [[nodiscard]] auto as() const&& // - -> std::conditional_t, T, const T&&>; + requires(!detail::pointer_kind) + [[nodiscard]] const T&& as() const&&; template < typename T > - [[nodiscard]] auto try_as() noexcept // - -> std::conditional_t, T, T*>; + requires detail::pointer_kind + [[nodiscard]] T try_as() noexcept; template < typename T > - [[nodiscard]] auto try_as() const noexcept // - -> std::conditional_t, T, const T*>; + requires detail::pointer_kind + [[nodiscard]] T try_as() const noexcept; + + template < typename T > + requires(!detail::pointer_kind) + [[nodiscard]] T* try_as() noexcept; + + template < typename T > + requires(!detail::pointer_kind) + [[nodiscard]] const T* try_as() const noexcept; private: struct vtable_t; diff --git a/headers/meta.hpp/meta_uvalue/uvalue.hpp b/headers/meta.hpp/meta_uvalue/uvalue.hpp index 0d21352..957c69e 100644 --- a/headers/meta.hpp/meta_uvalue/uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue/uvalue.hpp @@ -14,6 +14,7 @@ #include "../meta_detail/value_traits/index_traits.hpp" #include "../meta_detail/value_traits/unmap_traits.hpp" +#include "../meta_detail/value_utilities/uarg.hpp" #include "../meta_detail/value_utilities/utraits.hpp" namespace meta_hpp @@ -435,198 +436,136 @@ namespace meta_hpp template < typename T > bool uvalue::is() const noexcept { static_assert(std::is_same_v>); - return detail::is_base_of(resolve_type(), get_type()); + return detail::is_a(resolve_type(), get_type()); } template < typename T > + requires detail::pointer_kind + T uvalue::as() { + static_assert(std::is_same_v>); + + if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { + return ptr; + } + + throw_exception(error_code::bad_uvalue_access); + } + + template < typename T > + requires detail::pointer_kind + T uvalue::as() const { + static_assert(std::is_same_v>); + + if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { + return ptr; + } + + throw_exception(error_code::bad_uvalue_access); + } + + template < typename T > + requires(!detail::pointer_kind) T uvalue::as() && { static_assert(std::is_same_v>); - if constexpr ( detail::pointer_kind ) { - if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { - return ptr; - } - } else { - if ( T* ptr = try_as() ) { - return std::move(*ptr); - } + if ( T* ptr = try_as() ) { + return std::move(*ptr); } throw_exception(error_code::bad_uvalue_access); } template < typename T > - auto uvalue::as() & -> std::conditional_t, T, T&> { + requires(!detail::pointer_kind) + T& uvalue::as() & { static_assert(std::is_same_v>); - if constexpr ( detail::pointer_kind ) { - if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { - return ptr; - } - } else { - if ( T* ptr = try_as() ) { - return *ptr; - } + if ( T* ptr = try_as() ) { + return *ptr; } throw_exception(error_code::bad_uvalue_access); } template < typename T > - auto uvalue::as() const& -> std::conditional_t, T, const T&> { + requires(!detail::pointer_kind) + const T& uvalue::as() const& { static_assert(std::is_same_v>); - if constexpr ( detail::pointer_kind ) { - if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { - return ptr; - } - } else { - if ( const T* ptr = try_as() ) { - return *ptr; - } + if ( const T* ptr = try_as() ) { + return *ptr; } throw_exception(error_code::bad_uvalue_access); } template < typename T > - auto uvalue::as() const&& -> std::conditional_t, T, const T&&> { + requires(!detail::pointer_kind) + const T&& uvalue::as() const&& { static_assert(std::is_same_v>); - if constexpr ( detail::pointer_kind ) { - if ( T ptr = try_as(); ptr || get_type().is_nullptr() ) { - return ptr; - } - } else { - if ( const T* ptr = try_as() ) { - return std::move(*ptr); - } + if ( const T* ptr = try_as() ) { + return std::move(*ptr); } throw_exception(error_code::bad_uvalue_access); } template < typename T > - // NOLINTNEXTLINE(*-cognitive-complexity) - auto uvalue::try_as() noexcept -> std::conditional_t, T, T*> { + requires detail::pointer_kind + T uvalue::try_as() noexcept { static_assert(std::is_same_v>); - using detail::type_registry; + using namespace detail; type_registry& registry{type_registry::instance()}; - const any_type& from_type = get_type(); - const any_type& to_type = registry.resolve_type(); - - if constexpr ( detail::pointer_kind ) { - if ( to_type.is_pointer() && from_type.is_nullptr() ) { - return static_cast(nullptr); - } - - 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); - - 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 ( to_type_ptr_readonly >= from_type_ptr_readonly ) { - void** from_data_ptr = static_cast(get_data()); - - if ( to_data_type.is_void() || to_data_type == from_data_type ) { - void* to_ptr = *from_data_ptr; - return static_cast(to_ptr); - } - - if ( detail::is_base_of(to_data_type, from_data_type) ) { - const class_type& to_data_class = to_data_type.as_class(); - const class_type& from_data_class = from_data_type.as_class(); - - void* to_ptr = detail::pointer_upcast(*from_data_ptr, from_data_class, to_data_class); - return static_cast(to_ptr); - } - } - } - } - - if constexpr ( !detail::pointer_kind ) { - if ( from_type == to_type ) { - T* to_ptr = static_cast(get_data()); - return to_ptr; - } - - if ( detail::is_base_of(to_type, from_type) ) { - const class_type& to_class = to_type.as_class(); - const class_type& from_class = from_type.as_class(); - - T* to_ptr = static_cast(detail::pointer_upcast(get_data(), from_class, to_class)); - return to_ptr; - } + if ( const uarg varg{registry, *this}; varg.can_cast_to(registry) ) { + return varg.cast(registry); } return nullptr; } template < typename T > - // NOLINTNEXTLINE(*-cognitive-complexity) - auto uvalue::try_as() const noexcept -> std::conditional_t, T, const T*> { + requires detail::pointer_kind + T uvalue::try_as() const noexcept { static_assert(std::is_same_v>); - using detail::type_registry; + using namespace detail; type_registry& registry{type_registry::instance()}; - const any_type& from_type = get_type(); - const any_type& to_type = registry.resolve_type(); - - if constexpr ( detail::pointer_kind ) { - if ( to_type.is_pointer() && from_type.is_nullptr() ) { - return static_cast(nullptr); - } - - 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); - - 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 ( to_type_ptr_readonly >= from_type_ptr_readonly ) { - void* const* from_data_ptr = static_cast(get_data()); - - if ( to_data_type.is_void() || to_data_type == from_data_type ) { - void* to_ptr = *from_data_ptr; - return static_cast(to_ptr); - } - - if ( detail::is_base_of(to_data_type, from_data_type) ) { - const class_type& to_data_class = to_data_type.as_class(); - const class_type& from_data_class = from_data_type.as_class(); - - void* to_ptr = detail::pointer_upcast(*from_data_ptr, from_data_class, to_data_class); - return static_cast(to_ptr); - } - } - } + if ( const uarg varg{registry, *this}; varg.can_cast_to(registry) ) { + return varg.cast(registry); } - if constexpr ( !detail::pointer_kind ) { - if ( from_type == to_type ) { - const T* to_ptr = static_cast(get_data()); - return to_ptr; - } + return nullptr; + } - if ( detail::is_base_of(to_type, from_type) ) { - const class_type& to_class = to_type.as_class(); - const class_type& from_class = from_type.as_class(); + template < typename T > + requires(!detail::pointer_kind) + T* uvalue::try_as() noexcept { + static_assert(std::is_same_v>); - const T* to_ptr = static_cast(detail::pointer_upcast(get_data(), from_class, to_class)); - return to_ptr; - } + using namespace detail; + type_registry& registry{type_registry::instance()}; + + if ( const uarg varg{registry, *this}; varg.can_cast_to(registry) ) { + return std::addressof(varg.cast(registry)); + } + + return nullptr; + } + + template < typename T > + requires(!detail::pointer_kind) + const T* uvalue::try_as() const noexcept { + static_assert(std::is_same_v>); + + using namespace detail; + type_registry& registry{type_registry::instance()}; + + if ( const uarg varg{registry, *this}; varg.can_cast_to(registry) ) { + return std::addressof(varg.cast(registry)); } return nullptr;