diff --git a/src/neo/core/tuple.detail.hpp b/src/neo/core/tuple.detail.hpp new file mode 100644 index 0000000..d9042b3 --- /dev/null +++ b/src/neo/core/tuple.detail.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace neo::core::_tuple_detail { + +/// An indexed box to hold tuple elements +template +struct nth_box { + // The value for this element + NEO_NO_UNIQUE_ADDRESS object_t _obox; + + using lref = add_lvalue_reference_t>; + using cref = add_const_reference_t>; + + constexpr nth_box() = default; + constexpr explicit nth_box(std::in_place_t, auto&& t) + : _obox(NEO_FWD(t)) {} + + bool operator==(nth_box const&) const = default; + auto operator<=>(nth_box const&) const = default; + + constexpr lref get() noexcept { return static_cast(_obox); } + constexpr cref get() const noexcept { return static_cast(_obox); } +}; + +template >> +struct boxes; + +template +struct boxes, std::index_sequence> : nth_box... { + constexpr boxes() = default; + + template + constexpr explicit boxes(std::in_place_t tag, Args&&... args) // + : nth_box(tag, NEO_FWD(args))... {} + + bool operator==(boxes const&) const = default; + auto operator<=>(boxes const&) const = default; +}; + +template +constexpr bool equal(const auto& left, const auto& right, std::index_sequence) noexcept { + return (static_cast(left.template get() == right.template get()) and ...); +} + +template +constexpr auto compare_three_way(const T& lhs, const U& rhs, std::index_sequence) noexcept { + using Category = std::common_comparison_category_t< + std::compare_three_way_result_t()), + decltype(rhs.template get())>...>; + Category ret = std::strong_ordering::equal; + static_cast( + ((0 != (ret = synth_three_way(lhs.template get(), rhs.template get()))) or ... + or true)); + return ret; +} + +template + requires requires { std::tuple_size::value; } +using index_sequence_for_tpl = std::make_index_sequence>; + +template + requires(can_get_nth and ...) +constexpr void tuple_like_check(Tpl&& tpl, std::index_sequence); + +template +concept tuple_like = requires(Tpl&& tpl) { + typename index_sequence_for_tpl>; + tuple_like_check(static_cast(tpl), index_sequence_for_tpl>{}); +}; + +template +concept matching_tuple_like = tuple_like + and std::tuple_size_v> == std::tuple_size_v; + +template + requires( + (std::constructible_from>, get_nth_t> + and ...)) +void check_tuple_constructible(std::index_sequence); + +template +concept tuple_constructible = matching_tuple_like and requires(Argument&& from) { + check_tuple_constructible(index_sequence_for_tpl{}); +}; + +template + requires((std::convertible_to, object_t>> + and ...)) +void check_tuple_convertible(std::index_sequence); + +template +concept tuple_convertible = tuple_constructible + and requires { check_tuple_convertible(index_sequence_for_tpl{}); }; + +template + requires((nothrow_constructible_from>, + get_nth_t> + and ...)) +void check_tuple_construct_nothrow(std::index_sequence); + +template +concept tuple_construct_nothrow + = tuple_constructible and requires(Argument&& from) { + check_tuple_construct_nothrow(index_sequence_for_tpl{}); + }; + +} // namespace neo::core::_tuple_detail diff --git a/src/neo/core/tuple.hpp b/src/neo/core/tuple.hpp new file mode 100644 index 0000000..10c4bf5 --- /dev/null +++ b/src/neo/core/tuple.hpp @@ -0,0 +1,249 @@ +#pragma once + +#include "./tuple.detail.hpp" + +#include +#include +#include +#include + +#include + +namespace neo { + +// Tag used to select the piece-wise constructor for a tuple +struct tuple_construct_tag {}; +// Tag to select a tuple->tuple converting constructor +struct tuple_convert_tag {}; + +} // namespace neo + +namespace neo::core { + +/** + * @brief A core::tuple is like a full neo::tuple but provides a simpler interface + * upon which the more expressive interface is constructed. + */ +template +class tuple { +public: + using index_sequence = std::make_index_sequence; + + template + using nth_type = nonvoid_t>; + +private: + using boxes = _tuple_detail::boxes; + + NEO_NO_UNIQUE_ADDRESS boxes _boxes; + + template + using _nth_box = _tuple_detail::nth_box>; + +public: + tuple() = default; + + /** + * @brief Tuple per-element-converting constructor. + * + * @param tag A tag to select this constructor + * @param args... Arguments that will be converted to the tuple elements. + */ + template + constexpr explicit tuple(tuple_construct_tag, + Args&&... args) // + noexcept((nothrow_constructible_from, Args> and ...)) + : _boxes(std::in_place, NEO_FWD(args)...) {} + + /** + * @brief From-tuple converting constructor. Converts from another tuple-like + * object (using neo::get_nth) for each tuple element. + * + * @param tag The tag to select this constructor + * @param tpl The tuple-like object to construct from. Elements will be perfect-forwarded from + * this tuple + */ + template + constexpr explicit tuple(tuple_convert_tag, + Tpl&& tpl, + std::index_sequence) // + noexcept((nothrow_constructible_from, get_nth_t> and ...)) + : _boxes(std::in_place, get_nth(NEO_FWD(tpl))...) {} + + template + constexpr add_lvalue_reference_t> get() & noexcept { + auto& box = static_cast<_nth_box&>(_boxes); + return static_cast>>(box.get()); + } + + template + constexpr add_lvalue_reference_t> get() const& noexcept { + auto& box = static_cast&>(_boxes); + return static_cast>>(box.get()); + } + + template + constexpr add_rvalue_reference_t> get() && noexcept { + auto& box = static_cast<_nth_box&>(_boxes); + return static_cast>>(box.get()); + } + + template + constexpr add_rvalue_reference_t> get() const&& noexcept { + auto& box = static_cast&>(_boxes); + return static_cast>>(box.get()); + } + + bool operator==(tuple const&) const = default; + auto operator<=>(tuple const&) const = default; +}; + +// Specialization of an empty tuple +template <> +class tuple<> { +public: + using index_sequence = std::index_sequence<>; + + tuple() = default; + constexpr explicit tuple(tuple_construct_tag) noexcept {} + template + requires(not can_get_nth) + constexpr explicit tuple(tuple_convert_tag, Tpl&&, std::index_sequence<>) noexcept {} + + template + void get() = delete; + + bool operator==(tuple const&) const = default; + auto operator<=>(tuple const&) const = default; +}; + +// Specialization for a 1-element tuple +template +class tuple { + using Type = nonvoid_t; + NEO_NO_UNIQUE_ADDRESS object_t _value; + +public: + using index_sequence = std::index_sequence<0>; + + tuple() = default; + + template Arg> + constexpr explicit tuple(tuple_construct_tag, + Arg&& arg) // + noexcept(nothrow_constructible_from) + : _value(NEO_FWD(arg)) {} + + template + requires(can_get_nth) + constexpr explicit tuple(tuple_convert_tag, Tpl&& tpl, std::index_sequence<0>) noexcept( + nothrow_constructible_from>) + : _value(get_nth<0>(NEO_FWD(tpl))) {} + + template + requires(N == 0) + constexpr add_lvalue_reference_t get() & noexcept { + return static_cast>(_value); + } + + template + requires(N == 0) + constexpr add_const_reference_t get() const& noexcept { + return static_cast>(_value); + } + + template + requires(N == 0) + constexpr add_rvalue_reference_t get() && noexcept { + return static_cast>(_value); + } + + template + requires(N == 0) + constexpr add_rvalue_reference_t get() const&& noexcept { + return static_cast>(_value); + } + + bool operator==(tuple const&) const = default; + auto operator<=>(tuple const&) const = default; +}; + +// Specialization for a 2-element tuple +template +class tuple { + NEO_NO_UNIQUE_ADDRESS object_t _first; + NEO_NO_UNIQUE_ADDRESS object_t _second; + + using Tnv = nonvoid_t; + using Unv = nonvoid_t; + + template + using _type = conditional_t; + +public: + using index_sequence = std::index_sequence<0, 1>; + + tuple() = default; + + template Arg1, convertible_to Arg2> + constexpr explicit tuple(tuple_construct_tag, + Arg1&& arg1, + Arg2&& arg2) // + noexcept(nothrow_constructible_from and nothrow_constructible_from) + : _first(NEO_FWD(arg1)) + , _second(NEO_FWD(arg2)) {} + + template + requires(can_get_nth) + constexpr explicit tuple(tuple_convert_tag, Tpl&& tpl, std::index_sequence<0, 1>) noexcept( + nothrow_constructible_from> + and nothrow_constructible_from>) + : _first(get_nth<0>(NEO_FWD(tpl))) + , _second(get_nth<1>(NEO_FWD(tpl))) {} + + template + constexpr _type& get() & noexcept { + if constexpr (N) + return static_cast(_second); + else + return static_cast(_first); + } + + template + constexpr _type const& get() const& noexcept { + if constexpr (N) + return static_cast(_second); + else + return static_cast(_first); + } + + template + constexpr _type&& get() && noexcept { + if constexpr (N) + return static_cast(_second); + else + return static_cast(_first); + } + + template + constexpr _type const&& get() const&& noexcept { + if constexpr (N) + return static_cast(_second); + else + return static_cast(_first); + } + + bool operator==(tuple const&) const = default; + auto operator<=>(tuple const&) const = default; +}; + +} // namespace neo::core + +template +struct std::tuple_size> { + constexpr static inline std::size_t value = sizeof...(Ts); +}; + +template +struct std::tuple_element> + : neo::meta::tag, N>> {}; diff --git a/src/neo/tuple.hpp b/src/neo/tuple.hpp index 6c73308..7aaec9b 100644 --- a/src/neo/tuple.hpp +++ b/src/neo/tuple.hpp @@ -2,14 +2,14 @@ #include "./attrib.hpp" #include "./concepts.hpp" -#include "./declval.hpp" #include "./fwd.hpp" +#include "./get.hpp" #include "./meta.hpp" -#include "./pick_type.hpp" -#include "./tag.hpp" +#include "./type_traits.hpp" + +#include #include -#include #include namespace neo { @@ -22,55 +22,6 @@ class tuple; template tuple(Ts&&... ts) -> tuple...>; -struct tuple_construct_tag {}; -struct tuple_convert_tag {}; - -namespace _tup_detail { - -template -void get() = delete; - -template -concept has_member_get_n = requires(T&& v) { NEO_FWD(v).template get(); }; - -template -concept has_adl_get_n = requires(T&& t) { get(NEO_FWD(t)); }; - -} // namespace _tup_detail - -template -concept can_get_nth = requires { - requires meta::len_v> > Index; - requires _tup_detail::has_adl_get_n - or _tup_detail::has_member_get_n; - }; - -/** - * @brief Invocable object template that attempts to access the Nth element of the given argument - * - * @tparam N The index to access. - * - * The object must have a member function template get(), or an ADL-visitle - * get(t); - * - * This works for std::tuple, std::variant, neo::tuple, nad neo::nano_tuple - */ -template -struct get_nth_fn { - template T> - constexpr decltype(auto) operator()(T&& t) const noexcept { - if constexpr (_tup_detail::has_member_get_n) { - return NEO_FWD(t).template get(); - } else { - using _tup_detail::get; - return get(NEO_FWD(t)); - } - } -}; - -template -constexpr inline auto get_nth = get_nth_fn{}; - /** * @brief Obtain the return type of calling get_nth() with the given tuple type * @@ -79,153 +30,15 @@ constexpr inline auto get_nth = get_nth_fn{}; */ template requires can_get_nth -using tuple_get_t = decltype(get_nth(NEO_DECLVAL(Tuple))); - -namespace _tup_detail { - -/// An indexed box to hold tuple elements -template -struct nth_box { - // The value for this element - NEO_NO_UNIQUE_ADDRESS T item; - - constexpr nth_box() = default; - constexpr explicit nth_box(tuple_construct_tag, auto&& t) - : item(NEO_FWD(t)) {} - - bool operator==(nth_box const&) const = default; - auto operator<=>(nth_box const&) const = default; -}; - -template -struct boxes; - -template -struct boxes, Tpl> : nth_box... { - constexpr boxes() = default; - - template - constexpr explicit boxes(tuple_construct_tag tag, Args&&... args) // - : nth_box(tag, NEO_FWD(args))... {} - - bool operator==(boxes const&) const = default; - auto operator<=>(boxes const&) const = default; -}; - -template -constexpr bool equal(const auto& left, const auto& right, std::index_sequence) noexcept { - return (static_cast(left.template get() == right.template get()) and ...); -} - -constexpr auto compare_three_way(const auto&, const auto&, std::index_sequence<>) noexcept { - return std::strong_ordering::equal; -} - -template -constexpr std::strong_ordering -compare_three_way(const auto& lhs, const auto& rhs, std::index_sequence) noexcept { - const auto result - = std::compare_strong_order_fallback(lhs.template get(), rhs.template get()); - if (not std::is_eq(result)) { - return result; - } - auto next = _tup_detail::compare_three_way(lhs, rhs, std::index_sequence{}); - return next; -} - -template -constexpr std::strong_ordering -compare_three_way(const auto& lhs, const auto& rhs, std::index_sequence) noexcept { - const auto i = std::compare_strong_order_fallback(lhs.template get(), rhs.template get()); - if (i != 0) { - return i; - } - const auto j = std::compare_strong_order_fallback(lhs.template get(), rhs.template get()); - if (j != 0) { - return j; - } - const auto k = std::compare_strong_order_fallback(lhs.template get(), rhs.template get()); - if (k != 0) { - return k; - } - auto next = _tup_detail::compare_three_way(lhs, rhs, std::index_sequence{}); - return next; -} - -} // namespace _tup_detail +using tuple_get_t = get_nth_t; template -class nano_tuple { -public: - using index_sequence = std::make_index_sequence; - -private: - using boxes = _tup_detail::boxes; - - NEO_NO_UNIQUE_ADDRESS boxes _boxes; - - template - using _nth_box = _tup_detail::nth_box>; - - template - using _nth_type = meta::at; - -public: - nano_tuple() = default; - - template - constexpr nano_tuple(tuple_construct_tag, - Args&&... args) // - noexcept((nothrow_constructible_from and ...)) - : _boxes(tuple_construct_tag{}, static_cast(NEO_FWD(args))...) {} - - template - constexpr nano_tuple(tuple_convert_tag, - Tpl&& tpl, - std::index_sequence) // - noexcept((nothrow_constructible_from> and ...)) - : _boxes(tuple_construct_tag{}, static_cast(NEO_FWD(tpl).template get())...) {} - - template - constexpr _nth_type& get() & noexcept { - auto& box = static_cast<_nth_box&>(_boxes); - return static_cast<_nth_type&>(box.item); - } - - template - constexpr _nth_type const& get() const& noexcept { - auto& box = static_cast&>(_boxes); - return static_cast<_nth_type const&>(box.item); - } - - template - constexpr _nth_type&& get() && noexcept { - auto& box = static_cast<_nth_box&>(_boxes); - return static_cast<_nth_type&&>(box.item); - } - - template - constexpr _nth_type const&& get() const&& noexcept { - auto& box = static_cast&>(_boxes); - return static_cast<_nth_type const&&>(box.item); - } - - bool operator==(nano_tuple const&) const - requires(equality_comparable and ...) - = default; - auto operator<=>(nano_tuple const&) const - requires(totally_ordered and ...) - = default; -}; - -template -class tuple : public nano_tuple { - using _base = nano_tuple; +class tuple : public core::tuple { + using _base = core::tuple; public: using index_sequence = typename _base::index_sequence; - using _base::get; - using _base::nano_tuple; + using _base::tuple; // clang-format off tuple(tuple&) = default; @@ -236,92 +49,81 @@ class tuple : public nano_tuple { tuple& operator=(tuple&&) = default; /// XXX: MSVC has trouble with concepts in the conditional-explicit. Use an intrinsic instead: - explicit(not (neo_is_convertible_to(const Ts&, Ts) and ...)) - constexpr tuple(const Ts&... ts) - noexcept((nothrow_constructible_from and ...)) + /** + * @brief Per-element copying constructor. + * + * Since the types of the arguments here are non-deduced, this constructor will cause + * construction at call sites if the corresponding argument cannot be deduced (e.g. is a bare + * braced initializer). + */ + explicit(not (neo_is_convertible_to(nonvoid_t const&, nonvoid_t) and ...)) + constexpr tuple(nonvoid_t const&... ts) + noexcept((nothrow_constructible_from, nonvoid_t const&> and ...)) requires (sizeof...(Ts) != 0) - and (constructible_from and ... ) + and (constructible_from, nonvoid_t const&> and ... ) : _base(tuple_construct_tag{}, ts...) {} - template ... Args> + /** + * @brief Per-element converting constructor. Each argument given here is + * passed through to the corresponding element of the tuple. + * + * Each argument must be explicit-convertible to the corresponding underlying element type. This + * constructor is `explicit` if any sub-element requires an explicit conversion from + * the corresponding argument + */ + template >... Args> + // Don't let this be visible when Ts is empty. That confuses some compilers requires (sizeof...(Ts) > 0) - explicit(not (neo_is_convertible_to(Args&&, Ts) and ...)) + // We are an explicit constructor if any sub-element requires an explicit conversion + explicit(not (neo_is_convertible_to(Args&&, nonvoid_t) and ...)) constexpr tuple(Args&&... args) - noexcept((nothrow_constructible_from and ...)) - : _base(tuple_construct_tag{}, static_cast(NEO_FWD(args))...) + // We are no-throw if all element conversions are also no-throw + noexcept((noexcept(static_cast>(NEO_FWD(args))) and ...)) + // Delegate to the base constructor: + : _base(tuple_construct_tag{}, NEO_FWD(args)...) {} - template - requires (sizeof...(Us) == sizeof...(Ts)) - and (constructible_from and ...) - explicit(not (neo_is_convertible_to(const Us&, Ts) and ...)) - constexpr tuple(const tuple& other) - noexcept((nothrow_constructible_from and ...)) - : _base(tuple_convert_tag{}, other, index_sequence{}) - {} - - template - requires (sizeof...(Us) == sizeof...(Ts)) - and (constructible_from and ...) - explicit(not (neo_is_convertible_to(Us&, Ts) and ...)) - constexpr tuple(tuple& other) - noexcept((nothrow_constructible_from and ...)) - : _base(tuple_convert_tag{}, other, index_sequence{}) - {} - - template - requires (sizeof...(Us) == sizeof...(Ts)) - and (constructible_from and ...) - explicit(not (neo_is_convertible_to(Us&&, Ts) and ...)) - constexpr tuple(tuple&& other) - noexcept((nothrow_constructible_from and ...)) + /** + * @brief Tuple conversion constructor. Allows constructing a tuple from another + * tuple type that may have different elements, provided that there exists valid + * conversions between those elements. + * + * This constructor requires that the argument be a tuple-like type with the same + * number of elements, and that each element from the other tuple is explicit-convertible-to + * the corresponding element in this tuple. This constructor is explicit if any of + * the sub-element conversions requires an explicit conversion. + * + * The tuple is perfect-forwarded through during construction. + */ + template OtherTpl> + requires core::_tuple_detail::tuple_constructible + explicit(not core::_tuple_detail::tuple_convertible) + constexpr tuple(OtherTpl&& other) + noexcept(core::_tuple_detail::tuple_construct_nothrow) : _base(tuple_convert_tag{}, NEO_FWD(other), index_sequence{}) {} - template - requires (sizeof...(Us) == sizeof...(Ts)) - and (constructible_from and ...) - explicit(not (neo_is_convertible_to(const Us&&, Ts) and ...)) - constexpr tuple(const tuple&& other) - noexcept((nothrow_constructible_from and ...)) - : _base(tuple_convert_tag{}, NEO_FWD(other), index_sequence{}) - {} // clang-format on - bool operator==(tuple const&) const - requires(equality_comparable and ...) - = default; - auto operator<=>(tuple const&) const - requires(totally_ordered and ...) - = default; + bool operator==(tuple const&) const = default; + auto operator<=>(tuple const&) const = default; - template ... Us> + template + requires(equality_comparable_with, object_t> and ...) constexpr bool operator==(tuple const& rhs) const noexcept { - return _tup_detail::equal(*this, rhs, index_sequence{}); + return core::_tuple_detail::equal(*this, rhs, index_sequence{}); } - template ... Us> + template + requires(totally_ordered_with, object_t> and ...) constexpr typename std::common_comparison_category< - typename std::compare_three_way_result::type...>::type + typename std::compare_three_way_result, object_t>::type...>::type operator<=>(const tuple& other) const noexcept { - return _tup_detail::compare_three_way(*this, other, index_sequence{}); + return core::_tuple_detail::compare_three_way(*this, other, index_sequence{}); } }; -template <> -class tuple<> : nano_tuple<> { - using _base = nano_tuple<>; - -public: - using index_sequence = typename _base::index_sequence; - using _base::get; - using _base::nano_tuple; - - bool operator==(tuple const&) const = default; - auto operator<=>(tuple const&) const = default; -}; - template using index_sequence_t = typename remove_reference_t::index_sequence; @@ -446,30 +248,16 @@ struct std::tuple_size> { template struct std::tuple_element> - : neo::meta::tag, N>> {}; + : neo::meta::tag>> {}; -template -struct std::tuple_size> { - constexpr static inline std::size_t value = sizeof...(Ts); -}; - -template -struct std::tuple_element> - : neo::meta::tag, N>> {}; - -// clang-format off template -requires (sizeof...(Ts) == sizeof...(Us)) - and (neo::has_common_type and ...) + requires(sizeof...(Ts) == sizeof...(Us)) and (neo::has_common_type and ...) struct neo::basic_common_type, neo::tuple> { using type = neo::tuple...>; }; template -requires (sizeof...(Ts) == sizeof...(Us)) - and (neo::has_common_reference and ...) + requires(sizeof...(Ts) == sizeof...(Us)) and (neo::has_common_reference and ...) struct std::basic_common_reference, neo::tuple, Tq, Uq> { using type = neo::tuple, Uq>...>; }; - -// clang-format on diff --git a/src/neo/tuple.test.cpp b/src/neo/tuple.test.cpp index d52791d..9f07d73 100644 --- a/src/neo/tuple.test.cpp +++ b/src/neo/tuple.test.cpp @@ -1,4 +1,7 @@ #include "./tuple.hpp" +#include "neo/concepts.hpp" +#include "neo/get.hpp" +#include #include @@ -9,6 +12,85 @@ using neo::tuple; using neo::tuple_get_t; +neo::testing::cx_test_case EmptyTuple = [](auto check) consteval { + neo::tuple<> empty; + check(empty == empty); + check(empty <= empty); + auto dup = empty; // Copy-construct + empty = dup; // Copy-assign + auto dup2 [[maybe_unused]] = NEO_MOVE(empty); // Move-construct + dup = NEO_MOVE(empty); // Move-assign + static_assert(neo::constructible_from>); + static_assert(neo::constructible_from, tuple<>&>); + static_assert(neo::constructible_from, tuple<> const&>); + static_assert(neo::constructible_from, tuple<>&&>); + static_assert(neo::constructible_from, tuple<> const&&>); + static_assert(not neo::constructible_from, tuple const&&>); + // static_assert(neo::constructible_from, std::allocator_arg_t, std::allocator>); +}; + +neo::testing::cx_test_case BasicTuple = [](auto check) consteval { + tuple a{21}; + check(a.get<0>() == 21); + check(a == a); + check(a == tuple{21}); + auto b = a; + check(b == a); + b.get<0>() = 412; + check(b != a); + check(a < b); +}; + +neo::testing::cx_test_case TupleOrdering = [](auto check) consteval { + tuple a{21, 41}; + tuple b{2, 79}; + check(b < a); // 21 < 2 + b.get<0>() = 22; + check(b > a); // 22 > 21 + b.get<0>() = 21; + check(b > a); // 79 > 41 +}; + +neo::testing::cx_test_case TupleOfVoid = [](auto check) consteval { + tuple v1; + tuple v2; + tuple v3 [[maybe_unused]]; + auto dup = v2; + (void)dup; + dup = v2; + check(v1 == v1); + check(v2 == v2); + + tuple u1; + check(v1 == u1); + u1 = v1; // okay + + (void)tuple(0); // okay + static_assert(std::regular>); + static_assert(std::regular>); +}; + +neo::testing::cx_test_case TupleOfReferences = [](auto check) consteval { + int a = 0, b = 1, c = 2; + tuple t1{a}; + tuple t2{b}; + tuple t3{c}; + check(t1 == t1); + check(t2.get<0>() == 1); + check(t1 != t2); + a = 1; + // Both no refer to equal values, but the references are not equal: + check(t1.get<0>() == 1); + check(t2.get<0>() == 1); + check(t1 != t2); + + // Compare against a tuple that holds a value + tuple regular{1}; + // Both values are 1 + check(t1.get<0>() == 1); + check(regular.get<0>() == 1); +}; + static_assert(neo::explicit_convertible_to, tuple>); static_assert(neo::convertible_to, tuple>); @@ -34,34 +116,54 @@ TEST_CASE("Empty tuple") { regular_assertions<>(); } +TEST_CASE("Tuple with reference") { + int i = 42; + tuple t = {i}; + int i2 = 51; + tuple another = {i2}; + auto first = neo::get_nth<0>(another); + CHECK(first == i2); + CHECK(another.get<0>() == 51); + t = another; + CHECK(i == 42); +} + struct not_equality_comparable {}; TEST_CASE("Equality and compare") { - static_assert(not neo::equality_comparable>); - static_assert(neo::equality_comparable>); - static_assert(neo::equality_comparable>); - static_assert(neo::equality_comparable>); - static_assert( + STATIC_REQUIRE(not neo::equality_comparable>); + STATIC_REQUIRE(neo::equality_comparable>); + STATIC_REQUIRE(neo::equality_comparable>); + STATIC_REQUIRE(neo::equality_comparable>); + STATIC_REQUIRE( neo::equality_comparable_with, tuple>); + STATIC_REQUIRE( + neo::totally_ordered_with, tuple>); - static_assert(not neo::equality_comparable_with, - tuple>); + STATIC_REQUIRE(not neo::equality_comparable_with, + tuple>); neo::tuple t1{1, 2}; neo::tuple t2{2, 1}; CHECK(t1 != t2); CHECK(t1 < t2); CHECK(t2 > t1); + + { + neo::tuple t1{"hey", 2}; + neo::tuple t2{"aaa", 5}; + CHECK(t1 > t2); + } } TEST_CASE("Const-ness") { - static_assert(std::assignable_from&, 0>, int>); - static_assert(not std::assignable_from&, 0>, int>); + STATIC_REQUIRE(std::assignable_from&, 0>, int>); + STATIC_REQUIRE(not std::assignable_from&, 0>, int>); using moveonly = std::unique_ptr; - static_assert(std::assignable_from&&, 0>>); - static_assert(not std::assignable_from&, 0>>); - static_assert(not std::assignable_from const&, 0>>); - static_assert(not std::assignable_from const&&, 0>>); + STATIC_REQUIRE(std::assignable_from&&, 0>>); + STATIC_REQUIRE(not std::assignable_from&, 0>>); + STATIC_REQUIRE(not std::assignable_from const&, 0>>); + STATIC_REQUIRE(not std::assignable_from const&&, 0>>); } TEST_CASE("Simple usage") { diff --git a/src/neo/utility.hpp b/src/neo/utility.hpp index 9bbd16f..69ecef5 100644 --- a/src/neo/utility.hpp +++ b/src/neo/utility.hpp @@ -5,7 +5,6 @@ #include "./type_traits.hpp" #include -#include namespace neo { @@ -56,7 +55,7 @@ constexpr inline struct logical_and_t { */ template class any_of { - nano_tuple _items; + core::tuple _items; template constexpr bool _op_1(std::index_sequence, logical_or_t, Other&& o) const noexcept { @@ -96,7 +95,7 @@ explicit any_of(const Items&...) -> any_of; */ template class none_of { - nano_tuple _items; + core::tuple _items; template constexpr bool _op_1(std::index_sequence, Other&& o) const noexcept { @@ -135,9 +134,9 @@ explicit none_of(const Items&...) -> none_of; template constexpr bool between(Arg&& arg, Low&& low, High&& high) noexcept requires requires { - arg <= high; - arg >= low; - } + arg <= high; + arg >= low; + } { return (arg <= high) && (arg >= low); } @@ -152,9 +151,9 @@ constexpr bool between(Arg&& arg, Low&& low, High&& high) noexcept template constexpr bool properly_between(Arg&& arg, Low&& low, High&& high) noexcept requires requires { - arg < high; - arg > low; - } + arg < high; + arg > low; + } { return (arg < high) && (arg > low); }