diff --git a/headers/meta.hpp/meta_utilities.hpp b/headers/meta.hpp/meta_utilities.hpp index 3058542..33610c8 100644 --- a/headers/meta.hpp/meta_utilities.hpp +++ b/headers/meta.hpp/meta_utilities.hpp @@ -31,69 +31,128 @@ namespace meta_hpp::detail requires { static_cast(std::declval()); }; } +namespace meta_hpp::detail +{ + template < typename T > + concept value_kind = + std::same_as; + + template < typename T > + concept decay_value_kind = + value_kind>; + + template < typename T > + concept uvalue_kind = + std::same_as || + std::same_as || + std::same_as; + + template < typename T > + concept decay_uvalue_kind = + uvalue_kind>; + + template < typename T > + concept decay_non_uvalue_kind = + (!decay_uvalue_kind); + + template < typename T > + concept arg_lvalue_ref_kind = + (std::is_lvalue_reference_v); + + template < typename T > + concept arg_rvalue_ref_kind = + (!std::is_reference_v) || + (std::is_rvalue_reference_v); + + template < typename T > + concept inst_class_ref_kind = + (std::is_class_v) || + (std::is_reference_v && std::is_class_v>); + + template < typename T > + concept inst_class_lvalue_ref_kind = + (std::is_lvalue_reference_v && std::is_class_v>); + + template < typename T > + concept inst_class_rvalue_ref_kind = + (std::is_class_v) || + (std::is_rvalue_reference_v && std::is_class_v>); +} + +namespace meta_hpp::detail +{ + template < typename T > + concept has_less_op_kind = requires(const T& v) { + { v < v } -> convertible_to; + }; + + template < typename T > + concept has_equals_op_kind = requires(const T& v) { + { v == v } -> convertible_to; + }; + + template < typename T > + concept has_istream_op_kind = requires(std::istream& is, T& v) { + { is >> v } -> convertible_to; + }; + + template < typename T > + concept has_ostream_op_kind = requires(std::ostream& os, const T& v) { + { os << v } -> convertible_to; + }; +} + namespace meta_hpp { class value final { public: value() = delete; + // NOLINTNEXTLINE(performance-noexcept-move-constructor) value(value&& other); value(const value& other); + // NOLINTNEXTLINE(performance-noexcept-move-constructor) value& operator=(value&& other); value& operator=(const value& other); - template < typename T, typename Tp = std::decay_t - , std::enable_if_t, int> = 0 - , std::enable_if_t, int> = 0 - , std::enable_if_t, int> = 0 > + ~value() = default; + + template < detail::decay_non_uvalue_kind T > explicit value(T&& val); - template < typename T, typename Tp = std::decay_t - , std::enable_if_t, int> = 0 - , std::enable_if_t, int> = 0 - , std::enable_if_t, int> = 0 > + template < detail::decay_non_uvalue_kind T > value& operator=(T&& val); void swap(value& other) noexcept; - const any_type& get_type() const noexcept; + [[nodiscard]] const any_type& get_type() const noexcept; - void* data() noexcept; - const void* data() const noexcept; - const void* cdata() const noexcept; + [[nodiscard]] void* data() noexcept; + [[nodiscard]] const void* data() const noexcept; + [[nodiscard]] const void* cdata() const noexcept; template < typename T, typename Tp = std::decay_t > - Tp& cast() &; + [[nodiscard]] Tp& cast() &; template < typename T, typename Tp = std::decay_t > - Tp&& cast() &&; + [[nodiscard]] Tp&& cast() &&; template < typename T, typename Tp = std::decay_t > - const Tp& cast() const &; + [[nodiscard]] const Tp& cast() const &; template < typename T, typename Tp = std::decay_t > - const Tp&& cast() const &&; + [[nodiscard]] const Tp&& cast() const &&; template < typename T, typename Tp = std::decay_t > - Tp* try_cast() noexcept; + [[nodiscard]] Tp* try_cast() noexcept; template < typename T, typename Tp = std::decay_t > - const Tp* try_cast() const noexcept; + [[nodiscard]] const Tp* try_cast() const noexcept; - template < typename T > - friend bool operator<(const value& l, const T& r); - template < typename T > - friend bool operator<(const T& l, const value& r); friend bool operator<(const value& l, const value& r); - - template < typename T > - friend bool operator==(const value& l, const T& r); - template < typename T > - friend bool operator==(const T& l, const value& r); friend bool operator==(const value& l, const value& r); - - friend std::istream& operator>>(std::istream& os, value& v); + friend std::istream& operator>>(std::istream& is, value& v); friend std::ostream& operator<<(std::ostream& os, const value& v); private: struct traits; diff --git a/headers/meta.hpp/meta_utilities/value.hpp b/headers/meta.hpp/meta_utilities/value.hpp index 2f6cd54..7be7bad 100644 --- a/headers/meta.hpp/meta_utilities/value.hpp +++ b/headers/meta.hpp/meta_utilities/value.hpp @@ -9,50 +9,6 @@ #include "../meta_base.hpp" #include "../meta_utilities.hpp" -namespace meta_hpp::detail -{ - template < typename T, typename = void > - struct has_value_type_less_op : std::false_type {}; - - template < typename T > - struct has_value_type_less_op() < std::declval() - )>> : std::true_type {}; -} - -namespace meta_hpp::detail -{ - template < typename T, typename = void > - struct has_value_type_equals_op : std::false_type {}; - - template < typename T > - struct has_value_type_equals_op() == std::declval() - )>> : std::true_type {}; -} - -namespace meta_hpp::detail -{ - template < typename T, typename = void > - struct has_value_type_istream_op : std::false_type {}; - - template < typename T > - struct has_value_type_istream_op() >> std::declval() - )>> : std::true_type {}; -} - -namespace meta_hpp::detail -{ - template < typename T, typename = void > - struct has_value_type_ostream_op : std::false_type {}; - - template < typename T > - struct has_value_type_ostream_op() << std::declval() - )>> : std::true_type {}; -} - namespace meta_hpp { struct value::traits final { @@ -87,23 +43,23 @@ namespace meta_hpp return v.try_cast(); }, - .less = +[](const value& l, const value& r) -> bool { - if constexpr ( detail::has_value_type_less_op::value ) { + .less = +[]([[maybe_unused]] const value& l, [[maybe_unused]] const value& r) -> bool { + if constexpr ( detail::has_less_op_kind ) { return l.cast() < r.cast(); } else { throw std::logic_error("value type doesn't have less operator"); } }, - .equals = +[](const value& l, const value& r) -> bool { - if constexpr ( detail::has_value_type_equals_op::value ) { + .equals = +[]([[maybe_unused]] const value& l, [[maybe_unused]] const value& r) -> bool { + if constexpr ( detail::has_equals_op_kind ) { return l.cast() == r.cast(); } else { throw std::logic_error("value type doesn't have equality operator"); } }, - .move_ctor = +[](std::any& dst, value&& src) { + .move_ctor = +[]([[maybe_unused]] std::any& dst, [[maybe_unused]] value&& src) { if constexpr ( std::is_move_constructible_v ) { dst.emplace(std::move(src).cast()); } else { @@ -111,7 +67,7 @@ namespace meta_hpp } }, - .copy_ctor = +[](std::any& dst, const value& src) { + .copy_ctor = +[]([[maybe_unused]] std::any& dst, [[maybe_unused]] const value& src) { if constexpr ( std::is_copy_constructible_v ) { dst.emplace(src.cast()); } else { @@ -119,16 +75,16 @@ namespace meta_hpp } }, - .istream = +[](std::istream& os, value& v) -> std::istream& { - if constexpr ( detail::has_value_type_istream_op::value ) { - return os >> v.cast(); + .istream = +[]([[maybe_unused]] std::istream& is, [[maybe_unused]] value& v) -> std::istream& { + if constexpr ( detail::has_istream_op_kind ) { + return is >> v.cast(); } else { throw std::logic_error("value type doesn't have istream operator"); } }, - .ostream = +[](std::ostream& os, const value& v) -> std::ostream& { - if constexpr ( detail::has_value_type_ostream_op::value ) { + .ostream = +[]([[maybe_unused]] std::ostream& os, [[maybe_unused]] const value& v) -> std::ostream& { + if constexpr ( detail::has_ostream_op_kind ) { return os << v.cast(); } else { throw std::logic_error("value type doesn't have ostream operator"); @@ -141,16 +97,18 @@ namespace meta_hpp namespace meta_hpp { - inline value::value(value&& other) { - traits_ = other.traits_; + // NOLINTNEXTLINE(performance-noexcept-move-constructor) + inline value::value(value&& other) + : traits_{other.traits_} { traits_->move_ctor(raw_, std::move(other)); } - inline value::value(const value& other) { - traits_ = other.traits_; + inline value::value(const value& other) + : traits_{other.traits_} { traits_->copy_ctor(raw_, other); } + // NOLINTNEXTLINE(performance-noexcept-move-constructor) inline value& value::operator=(value&& other) { if ( this != &other ) { traits_ = other.traits_; @@ -167,21 +125,15 @@ namespace meta_hpp return *this; } - template < typename T, typename Tp - , std::enable_if_t, int> - , std::enable_if_t, int> - , std::enable_if_t, int> > + template < detail::decay_non_uvalue_kind T > value::value(T&& val) : raw_{std::forward(val)} - , traits_{traits::get()} {} + , traits_{traits::get>()} {} - template < typename T, typename Tp - , std::enable_if_t, int> - , std::enable_if_t, int> - , std::enable_if_t, int> > + template < detail::decay_non_uvalue_kind T > value& value::operator=(T&& val) { - raw_ = std::forward(val); - traits_ = resolve_type(); + value temp{std::forward(val)}; + swap(temp); return *this; } @@ -253,38 +205,42 @@ namespace meta_hpp namespace meta_hpp { template < typename T > - bool operator<(const value& l, const T& r) { + [[nodiscard]] bool operator<(const value& l, const T& r) { const any_type& r_type = resolve_type(); - return (l.get_type() < r_type) || (l.get_type() == r_type && std::less<>{}(l.cast(), r)); + return (l.get_type() < r_type) + || (l.get_type() == r_type && std::less<>{}(l.cast(), r)); } template < typename T > - bool operator<(const T& l, const value& r) { + [[nodiscard]] bool operator<(const T& l, const value& r) { const any_type& l_type = resolve_type(); - return (l_type < r.get_type()) || (l_type == r.get_type() && std::less<>{}(l, r.cast())); + return (l_type < r.get_type()) + || (l_type == r.get_type() && std::less<>{}(l, r.cast())); } - inline bool operator<(const value& l, const value& r) { - return (l.get_type() < r.get_type()) || (l.get_type() == r.get_type() && l.traits_->less(l, r)); + [[nodiscard]] inline bool operator<(const value& l, const value& r) { + return (l.get_type() < r.get_type()) + || (l.get_type() == r.get_type() && l.traits_->less(l, r)); } } namespace meta_hpp { template < typename T > - bool operator==(const value& l, const T& r) { - const any_type& r_type = resolve_type(); - return l.get_type() == r_type && std::equal_to<>{}(l.cast(), r); + [[nodiscard]] bool operator==(const value& l, const T& r) { + return l.get_type() == resolve_type() + && std::equal_to<>{}(l.cast(), r); } template < typename T > - bool operator==(const T& l, const value& r) { - const any_type& l_type = resolve_type(); - return l_type == r.get_type() && std::equal_to<>{}(l, r.cast()); + [[nodiscard]] bool operator==(const T& l, const value& r) { + return resolve_type() == r.get_type() + && std::equal_to<>{}(l, r.cast()); } - inline bool operator==(const value& l, const value& r) { - return l.get_type() == r.get_type() && l.traits_->equals(l, r); + [[nodiscard]] inline bool operator==(const value& l, const value& r) { + return l.get_type() == r.get_type() + && l.traits_->equals(l, r); } } diff --git a/untests/meta_utilities/value_tests.cpp b/untests/meta_utilities/value_tests.cpp index 740bcac..72f212c 100644 --- a/untests/meta_utilities/value_tests.cpp +++ b/untests/meta_utilities/value_tests.cpp @@ -304,7 +304,7 @@ TEST_CASE("meta/meta_utilities/value") { class empty_class2 {}; CHECK(operator<(meta::value{empty_class1{}}, meta::value{empty_class2{}})); - CHECK_THROWS(operator<(meta::value{empty_class1{}}, meta::value{empty_class1{}})); + CHECK_THROWS(std::ignore = operator<(meta::value{empty_class1{}}, meta::value{empty_class1{}})); } } @@ -323,7 +323,7 @@ TEST_CASE("meta/meta_utilities/value") { class empty_class2 {}; CHECK_FALSE(operator==(meta::value{empty_class1{}}, meta::value{empty_class2{}})); - CHECK_THROWS(operator==(meta::value{empty_class1{}}, meta::value{empty_class1{}})); + CHECK_THROWS(std::ignore = operator==(meta::value{empty_class1{}}, meta::value{empty_class1{}})); } } }