From 58c7a31af5f8d31983882cc356c6208a3f2ca0d1 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 17 Feb 2023 03:03:17 +0700 Subject: [PATCH] new uresult class for safe api --- develop/singles/headers/meta.hpp/meta_all.hpp | 386 +++++++++++++++--- .../untests/meta_utilities/result_tests.cpp | 306 ++++++++++++++ headers/meta.hpp/meta_all.hpp | 3 + headers/meta.hpp/meta_base.hpp | 3 + .../meta_detail/value_utilities/uarg.hpp | 17 +- .../meta_detail/value_utilities/uinst.hpp | 17 +- .../meta_detail/value_utilities/utraits.hpp | 54 ++- headers/meta.hpp/meta_uresult.hpp | 146 +++++++ headers/meta.hpp/meta_uresult/uresult.hpp | 156 +++++++ headers/meta.hpp/meta_uvalue.hpp | 15 - 10 files changed, 991 insertions(+), 112 deletions(-) create mode 100644 develop/untests/meta_utilities/result_tests.cpp create mode 100644 headers/meta.hpp/meta_uresult.hpp create mode 100644 headers/meta.hpp/meta_uresult/uresult.hpp diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index adcc5c6..1bf0747 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -1430,6 +1430,9 @@ namespace meta_hpp namespace meta_hpp { + class uerror; + class uresult; + class uvalue; namespace detail @@ -2859,21 +2862,6 @@ namespace std }; } -namespace meta_hpp::detail -{ - template < typename T > - concept uvalue_kind // - = std::is_same_v; // - - template < typename T > - concept any_uvalue_kind // - = std::is_same_v // - || std::is_same_v // - || std::is_same_v // - || std::is_same_v // - || std::is_same_v; // -} - namespace meta_hpp { class uvalue final { @@ -5203,37 +5191,49 @@ namespace meta_hpp bool is_invocable_with(Instance&& instance, Args&&... args); } +namespace meta_hpp::detail +{ + template < typename T > + concept any_uvalue_kind // + = std::is_same_v // + || std::is_same_v // + || std::is_same_v // + || std::is_same_v // + || std::is_same_v // + || std::is_same_v; // +} + namespace meta_hpp::detail { template < typename T, typename Tp = std::decay_t > - concept arg_lvalue_ref_kind // - = (!any_uvalue_kind) // - && (std::is_lvalue_reference_v); // + concept arg_lvalue_ref_kind // + = (!any_uvalue_kind) // + && (std::is_lvalue_reference_v); template < typename T, typename Tp = std::decay_t > - concept arg_rvalue_ref_kind // - = (!any_uvalue_kind) // - && (!std::is_reference_v || std::is_rvalue_reference_v); // + concept arg_rvalue_ref_kind // + = (!any_uvalue_kind) // + && (!std::is_reference_v || std::is_rvalue_reference_v); } namespace meta_hpp::detail { template < typename T > - concept inst_class_ref_kind // - = (std::is_class_v) // - || (std::is_reference_v && std::is_class_v>); // + concept inst_class_ref_kind // + = (std::is_class_v) // + || (std::is_reference_v && std::is_class_v>); template < typename T, typename Tp = std::decay_t > - concept inst_class_lvalue_ref_kind // - = (!any_uvalue_kind) // - && (std::is_lvalue_reference_v) // - && (std::is_class_v>>); // + concept inst_class_lvalue_ref_kind // + = (!any_uvalue_kind) // + && (std::is_lvalue_reference_v) // + && (std::is_class_v>>); template < typename T, typename Tp = std::decay_t > - concept inst_class_rvalue_ref_kind // - = (!any_uvalue_kind) // - && (!std::is_reference_v || std::is_rvalue_reference_v) // - && (std::is_class_v>>); // + concept inst_class_rvalue_ref_kind // + = (!any_uvalue_kind) // + && (!std::is_reference_v || std::is_rvalue_reference_v) // + && (std::is_class_v>>); } namespace meta_hpp::detail @@ -5330,12 +5330,16 @@ namespace meta_hpp::detail template < class_kind To, class_kind From > [[nodiscard]] To* pointer_upcast(type_registry& registry, From* ptr) { - return static_cast(pointer_upcast(registry, ptr, registry.resolve_type(), registry.resolve_type())); + const class_type& to_class = registry.resolve_type(); + const class_type& from_class = registry.resolve_type(); + return static_cast(pointer_upcast(registry, ptr, from_class, to_class)); } template < class_kind To, class_kind From > [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { - return static_cast(pointer_upcast(registry, ptr, registry.resolve_type(), registry.resolve_type())); + const class_type& to_class = registry.resolve_type(); + const class_type& from_class = registry.resolve_type(); + return static_cast(pointer_upcast(registry, ptr, from_class, to_class)); } } @@ -5360,12 +5364,8 @@ namespace meta_hpp::detail uarg_base& operator=(uarg_base&&) = delete; uarg_base& operator=(const uarg_base&) = delete; - template < typename T > - uarg_base(type_list) = delete; - template < typename T, typename Tp = std::decay_t > requires(!any_uvalue_kind) - // NOLINTNEXTLINE(*-forwarding-reference-overload) explicit uarg_base(type_registry& registry, T&&) : uarg_base{registry, type_list{}} {} @@ -5435,26 +5435,25 @@ namespace meta_hpp::detail uarg& operator=(uarg&&) = delete; uarg& operator=(const uarg&) = delete; - template < typename T, uvalue_kind Tp = std::decay_t > - // NOLINTNEXTLINE(*-forwarding-reference-overload) + template < typename T, typename Tp = std::decay_t > + requires std::is_same_v explicit uarg(type_registry& registry, T&& v) : uarg_base{registry, std::forward(v)} , data_{const_cast(v.get_data())} {} // NOLINT(*-const-cast) template < typename T, typename Tp = std::decay_t > requires(!any_uvalue_kind) - // NOLINTNEXTLINE(*-forwarding-reference-overload) explicit uarg(type_registry& registry, T&& v) : uarg_base{registry, std::forward(v)} , data_{const_cast*>(std::addressof(v))} {} // NOLINT(*-const-cast) template < typename To > requires(std::is_pointer_v) - [[nodiscard]] To cast(type_registry& registry) const; + [[nodiscard]] decltype(auto) cast(type_registry& registry) const; template < typename To > requires(!std::is_pointer_v) - [[nodiscard]] To cast(type_registry& registry) const; + [[nodiscard]] decltype(auto) cast(type_registry& registry) const; private: void* data_{}; @@ -5585,7 +5584,7 @@ namespace meta_hpp::detail { template < typename To > requires(std::is_pointer_v) - [[nodiscard]] To uarg::cast(type_registry& registry) const { + [[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; @@ -5645,7 +5644,7 @@ namespace meta_hpp::detail template < typename To > requires(!std::is_pointer_v) - [[nodiscard]] To uarg::cast(type_registry& registry) const { + [[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; @@ -5952,12 +5951,8 @@ namespace meta_hpp::detail uinst_base& operator=(uinst_base&&) = delete; uinst_base& operator=(const uinst_base&) = delete; - template < typename T > - uinst_base(type_list) = delete; - template < typename T, typename Tp = std::decay_t > requires(!any_uvalue_kind) - // NOLINTNEXTLINE(*-forwarding-reference-overload) explicit uinst_base(type_registry& registry, T&&) : uinst_base{registry, type_list{}} {} @@ -6027,18 +6022,17 @@ namespace meta_hpp::detail uinst& operator=(uinst&&) = delete; uinst& operator=(const uinst&) = delete; - template < typename T, uvalue_kind Tp = std::decay_t > - // NOLINTNEXTLINE(*-forwarding-reference-overload) + template < typename T, typename Tp = std::decay_t > + requires std::is_same_v explicit uinst(type_registry& registry, T&& v) - : uinst_base{registry, std::forward(v)} // NOLINTNEXTLINE(*-const-cast) - , data_{const_cast(v.get_data())} {} + : uinst_base{registry, std::forward(v)} + , data_{const_cast(v.get_data())} {} // NOLINT(*-const-cast) template < typename T, typename Tp = std::decay_t > requires(!any_uvalue_kind) - // NOLINTNEXTLINE(*-forwarding-reference-overload) explicit uinst(type_registry& registry, T&& v) - : uinst_base{registry, std::forward(v)} // NOLINTNEXTLINE(*-const-cast) - , data_{const_cast*>(std::addressof(v))} {} + : uinst_base{registry, std::forward(v)} + , data_{const_cast*>(std::addressof(v))} {} // NOLINT(*-const-cast) template < inst_class_ref_kind Q > [[nodiscard]] decltype(auto) cast(type_registry& registry) const; @@ -8164,6 +8158,142 @@ namespace meta_hpp::detail : type_data_base{type_id{type_list>{}}, type_kind::void_} {} } +namespace meta_hpp +{ + class uerror final { + public: + uerror() = default; + ~uerror() = default; + + uerror(uerror&&) noexcept = default; + uerror(const uerror&) noexcept = default; + + uerror& operator=(uerror&&) noexcept = default; + uerror& operator=(const uerror&) noexcept = default; + + explicit uerror(error_code error) noexcept; + uerror& operator=(error_code error) noexcept; + + [[nodiscard]] error_code get_error() const noexcept; + + void reset() noexcept; + void swap(uerror& other) noexcept; + + [[nodiscard]] std::size_t get_hash() const noexcept; + [[nodiscard]] std::strong_ordering operator<=>(const uerror& other) const = default; + + private: + error_code error_{error_code::no_error}; + }; + + inline void swap(uerror& l, uerror& r) noexcept { + l.swap(r); + } + + inline uerror make_uerror(error_code error) { + return uerror{error}; + } +} + +namespace std +{ + template <> + struct hash { + size_t operator()(meta_hpp::uerror ue) const noexcept { + return ue.get_hash(); + } + }; +} + +namespace meta_hpp +{ + class uresult final { + public: + uresult() = default; + ~uresult() = default; + + uresult(uresult&&) noexcept = default; + uresult(const uresult&) = default; + + uresult& operator=(uresult&&) noexcept = default; + uresult& operator=(const uresult&) = default; + + explicit(false) uresult(uerror error) noexcept; + explicit(false) uresult(uvalue value) noexcept; + + uresult& operator=(uerror error) noexcept; + uresult& operator=(uvalue value) noexcept; + + template < typename T, typename Tp = std::decay_t > + requires(!std::is_same_v) // + && (!std::is_same_v) // + && (!std::is_same_v) // + && (!detail::is_in_place_type_v) // + && (std::is_copy_constructible_v) // + // NOLINTNEXTLINE(*-forwarding-reference-overload) + uresult(T&& val); + + template < typename T, typename Tp = std::decay_t > + requires(!std::is_same_v) // + && (!std::is_same_v) // + && (!std::is_same_v) // + && (std::is_copy_constructible_v) // + uresult& operator=(T&& val); + + template < typename T, typename... Args, typename Tp = std::decay_t > + requires std::is_copy_constructible_v // + && std::is_constructible_v // + explicit uresult(std::in_place_type_t, Args&&... args); + + template < typename T, typename U, typename... Args, typename Tp = std::decay_t > + requires std::is_copy_constructible_v // + && std::is_constructible_v&, Args...> // + explicit uresult(std::in_place_type_t, std::initializer_list ilist, Args&&... args); + + template < typename T, typename... Args, typename Tp = std::decay_t > + requires std::is_copy_constructible_v // + && std::is_constructible_v // + Tp& emplace(Args&&... args); + + template < typename T, typename U, typename... Args, typename Tp = std::decay_t > + requires std::is_copy_constructible_v // + && std::is_constructible_v&, Args...> // + Tp& emplace(std::initializer_list ilist, Args&&... args); + + [[nodiscard]] bool has_error() const noexcept; + [[nodiscard]] bool has_value() const noexcept; + [[nodiscard]] explicit operator bool() const noexcept; + + void reset() noexcept; + void swap(uresult& other) noexcept; + + [[nodiscard]] uvalue& get_value() &; + [[nodiscard]] uvalue&& get_value() &&; + [[nodiscard]] const uvalue& get_value() const&; + [[nodiscard]] const uvalue&& get_value() const&&; + + [[nodiscard]] error_code get_error() const noexcept; + + private: + uvalue value_{}; + error_code error_{error_code::no_error}; + }; + + inline void swap(uresult& l, uresult& r) noexcept { + l.swap(r); + } + + template < typename T, typename... Args > + uresult make_uresult(Args&&... args) { + return uresult(std::in_place_type, std::forward(args)...); + } + + template < typename T, typename U, typename... Args > + uresult make_uresult(std::initializer_list ilist, Args&&... args) { + return uresult(std::in_place_type, ilist, std::forward(args)...); + } +} + namespace meta_hpp::detail { template < typename T > @@ -8997,3 +9127,147 @@ namespace meta_hpp return std::nullopt; } } + +namespace meta_hpp +{ + inline uerror::uerror(error_code error) noexcept + : error_{error} {} + + inline uerror& uerror::operator=(error_code error) noexcept { + error_ = error; + return *this; + } + + inline error_code uerror::get_error() const noexcept { + return error_; + } + + inline void uerror::reset() noexcept { + error_ = error_code::no_error; + } + + inline void uerror::swap(uerror& other) noexcept { + using std::swap; + swap(error_, other.error_); + } + + inline std::size_t uerror::get_hash() const noexcept { + return std::hash{}(error_); + } +} + +namespace meta_hpp +{ + inline uresult::uresult(uerror error) noexcept + : error_{error.get_error()} {} + + inline uresult::uresult(uvalue value) noexcept + : value_{std::move(value)} {} + + inline uresult& uresult::operator=(uerror error) noexcept { + value_ = uvalue{}; + error_ = error.get_error(); + return *this; + } + + inline uresult& uresult::operator=(uvalue value) noexcept { + value_ = std::move(value); + error_ = error_code::no_error; + return *this; + } + + template < typename T, typename Tp > + requires(!std::is_same_v) // + && (!std::is_same_v) // + && (!std::is_same_v) // + && (!detail::is_in_place_type_v) // + && (std::is_copy_constructible_v) // + // NOLINTNEXTLINE(*-forwarding-reference-overload) + uresult::uresult(T&& val) + : value_{std::forward(val)} {} + + template < typename T, typename Tp > + requires(!std::is_same_v) // + && (!std::is_same_v) // + && (!std::is_same_v) // + && (std::is_copy_constructible_v) // + uresult& uresult::operator=(T&& val) { + value_ = std::forward(val); + error_ = error_code::no_error; + return *this; + } + + template < typename T, typename... Args, typename Tp > + requires std::is_copy_constructible_v // + && std::is_constructible_v // + uresult::uresult(std::in_place_type_t, Args&&... args) + : value_{std::in_place_type, std::forward(args)...} {} + + template < typename T, typename U, typename... Args, typename Tp > + requires std::is_copy_constructible_v // + && std::is_constructible_v&, Args...> // + uresult::uresult(std::in_place_type_t, std::initializer_list ilist, Args&&... args) + : value_{std::in_place_type, ilist, std::forward(args)...} {} + + template < typename T, typename... Args, typename Tp > + requires std::is_copy_constructible_v // + && std::is_constructible_v // + Tp& uresult::emplace(Args&&... args) { + Tp& val{value_.emplace(std::forward(args)...)}; + error_ = error_code::no_error; + return val; + } + + template < typename T, typename U, typename... Args, typename Tp > + requires std::is_copy_constructible_v // + && std::is_constructible_v&, Args...> // + Tp& uresult::emplace(std::initializer_list ilist, Args&&... args) { + Tp& val{value_.emplace(ilist, std::forward(args)...)}; + error_ = error_code::no_error; + return val; + } + + inline bool uresult::has_error() const noexcept { + return error_ != error_code::no_error; + } + + inline bool uresult::has_value() const noexcept { + return error_ == error_code::no_error; + } + + inline uresult::operator bool() const noexcept { + return has_value(); + } + + inline void uresult::reset() noexcept { + value_ = uvalue{}; + error_ = error_code::no_error; + } + + inline void uresult::swap(uresult& other) noexcept { + using std::swap; + swap(value_, other.value_); + swap(error_, other.error_); + } + + inline uvalue& uresult::get_value() & { + return value_; + } + + inline uvalue&& uresult::get_value() && { + return std::move(value_); + } + + inline const uvalue& uresult::get_value() const& { + return value_; + } + + inline const uvalue&& uresult::get_value() const&& { + // NOLINTNEXTLINE(*-move-const-arg) + return std::move(value_); + } + + inline error_code uresult::get_error() const noexcept { + return error_; + } +} diff --git a/develop/untests/meta_utilities/result_tests.cpp b/develop/untests/meta_utilities/result_tests.cpp new file mode 100644 index 0000000..83cd891 --- /dev/null +++ b/develop/untests/meta_utilities/result_tests.cpp @@ -0,0 +1,306 @@ +/******************************************************************************* + * 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 ivec2 { + int x{}; + int y{}; + + explicit ivec2(int nv) : x{nv}, y{nv} {} + ivec2(int nx, int ny) : x{nx}, y{ny} {} + }; + + bool operator==(const ivec2& l, const ivec2& r) noexcept { + return l.x == r.x && l.y == r.y; + } +} + +TEST_CASE("meta/meta_utilities/uerror") { + namespace meta = meta_hpp; + + SUBCASE("ctors") { + { + meta::uerror err{}; + CHECK(err.get_error() == meta::error_code::no_error); + } + { + meta::uerror err{meta::error_code::bad_argument_cast}; + CHECK(err.get_error() == meta::error_code::bad_argument_cast); + } + { + meta::uerror err{meta::error_code::bad_instance_cast}; + CHECK(err.get_error() == meta::error_code::bad_instance_cast); + } + { + meta::uerror err1{meta::error_code::bad_argument_cast}; + meta::uerror err2{err1}; + CHECK(err2.get_error() == meta::error_code::bad_argument_cast); + } + { + meta::uerror err1{meta::error_code::bad_argument_cast}; + meta::uerror err2{std::move(err1)}; + CHECK(err2.get_error() == meta::error_code::bad_argument_cast); + } + } + + SUBCASE("operator=") { + { + meta::uerror err1{meta::error_code::bad_argument_cast}; + meta::uerror err2{meta::error_code::bad_instance_cast}; + err1 = err2; + CHECK(err1.get_error() == meta::error_code::bad_instance_cast); + } + { + meta::uerror err1{meta::error_code::bad_argument_cast}; + meta::uerror err2{meta::error_code::bad_instance_cast}; + err1 = std::move(err2); + CHECK(err1.get_error() == meta::error_code::bad_instance_cast); + } + { + meta::uerror err1{meta::error_code::bad_argument_cast}; + err1 = meta::error_code::bad_instance_cast; + CHECK(err1.get_error() == meta::error_code::bad_instance_cast); + } + } + + SUBCASE("operator==") { + meta::uerror err1{meta::error_code::bad_argument_cast}; + meta::uerror err2{meta::error_code::bad_instance_cast}; + meta::uerror err3{meta::error_code::bad_instance_cast}; + + CHECK_FALSE(err1 == err2); + CHECK(err2 == err3); + } + + SUBCASE("hash") { + meta::uerror err1{meta::error_code::bad_argument_cast}; + meta::uerror err2{meta::error_code::bad_instance_cast}; + meta::uerror err3{meta::error_code::bad_instance_cast}; + + CHECK_FALSE(err1.get_hash() == err2.get_hash()); + CHECK(err2.get_hash() == err3.get_hash()); + + CHECK_FALSE(std::hash{}(err1) == err2.get_hash()); + CHECK(std::hash{}(err2) == err3.get_hash()); + } + + SUBCASE("reset") { + meta::uerror err{meta::error_code::bad_argument_cast}; + err.reset(); + CHECK(err.get_error() == meta::error_code::no_error); + } + + SUBCASE("swap") { + meta::uerror err1{meta::error_code::bad_argument_cast}; + meta::uerror err2{meta::error_code::bad_instance_cast}; + + err1.swap(err2); + CHECK(err1.get_error() == meta::error_code::bad_instance_cast); + CHECK(err2.get_error() == meta::error_code::bad_argument_cast); + + swap(err1, err2); + CHECK(err1.get_error() == meta::error_code::bad_argument_cast); + CHECK(err2.get_error() == meta::error_code::bad_instance_cast); + } + + SUBCASE("make_uerror") { + { + meta::uerror err = meta::make_uerror(meta::error_code::bad_argument_cast); + CHECK(err.get_error() == meta::error_code::bad_argument_cast); + } + { + meta::uerror err = meta::make_uerror(meta::error_code::bad_instance_cast); + CHECK(err.get_error() == meta::error_code::bad_instance_cast); + } + } +} + +TEST_CASE("meta/meta_utilities/uresult") { + namespace meta = meta_hpp; + + SUBCASE("ctor/error") { + { + meta::uresult res{meta::uerror{meta::error_code::bad_argument_cast}}; + CHECK_FALSE(res); + CHECK_FALSE(res.has_value()); + CHECK_FALSE(res.get_value()); + CHECK(res.has_error()); + CHECK(res.get_error() == meta::error_code::bad_argument_cast); + } + { + meta::uresult res{meta::uresult{meta::uerror{meta::error_code::bad_instance_cast}}}; + CHECK_FALSE(res); + CHECK_FALSE(res.has_value()); + CHECK_FALSE(res.get_value()); + CHECK(res.has_error()); + CHECK(res.get_error() == meta::error_code::bad_instance_cast); + } + } + + SUBCASE("ctor/value") { + { + meta::uresult res{ivec2{42, 21}}; + CHECK(res); + CHECK(res.has_value()); + REQUIRE(res.get_value()); + CHECK(res.get_value().get_as() == ivec2{42, 21}); + CHECK_FALSE(res.has_error()); + CHECK(res.get_error() == meta::error_code::no_error); + } + { + meta::uresult res{meta::uvalue{ivec2{42, 21}}}; + CHECK(res); + CHECK(res.has_value()); + REQUIRE(res.get_value()); + CHECK(res.get_value().get_as() == ivec2{42, 21}); + CHECK_FALSE(res.has_error()); + CHECK(res.get_error() == meta::error_code::no_error); + } + { + meta::uresult res{meta::uresult(ivec2{42, 21})}; + CHECK(res); + CHECK(res.has_value()); + REQUIRE(res.get_value()); + CHECK(res.get_value().get_as() == ivec2{42, 21}); + CHECK_FALSE(res.has_error()); + CHECK(res.get_error() == meta::error_code::no_error); + } + } + + SUBCASE("ctor/value/in_place") { + { + meta::uresult res{std::in_place_type, 42, 21}; + CHECK(res); + CHECK(res.has_value()); + REQUIRE(res.get_value()); + CHECK(res.get_value().get_as() == ivec2{42, 21}); + CHECK_FALSE(res.has_error()); + CHECK(res.get_error() == meta::error_code::no_error); + } + { + meta::uresult res{meta::make_uresult(42, 21)}; + CHECK(res); + CHECK(res.has_value()); + REQUIRE(res.get_value()); + CHECK(res.get_value().get_as() == ivec2{42, 21}); + CHECK_FALSE(res.has_error()); + CHECK(res.get_error() == meta::error_code::no_error); + } + { + meta::uresult res{std::in_place_type>, {42, 21}}; + CHECK(res); + CHECK(res.has_value()); + REQUIRE(res.get_value()); + CHECK(res.get_value().get_as>() == std::vector{42, 21}); + CHECK_FALSE(res.has_error()); + CHECK(res.get_error() == meta::error_code::no_error); + } + { + meta::uresult res{meta::make_uresult>({42, 21})}; + CHECK(res); + CHECK(res.has_value()); + REQUIRE(res.get_value()); + CHECK(res.get_value().get_as>() == std::vector{42, 21}); + CHECK_FALSE(res.has_error()); + CHECK(res.get_error() == meta::error_code::no_error); + } + } + + SUBCASE("operator=") { + meta::uresult res{meta::uerror{meta::error_code::bad_argument_cast}}; + + res = ivec2{42, 21}; + CHECK(res); + CHECK(res.has_value()); + REQUIRE(res.get_value()); + CHECK(res.get_value().get_as() == ivec2{42, 21}); + CHECK_FALSE(res.has_error()); + CHECK(res.get_error() == meta::error_code::no_error); + + res = meta::uerror{meta::error_code::bad_argument_cast}; + CHECK_FALSE(res); + CHECK_FALSE(res.has_value()); + CHECK_FALSE(res.get_value()); + CHECK(res.has_error()); + CHECK(res.get_error() == meta::error_code::bad_argument_cast); + + res = meta::uvalue{ivec2{42, 21}}; + CHECK(res); + CHECK(res.has_value()); + REQUIRE(res.get_value()); + CHECK(res.get_value().get_as() == ivec2{42, 21}); + CHECK_FALSE(res.has_error()); + CHECK(res.get_error() == meta::error_code::no_error); + } + + SUBCASE("emplace") { + { + meta::uresult res{ivec2{42, 21}}; + CHECK(res.emplace>({42, 21}) == std::vector{42, 21}); + CHECK(res); + CHECK(res.has_value()); + CHECK(res.get_value().get_as>() == std::vector{42, 21}); + + CHECK(res.emplace(42, 21) == ivec2{42, 21}); + CHECK(res); + CHECK(res.has_value()); + CHECK(res.get_value().get_as() == ivec2{42, 21}); + } + { + meta::uresult res{meta::uerror{meta::error_code::bad_argument_cast}}; + CHECK(res.emplace(42, 21) == ivec2{42, 21}); + CHECK(res); + CHECK(res.has_value()); + CHECK(res.get_value().get_as() == ivec2{42, 21}); + + CHECK(res.emplace>({42, 21}) == std::vector{42, 21}); + CHECK(res); + CHECK(res.has_value()); + CHECK(res.get_value().get_as>() == std::vector{42, 21}); + } + } + + SUBCASE("swap") { + { + meta::uresult res1{ivec2{42}}; + meta::uresult res2{ivec2{21}}; + res1.swap(res2); + CHECK(res1.get_value().get_as() == ivec2{21}); + CHECK(res2.get_value().get_as() == ivec2{42}); + } + { + meta::uresult res1{ivec2{42}}; + meta::uresult res2{meta::uerror{meta::error_code::bad_argument_cast}}; + swap(res1, res2); + CHECK(res1.get_error() == meta::error_code::bad_argument_cast); + CHECK(res2.get_value().get_as() == ivec2{42}); + } + { + meta::uresult res1{meta::uerror{meta::error_code::bad_argument_cast}}; + meta::uresult res2{meta::uerror{meta::error_code::bad_instance_cast}}; + swap(res1, res2); + CHECK(res1.get_error() == meta::error_code::bad_instance_cast); + CHECK(res2.get_error() == meta::error_code::bad_argument_cast); + } + } + + SUBCASE("get_value") { + static_assert(std::is_same_v().get_value())>); + static_assert(std::is_same_v().get_value())>); + static_assert(std::is_same_v().get_value())>); + static_assert(std::is_same_v().get_value())>); + + meta::uresult res{ivec2{42, 21}}; + CHECK(res.get_value().get_as() == ivec2{42, 21}); + CHECK(std::move(res).get_value().get_as() == ivec2{42, 21}); + CHECK(std::as_const(res).get_value().get_as() == ivec2{42, 21}); + CHECK(std::move(std::as_const(res)).get_value().get_as() == ivec2{42, 21}); + } +} diff --git a/headers/meta.hpp/meta_all.hpp b/headers/meta.hpp/meta_all.hpp index 0e71c1d..e5edf2b 100644 --- a/headers/meta.hpp/meta_all.hpp +++ b/headers/meta.hpp/meta_all.hpp @@ -65,5 +65,8 @@ #include "meta_types/reference_type.hpp" #include "meta_types/void_type.hpp" +#include "meta_uresult.hpp" +#include "meta_uresult/uresult.hpp" + #include "meta_uvalue.hpp" #include "meta_uvalue/uvalue.hpp" diff --git a/headers/meta.hpp/meta_base.hpp b/headers/meta.hpp/meta_base.hpp index 90e9b6c..352c9a3 100644 --- a/headers/meta.hpp/meta_base.hpp +++ b/headers/meta.hpp/meta_base.hpp @@ -48,6 +48,9 @@ namespace meta_hpp namespace meta_hpp { + class uerror; + class uresult; + class uvalue; namespace detail diff --git a/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp b/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp index 9f7cafd..1d9445e 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/uarg.hpp @@ -33,12 +33,8 @@ namespace meta_hpp::detail uarg_base& operator=(uarg_base&&) = delete; uarg_base& operator=(const uarg_base&) = delete; - template < typename T > - uarg_base(type_list) = delete; - template < typename T, typename Tp = std::decay_t > requires(!any_uvalue_kind) - // NOLINTNEXTLINE(*-forwarding-reference-overload) explicit uarg_base(type_registry& registry, T&&) : uarg_base{registry, type_list{}} {} @@ -108,26 +104,25 @@ namespace meta_hpp::detail uarg& operator=(uarg&&) = delete; uarg& operator=(const uarg&) = delete; - template < typename T, uvalue_kind Tp = std::decay_t > - // NOLINTNEXTLINE(*-forwarding-reference-overload) + template < typename T, typename Tp = std::decay_t > + requires std::is_same_v explicit uarg(type_registry& registry, T&& v) : uarg_base{registry, std::forward(v)} , data_{const_cast(v.get_data())} {} // NOLINT(*-const-cast) template < typename T, typename Tp = std::decay_t > requires(!any_uvalue_kind) - // NOLINTNEXTLINE(*-forwarding-reference-overload) explicit uarg(type_registry& registry, T&& v) : uarg_base{registry, std::forward(v)} , data_{const_cast*>(std::addressof(v))} {} // NOLINT(*-const-cast) template < typename To > requires(std::is_pointer_v) - [[nodiscard]] To cast(type_registry& registry) const; + [[nodiscard]] decltype(auto) cast(type_registry& registry) const; template < typename To > requires(!std::is_pointer_v) - [[nodiscard]] To cast(type_registry& registry) const; + [[nodiscard]] decltype(auto) cast(type_registry& registry) const; private: void* data_{}; @@ -258,7 +253,7 @@ namespace meta_hpp::detail { template < typename To > requires(std::is_pointer_v) - [[nodiscard]] To uarg::cast(type_registry& registry) const { + [[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; @@ -318,7 +313,7 @@ namespace meta_hpp::detail template < typename To > requires(!std::is_pointer_v) - [[nodiscard]] To uarg::cast(type_registry& registry) const { + [[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; diff --git a/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp b/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp index 4f818d2..526f2bc 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/uinst.hpp @@ -33,12 +33,8 @@ namespace meta_hpp::detail uinst_base& operator=(uinst_base&&) = delete; uinst_base& operator=(const uinst_base&) = delete; - template < typename T > - uinst_base(type_list) = delete; - template < typename T, typename Tp = std::decay_t > requires(!any_uvalue_kind) - // NOLINTNEXTLINE(*-forwarding-reference-overload) explicit uinst_base(type_registry& registry, T&&) : uinst_base{registry, type_list{}} {} @@ -108,18 +104,17 @@ namespace meta_hpp::detail uinst& operator=(uinst&&) = delete; uinst& operator=(const uinst&) = delete; - template < typename T, uvalue_kind Tp = std::decay_t > - // NOLINTNEXTLINE(*-forwarding-reference-overload) + template < typename T, typename Tp = std::decay_t > + requires std::is_same_v explicit uinst(type_registry& registry, T&& v) - : uinst_base{registry, std::forward(v)} // NOLINTNEXTLINE(*-const-cast) - , data_{const_cast(v.get_data())} {} + : uinst_base{registry, std::forward(v)} + , data_{const_cast(v.get_data())} {} // NOLINT(*-const-cast) template < typename T, typename Tp = std::decay_t > requires(!any_uvalue_kind) - // NOLINTNEXTLINE(*-forwarding-reference-overload) explicit uinst(type_registry& registry, T&& v) - : uinst_base{registry, std::forward(v)} // NOLINTNEXTLINE(*-const-cast) - , data_{const_cast*>(std::addressof(v))} {} + : uinst_base{registry, std::forward(v)} + , data_{const_cast*>(std::addressof(v))} {} // NOLINT(*-const-cast) template < inst_class_ref_kind Q > [[nodiscard]] decltype(auto) cast(type_registry& registry) const; diff --git a/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp b/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp index 8041d01..5063e4b 100644 --- a/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp +++ b/headers/meta.hpp/meta_detail/value_utilities/utraits.hpp @@ -10,37 +10,49 @@ #include "../../meta_registry.hpp" #include "../../meta_uvalue.hpp" +namespace meta_hpp::detail +{ + template < typename T > + concept any_uvalue_kind // + = std::is_same_v // + || std::is_same_v // + || std::is_same_v // + || std::is_same_v // + || std::is_same_v // + || std::is_same_v; // +} + namespace meta_hpp::detail { template < typename T, typename Tp = std::decay_t > - concept arg_lvalue_ref_kind // - = (!any_uvalue_kind) // - && (std::is_lvalue_reference_v); // + concept arg_lvalue_ref_kind // + = (!any_uvalue_kind) // + && (std::is_lvalue_reference_v); template < typename T, typename Tp = std::decay_t > - concept arg_rvalue_ref_kind // - = (!any_uvalue_kind) // - && (!std::is_reference_v || std::is_rvalue_reference_v); // + concept arg_rvalue_ref_kind // + = (!any_uvalue_kind) // + && (!std::is_reference_v || std::is_rvalue_reference_v); } namespace meta_hpp::detail { template < typename T > - concept inst_class_ref_kind // - = (std::is_class_v) // - || (std::is_reference_v && std::is_class_v>); // + concept inst_class_ref_kind // + = (std::is_class_v) // + || (std::is_reference_v && std::is_class_v>); template < typename T, typename Tp = std::decay_t > - concept inst_class_lvalue_ref_kind // - = (!any_uvalue_kind) // - && (std::is_lvalue_reference_v) // - && (std::is_class_v>>); // + concept inst_class_lvalue_ref_kind // + = (!any_uvalue_kind) // + && (std::is_lvalue_reference_v) // + && (std::is_class_v>>); template < typename T, typename Tp = std::decay_t > - concept inst_class_rvalue_ref_kind // - = (!any_uvalue_kind) // - && (!std::is_reference_v || std::is_rvalue_reference_v) // - && (std::is_class_v>>); // + concept inst_class_rvalue_ref_kind // + = (!any_uvalue_kind) // + && (!std::is_reference_v || std::is_rvalue_reference_v) // + && (std::is_class_v>>); } namespace meta_hpp::detail @@ -137,11 +149,15 @@ namespace meta_hpp::detail template < class_kind To, class_kind From > [[nodiscard]] To* pointer_upcast(type_registry& registry, From* ptr) { - return static_cast(pointer_upcast(registry, ptr, registry.resolve_type(), registry.resolve_type())); + const class_type& to_class = registry.resolve_type(); + const class_type& from_class = registry.resolve_type(); + return static_cast(pointer_upcast(registry, ptr, from_class, to_class)); } template < class_kind To, class_kind From > [[nodiscard]] const To* pointer_upcast(type_registry& registry, const From* ptr) { - return static_cast(pointer_upcast(registry, ptr, registry.resolve_type(), registry.resolve_type())); + const class_type& to_class = registry.resolve_type(); + const class_type& from_class = registry.resolve_type(); + return static_cast(pointer_upcast(registry, ptr, from_class, to_class)); } } diff --git a/headers/meta.hpp/meta_uresult.hpp b/headers/meta.hpp/meta_uresult.hpp new file mode 100644 index 0000000..4f7ce58 --- /dev/null +++ b/headers/meta.hpp/meta_uresult.hpp @@ -0,0 +1,146 @@ +/******************************************************************************* + * 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 "meta_base.hpp" +#include "meta_uvalue.hpp" + +namespace meta_hpp +{ + class uerror final { + public: + uerror() = default; + ~uerror() = default; + + uerror(uerror&&) noexcept = default; + uerror(const uerror&) noexcept = default; + + uerror& operator=(uerror&&) noexcept = default; + uerror& operator=(const uerror&) noexcept = default; + + explicit uerror(error_code error) noexcept; + uerror& operator=(error_code error) noexcept; + + [[nodiscard]] error_code get_error() const noexcept; + + void reset() noexcept; + void swap(uerror& other) noexcept; + + [[nodiscard]] std::size_t get_hash() const noexcept; + [[nodiscard]] std::strong_ordering operator<=>(const uerror& other) const = default; + + private: + error_code error_{error_code::no_error}; + }; + + inline void swap(uerror& l, uerror& r) noexcept { + l.swap(r); + } + + inline uerror make_uerror(error_code error) { + return uerror{error}; + } +} + +namespace std +{ + template <> + struct hash { + size_t operator()(meta_hpp::uerror ue) const noexcept { + return ue.get_hash(); + } + }; +} + +namespace meta_hpp +{ + class uresult final { + public: + uresult() = default; + ~uresult() = default; + + uresult(uresult&&) noexcept = default; + uresult(const uresult&) = default; + + uresult& operator=(uresult&&) noexcept = default; + uresult& operator=(const uresult&) = default; + + explicit(false) uresult(uerror error) noexcept; + explicit(false) uresult(uvalue value) noexcept; + + uresult& operator=(uerror error) noexcept; + uresult& operator=(uvalue value) noexcept; + + template < typename T, typename Tp = std::decay_t > + requires(!std::is_same_v) // + && (!std::is_same_v) // + && (!std::is_same_v) // + && (!detail::is_in_place_type_v) // + && (std::is_copy_constructible_v) // + // NOLINTNEXTLINE(*-forwarding-reference-overload) + uresult(T&& val); + + template < typename T, typename Tp = std::decay_t > + requires(!std::is_same_v) // + && (!std::is_same_v) // + && (!std::is_same_v) // + && (std::is_copy_constructible_v) // + uresult& operator=(T&& val); + + template < typename T, typename... Args, typename Tp = std::decay_t > + requires std::is_copy_constructible_v // + && std::is_constructible_v // + explicit uresult(std::in_place_type_t, Args&&... args); + + template < typename T, typename U, typename... Args, typename Tp = std::decay_t > + requires std::is_copy_constructible_v // + && std::is_constructible_v&, Args...> // + explicit uresult(std::in_place_type_t, std::initializer_list ilist, Args&&... args); + + template < typename T, typename... Args, typename Tp = std::decay_t > + requires std::is_copy_constructible_v // + && std::is_constructible_v // + Tp& emplace(Args&&... args); + + template < typename T, typename U, typename... Args, typename Tp = std::decay_t > + requires std::is_copy_constructible_v // + && std::is_constructible_v&, Args...> // + Tp& emplace(std::initializer_list ilist, Args&&... args); + + [[nodiscard]] bool has_error() const noexcept; + [[nodiscard]] bool has_value() const noexcept; + [[nodiscard]] explicit operator bool() const noexcept; + + void reset() noexcept; + void swap(uresult& other) noexcept; + + [[nodiscard]] uvalue& get_value() &; + [[nodiscard]] uvalue&& get_value() &&; + [[nodiscard]] const uvalue& get_value() const&; + [[nodiscard]] const uvalue&& get_value() const&&; + + [[nodiscard]] error_code get_error() const noexcept; + + private: + uvalue value_{}; + error_code error_{error_code::no_error}; + }; + + inline void swap(uresult& l, uresult& r) noexcept { + l.swap(r); + } + + template < typename T, typename... Args > + uresult make_uresult(Args&&... args) { + return uresult(std::in_place_type, std::forward(args)...); + } + + template < typename T, typename U, typename... Args > + uresult make_uresult(std::initializer_list ilist, Args&&... args) { + return uresult(std::in_place_type, ilist, std::forward(args)...); + } +} diff --git a/headers/meta.hpp/meta_uresult/uresult.hpp b/headers/meta.hpp/meta_uresult/uresult.hpp new file mode 100644 index 0000000..5138d92 --- /dev/null +++ b/headers/meta.hpp/meta_uresult/uresult.hpp @@ -0,0 +1,156 @@ +/******************************************************************************* + * 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 "../meta_base.hpp" +#include "../meta_uresult.hpp" + +#include "../meta_uvalue/uvalue.hpp" + +namespace meta_hpp +{ + inline uerror::uerror(error_code error) noexcept + : error_{error} {} + + inline uerror& uerror::operator=(error_code error) noexcept { + error_ = error; + return *this; + } + + inline error_code uerror::get_error() const noexcept { + return error_; + } + + inline void uerror::reset() noexcept { + error_ = error_code::no_error; + } + + inline void uerror::swap(uerror& other) noexcept { + using std::swap; + swap(error_, other.error_); + } + + inline std::size_t uerror::get_hash() const noexcept { + return std::hash{}(error_); + } +} + +namespace meta_hpp +{ + inline uresult::uresult(uerror error) noexcept + : error_{error.get_error()} {} + + inline uresult::uresult(uvalue value) noexcept + : value_{std::move(value)} {} + + inline uresult& uresult::operator=(uerror error) noexcept { + value_ = uvalue{}; + error_ = error.get_error(); + return *this; + } + + inline uresult& uresult::operator=(uvalue value) noexcept { + value_ = std::move(value); + error_ = error_code::no_error; + return *this; + } + + template < typename T, typename Tp > + requires(!std::is_same_v) // + && (!std::is_same_v) // + && (!std::is_same_v) // + && (!detail::is_in_place_type_v) // + && (std::is_copy_constructible_v) // + // NOLINTNEXTLINE(*-forwarding-reference-overload) + uresult::uresult(T&& val) + : value_{std::forward(val)} {} + + template < typename T, typename Tp > + requires(!std::is_same_v) // + && (!std::is_same_v) // + && (!std::is_same_v) // + && (std::is_copy_constructible_v) // + uresult& uresult::operator=(T&& val) { + value_ = std::forward(val); + error_ = error_code::no_error; + return *this; + } + + template < typename T, typename... Args, typename Tp > + requires std::is_copy_constructible_v // + && std::is_constructible_v // + uresult::uresult(std::in_place_type_t, Args&&... args) + : value_{std::in_place_type, std::forward(args)...} {} + + template < typename T, typename U, typename... Args, typename Tp > + requires std::is_copy_constructible_v // + && std::is_constructible_v&, Args...> // + uresult::uresult(std::in_place_type_t, std::initializer_list ilist, Args&&... args) + : value_{std::in_place_type, ilist, std::forward(args)...} {} + + template < typename T, typename... Args, typename Tp > + requires std::is_copy_constructible_v // + && std::is_constructible_v // + Tp& uresult::emplace(Args&&... args) { + Tp& val{value_.emplace(std::forward(args)...)}; + error_ = error_code::no_error; + return val; + } + + template < typename T, typename U, typename... Args, typename Tp > + requires std::is_copy_constructible_v // + && std::is_constructible_v&, Args...> // + Tp& uresult::emplace(std::initializer_list ilist, Args&&... args) { + Tp& val{value_.emplace(ilist, std::forward(args)...)}; + error_ = error_code::no_error; + return val; + } + + inline bool uresult::has_error() const noexcept { + return error_ != error_code::no_error; + } + + inline bool uresult::has_value() const noexcept { + return error_ == error_code::no_error; + } + + inline uresult::operator bool() const noexcept { + return has_value(); + } + + inline void uresult::reset() noexcept { + value_ = uvalue{}; + error_ = error_code::no_error; + } + + inline void uresult::swap(uresult& other) noexcept { + using std::swap; + swap(value_, other.value_); + swap(error_, other.error_); + } + + inline uvalue& uresult::get_value() & { + return value_; + } + + inline uvalue&& uresult::get_value() && { + return std::move(value_); + } + + inline const uvalue& uresult::get_value() const& { + return value_; + } + + inline const uvalue&& uresult::get_value() const&& { + // NOLINTNEXTLINE(*-move-const-arg) + return std::move(value_); + } + + inline error_code uresult::get_error() const noexcept { + return error_; + } +} diff --git a/headers/meta.hpp/meta_uvalue.hpp b/headers/meta.hpp/meta_uvalue.hpp index 9848ed2..7d7b72d 100644 --- a/headers/meta.hpp/meta_uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue.hpp @@ -9,21 +9,6 @@ #include "meta_base.hpp" #include "meta_types.hpp" -namespace meta_hpp::detail -{ - template < typename T > - concept uvalue_kind // - = std::is_same_v; // - - template < typename T > - concept any_uvalue_kind // - = std::is_same_v // - || std::is_same_v // - || std::is_same_v // - || std::is_same_v // - || std::is_same_v; // -} - namespace meta_hpp { class uvalue final {