diff --git a/headers/meta.hpp/meta_uvalue.hpp b/headers/meta.hpp/meta_uvalue.hpp index e01ab62..d9101d9 100644 --- a/headers/meta.hpp/meta_uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue.hpp @@ -68,6 +68,14 @@ namespace meta_hpp [[nodiscard]] auto get_as() const -> std::conditional_t, T, const T&>; + template < typename T > + [[nodiscard]] auto try_get_as() noexcept + -> std::conditional_t, T, T*>; + + template < typename T > + [[nodiscard]] auto try_get_as() const noexcept + -> std::conditional_t, T, const T*>; + template < typename T > [[nodiscard]] bool can_get_as() const noexcept; diff --git a/headers/meta.hpp/meta_uvalue/uvalue.hpp b/headers/meta.hpp/meta_uvalue/uvalue.hpp index 4fbce14..5f4084e 100644 --- a/headers/meta.hpp/meta_uvalue/uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue/uvalue.hpp @@ -330,27 +330,49 @@ namespace meta_hpp auto uvalue::get_as() -> std::conditional_t, T, T&> { static_assert(std::is_same_v>); + if constexpr ( detail::pointer_kind ) { + if ( T ptr = try_get_as(); ptr || get_type().is_nullptr() ) { + return ptr; + } + } else { + if ( T* ptr = try_get_as() ) { + return *ptr; + } + } + + detail::throw_exception_with("bad value cast"); + } + + template < typename T > + auto uvalue::get_as() const -> std::conditional_t, T, const T&> { + static_assert(std::is_same_v>); + + if constexpr ( detail::pointer_kind ) { + if ( T ptr = try_get_as(); ptr || get_type().is_nullptr() ) { + return ptr; + } + } else { + if ( const T* ptr = try_get_as() ) { + return *ptr; + } + } + + detail::throw_exception_with("bad value cast"); + } + + template < typename T > + // NOLINTNEXTLINE(*-function-cognitive-complexity) + auto uvalue::try_get_as() noexcept -> std::conditional_t, T, T*> { + static_assert(std::is_same_v>); + const any_type& from_type = get_type(); const any_type& to_type = resolve_type(); - if ( from_type == to_type ) { - T* to_ptr = static_cast(data()); - return *to_ptr; - } - 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())); }; - if ( is_a(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(data(), from_class, to_class)); - return *to_ptr; - } - if constexpr ( detail::pointer_kind ) { if ( to_type.is_pointer() && from_type.is_nullptr() ) { return static_cast(nullptr); @@ -385,34 +407,37 @@ namespace meta_hpp } } - detail::throw_exception_with("bad value cast"); + if constexpr ( !detail::pointer_kind ) { + if ( from_type == to_type ) { + T* to_ptr = static_cast(data()); + return to_ptr; + } + + if ( is_a(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(data(), from_class, to_class)); + return to_ptr; + } + } + + return nullptr; } template < typename T > - auto uvalue::get_as() const -> std::conditional_t, T, const T&> { + // NOLINTNEXTLINE(*-function-cognitive-complexity) + auto uvalue::try_get_as() const noexcept -> std::conditional_t, T, const T*> { static_assert(std::is_same_v>); const any_type& from_type = get_type(); const any_type& to_type = resolve_type(); - if ( from_type == to_type ) { - const T* to_ptr = static_cast(data()); - return *to_ptr; - } - 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())); }; - if ( is_a(to_type, from_type) ) { - const class_type& to_class = to_type.as_class(); - const class_type& from_class = from_type.as_class(); - - const T* to_ptr = static_cast(detail::pointer_upcast(data(), from_class, to_class)); - return *to_ptr; - } - if constexpr ( detail::pointer_kind ) { if ( to_type.is_pointer() && from_type.is_nullptr() ) { return static_cast(nullptr); @@ -447,49 +472,35 @@ namespace meta_hpp } } - detail::throw_exception_with("bad value cast"); + if constexpr ( !detail::pointer_kind ) { + if ( from_type == to_type ) { + const T* to_ptr = static_cast(data()); + return to_ptr; + } + + if ( is_a(to_type, from_type) ) { + const class_type& to_class = to_type.as_class(); + const class_type& from_class = from_type.as_class(); + + const T* to_ptr = static_cast(detail::pointer_upcast(data(), from_class, to_class)); + return to_ptr; + } + } + + return nullptr; } template < typename T > bool uvalue::can_get_as() const noexcept { static_assert(std::is_same_v>); - const any_type& from_type = get_type(); - const any_type& to_type = resolve_type(); - - if ( from_type == to_type ) { - return true; - } - - 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())); - }; - - if ( is_a(to_type, from_type) ) { - return true; - } - if constexpr ( detail::pointer_kind ) { - if ( to_type.is_pointer() && from_type.is_nullptr() ) { + if ( T ptr = try_get_as(); ptr || get_type().is_nullptr() ) { 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); - - 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 ) { - if ( to_data_type.is_void() || is_a(to_data_type, from_data_type) ) { - return true; - } - } + } else { + if ( const T* ptr = try_get_as() ) { + return true; } } diff --git a/untests/meta_utilities/value4_tests.cpp b/untests/meta_utilities/value3_tests.cpp similarity index 53% rename from untests/meta_utilities/value4_tests.cpp rename to untests/meta_utilities/value3_tests.cpp index 621e175..bef64a9 100644 --- a/untests/meta_utilities/value4_tests.cpp +++ b/untests/meta_utilities/value3_tests.cpp @@ -61,14 +61,6 @@ TEST_CASE("meta/meta_utilities/value4/get_type") { const derived d{}; CHECK(meta::uvalue{d}.get_type() == meta::resolve_type()); } - { - derived d{}; - CHECK(meta::uvalue{std::move(d)}.get_type() == meta::resolve_type()); - } - { - const derived d{}; - CHECK(meta::uvalue{std::move(d)}.get_type() == meta::resolve_type()); - } } SUBCASE("from ptr") { @@ -119,21 +111,11 @@ TEST_CASE("meta/meta_utilities/value4/get_as") { CHECK(v.get_as().l == 168); CHECK_THROWS(std::ignore = v.get_as()); } - { - meta::uvalue v{derived{}}; - CHECK(std::move(v).get_as().l == 168); - CHECK_THROWS(std::ignore = std::move(v).get_as()); - } { const meta::uvalue v{derived{}}; CHECK(v.get_as().l == 168); CHECK_THROWS(std::ignore = v.get_as()); } - { - const meta::uvalue v{derived{}}; - CHECK(std::move(v).get_as().l == 168); - CHECK_THROWS(std::ignore = std::move(v).get_as()); - } } SUBCASE("derived to base") { @@ -141,18 +123,10 @@ TEST_CASE("meta/meta_utilities/value4/get_as") { meta::uvalue v{derived{}}; CHECK(v.get_as().k == 84); } - { - meta::uvalue v{derived{}}; - CHECK(std::move(v).get_as().k == 84); - } { const meta::uvalue v{derived{}}; CHECK(v.get_as().k == 84); } - { - const meta::uvalue v{derived{}}; - CHECK(std::move(v).get_as().k == 84); - } } SUBCASE("voidptr") { @@ -161,16 +135,17 @@ TEST_CASE("meta/meta_utilities/value4/get_as") { meta::uvalue v{&d}; CHECK(v.get_as() == &d); CHECK(v.get_as() == &d); - CHECK(std::move(v).get_as() == &d); - CHECK(std::move(v).get_as() == &d); } { const derived d{}; meta::uvalue v{&d}; CHECK_THROWS(std::ignore = v.get_as()); CHECK(v.get_as() == &d); - CHECK_THROWS(std::ignore = std::move(v).get_as()); - CHECK(std::move(v).get_as() == &d); + } + { + meta::uvalue v{derived{}}; + CHECK_THROWS(std::ignore = v.get_as()); + CHECK_THROWS(std::ignore = v.get_as()); } } @@ -181,10 +156,8 @@ TEST_CASE("meta/meta_utilities/value4/get_as") { CHECK(v.get_as() == nullptr); CHECK(v.get_as() == nullptr); CHECK(v.get_as() == nullptr); - CHECK(std::move(v).get_as() == nullptr); - CHECK(std::move(v).get_as() == nullptr); - CHECK(std::move(v).get_as() == nullptr); - CHECK(std::move(v).get_as() == nullptr); + + CHECK_THROWS(std::ignore = v.get_as()); } { const meta::uvalue v{nullptr}; @@ -192,10 +165,8 @@ TEST_CASE("meta/meta_utilities/value4/get_as") { CHECK(v.get_as() == nullptr); CHECK(v.get_as() == nullptr); CHECK(v.get_as() == nullptr); - CHECK(std::move(v).get_as() == nullptr); - CHECK(std::move(v).get_as() == nullptr); - CHECK(std::move(v).get_as() == nullptr); - CHECK(std::move(v).get_as() == nullptr); + + CHECK_THROWS(std::ignore = v.get_as()); } } @@ -209,15 +180,6 @@ TEST_CASE("meta/meta_utilities/value4/get_as") { CHECK_THROWS(std::ignore = v.get_as()); CHECK_THROWS(std::ignore = v.get_as()); } - { - derived d{}; - meta::uvalue v{&d}; - CHECK(std::move(v).get_as()->l == 168); - CHECK(std::move(v).get_as()->l == 168); - - CHECK_THROWS(std::ignore = std::move(v).get_as()); - CHECK_THROWS(std::ignore = std::move(v).get_as()); - } { const derived d{}; meta::uvalue v{&d}; @@ -225,13 +187,6 @@ TEST_CASE("meta/meta_utilities/value4/get_as") { CHECK_THROWS(std::ignore = v.get_as()); } - { - const derived d{}; - meta::uvalue v{&d}; - CHECK(std::move(v).get_as()->l == 168); - - CHECK_THROWS(std::ignore = std::move(v).get_as()); - } } SUBCASE("derived* to base*") { @@ -251,6 +206,125 @@ TEST_CASE("meta/meta_utilities/value4/get_as") { } } +TEST_CASE("meta/meta_utilities/value4/try_get_as") { + namespace meta = meta_hpp; + + static_assert(std::is_same_v().try_get_as()), derived*>); + static_assert(std::is_same_v().try_get_as()), derived*>); + static_assert(std::is_same_v().try_get_as()), const derived*>); + static_assert(std::is_same_v().try_get_as()), const derived*>); + + static_assert(std::is_same_v().try_get_as()), derived*>); + static_assert(std::is_same_v().try_get_as()), derived*>); + static_assert(std::is_same_v().try_get_as()), derived*>); + static_assert(std::is_same_v().try_get_as()), derived*>); + + static_assert(std::is_same_v().try_get_as()), const derived*>); + static_assert(std::is_same_v().try_get_as()), const derived*>); + static_assert(std::is_same_v().try_get_as()), const derived*>); + static_assert(std::is_same_v().try_get_as()), const derived*>); + + SUBCASE("derived to derived") { + { + meta::uvalue v{derived{}}; + CHECK(v.try_get_as()->l == 168); + CHECK_FALSE(v.try_get_as()); + } + { + const meta::uvalue v{derived{}}; + CHECK(v.try_get_as()->l == 168); + CHECK_FALSE(v.try_get_as()); + } + } + + SUBCASE("derived to base") { + { + meta::uvalue v{derived{}}; + CHECK(v.try_get_as()->k == 84); + } + { + const meta::uvalue v{derived{}}; + CHECK(v.try_get_as()->k == 84); + } + } + + SUBCASE("voidptr") { + { + derived d{}; + meta::uvalue v{&d}; + CHECK(v.try_get_as() == &d); + CHECK(v.try_get_as() == &d); + } + { + const derived d{}; + meta::uvalue v{&d}; + CHECK_FALSE(v.try_get_as()); + CHECK(v.try_get_as() == &d); + } + { + meta::uvalue v{derived{}}; + CHECK_FALSE(v.try_get_as()); + CHECK_FALSE(v.try_get_as()); + } + } + + SUBCASE("nullptr") { + { + meta::uvalue v{nullptr}; + CHECK(v.try_get_as() == nullptr); + CHECK(v.try_get_as() == nullptr); + CHECK(v.try_get_as() == nullptr); + CHECK(v.try_get_as() == nullptr); + + CHECK_FALSE(v.try_get_as()); + } + { + const meta::uvalue v{nullptr}; + CHECK(v.try_get_as() == nullptr); + CHECK(v.try_get_as() == nullptr); + CHECK(v.try_get_as() == nullptr); + CHECK(v.try_get_as() == nullptr); + + CHECK_FALSE(v.try_get_as()); + } + } + + SUBCASE("derived* to derived*") { + { + derived d{}; + meta::uvalue v{&d}; + CHECK(v.try_get_as()->l == 168); + CHECK(v.try_get_as()->l == 168); + + CHECK_FALSE(v.try_get_as()); + CHECK_FALSE(v.try_get_as()); + } + { + const derived d{}; + meta::uvalue v{&d}; + CHECK(v.try_get_as()->l == 168); + + CHECK_FALSE(v.try_get_as()); + } + } + + SUBCASE("derived* to base*") { + { + derived d{}; + meta::uvalue v{&d}; + CHECK(v.try_get_as()->k == 84); + CHECK(v.try_get_as()->k == 84); + } + + { + const derived d{}; + meta::uvalue v{&d}; + CHECK_FALSE(v.try_get_as()); + CHECK(v.try_get_as()->k == 84); + } + } +} + TEST_CASE("meta/meta_utilities/value4/can_get_as") { namespace meta = meta_hpp; @@ -260,20 +334,97 @@ TEST_CASE("meta/meta_utilities/value4/can_get_as") { CHECK(v.can_get_as()); CHECK_FALSE(v.can_get_as()); } - { - meta::uvalue v{derived{}}; - CHECK(std::move(v).can_get_as()); - CHECK_FALSE(std::move(v).can_get_as()); - } { const meta::uvalue v{derived{}}; CHECK(v.can_get_as()); CHECK_FALSE(v.can_get_as()); } + } + + SUBCASE("derived to base") { + { + meta::uvalue v{derived{}}; + CHECK(v.can_get_as()); + } { const meta::uvalue v{derived{}}; - CHECK(std::move(v).can_get_as()); - CHECK_FALSE(std::move(v).can_get_as()); + CHECK(v.can_get_as()); + } + } + + SUBCASE("voidptr") { + { + derived d{}; + meta::uvalue v{&d}; + CHECK(v.can_get_as()); + CHECK(v.can_get_as()); + } + { + const derived d{}; + meta::uvalue v{&d}; + CHECK_FALSE(v.can_get_as()); + CHECK(v.can_get_as()); + } + { + meta::uvalue v{derived{}}; + CHECK_FALSE(v.can_get_as()); + CHECK_FALSE(v.can_get_as()); + } + } + + SUBCASE("nullptr") { + { + meta::uvalue v{nullptr}; + CHECK(v.can_get_as()); + CHECK(v.can_get_as()); + CHECK(v.can_get_as()); + CHECK(v.can_get_as()); + + CHECK_FALSE(v.can_get_as()); + } + { + const meta::uvalue v{nullptr}; + CHECK(v.can_get_as()); + CHECK(v.can_get_as()); + CHECK(v.can_get_as()); + CHECK(v.can_get_as()); + + CHECK_FALSE(v.can_get_as()); + } + } + + SUBCASE("derived* to derived*") { + { + derived d{}; + meta::uvalue v{&d}; + CHECK(v.can_get_as()); + CHECK(v.can_get_as()); + + CHECK_FALSE(v.can_get_as()); + CHECK_FALSE(v.can_get_as()); + } + { + const derived d{}; + meta::uvalue v{&d}; + CHECK(v.can_get_as()); + + CHECK_FALSE(v.can_get_as()); + } + } + + SUBCASE("derived* to base*") { + { + derived d{}; + meta::uvalue v{&d}; + CHECK(v.can_get_as()); + CHECK(v.can_get_as()); + } + + { + const derived d{}; + meta::uvalue v{&d}; + CHECK_FALSE(v.can_get_as()); + CHECK(v.can_get_as()); } } }