From 177532862a124146760c4ee3ebc6bb84a12f6ae9 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Mon, 2 Mar 2026 13:31:40 -0700 Subject: [PATCH 1/2] :art: Use `STATIC_CHECK` rather than `STATIC_REQUIRE` in tuple tests Problem: - `REQUIRE`, aborts a test when it fails. This mostly not what is intended when using `STATIC_REQUIRE`. Solution: - Use `STATIC_CHECK` instead of `STATIC_REQUIRE`. --- test/tuple.cpp | 264 ++++++++++++++++++++++++------------------------- 1 file changed, 131 insertions(+), 133 deletions(-) diff --git a/test/tuple.cpp b/test/tuple.cpp index f616364..4004af3 100644 --- a/test/tuple.cpp +++ b/test/tuple.cpp @@ -12,44 +12,44 @@ #include TEST_CASE("empty tuple", "[tuple]") { - STATIC_REQUIRE(std::is_empty_v>); + STATIC_CHECK(std::is_empty_v>); constexpr auto t = stdx::tuple{}; using T = std::remove_const_t; - STATIC_REQUIRE(std::is_same_v>); - STATIC_REQUIRE(stdx::tuple_size_v == 0); - STATIC_REQUIRE(T::size() == 0); + STATIC_CHECK(std::is_same_v>); + STATIC_CHECK(stdx::tuple_size_v == 0); + STATIC_CHECK(T::size() == 0); } TEST_CASE("single element tuple", "[tuple]") { constexpr auto t = stdx::tuple{1}; using T = std::remove_const_t; - STATIC_REQUIRE(std::is_same_v>); - STATIC_REQUIRE(stdx::tuple_size_v == 1); - STATIC_REQUIRE(T::size() == 1); - STATIC_REQUIRE(sizeof(T) == sizeof(int)); + STATIC_CHECK(std::is_same_v>); + STATIC_CHECK(stdx::tuple_size_v == 1); + STATIC_CHECK(T::size() == 1); + STATIC_CHECK(sizeof(T) == sizeof(int)); auto u = stdx::tuple{1}; using U = decltype(u); - STATIC_REQUIRE(std::is_same_v>); - STATIC_REQUIRE(stdx::tuple_size_v == 1); - STATIC_REQUIRE(U::size() == 1); - STATIC_REQUIRE(sizeof(U) == sizeof(int)); + STATIC_CHECK(std::is_same_v>); + STATIC_CHECK(stdx::tuple_size_v == 1); + STATIC_CHECK(U::size() == 1); + STATIC_CHECK(sizeof(U) == sizeof(int)); } TEST_CASE("multi element tuple", "[tuple]") { constexpr auto t = stdx::tuple{1, 2.0f}; using T = std::remove_const_t; - STATIC_REQUIRE(std::is_same_v>); - STATIC_REQUIRE(stdx::tuple_size_v == 2); - STATIC_REQUIRE(T::size() == 2); + STATIC_CHECK(std::is_same_v>); + STATIC_CHECK(stdx::tuple_size_v == 2); + STATIC_CHECK(T::size() == 2); } TEST_CASE("constexpr tuple of references", "[tuple]") { constexpr static int x = 1; constexpr auto t = stdx::tuple{x}; using T = std::remove_const_t; - STATIC_REQUIRE(stdx::tuple_size_v == 1); - STATIC_REQUIRE(T::size() == 1); + STATIC_CHECK(stdx::tuple_size_v == 1); + STATIC_CHECK(T::size() == 1); } TEST_CASE("free get", "[tuple]") { @@ -58,16 +58,16 @@ TEST_CASE("free get", "[tuple]") { CHECK(stdx::get<0>(t) == 5); CHECK(stdx::get<1>(t)); CHECK(stdx::get<2>(t) == 10); - STATIC_REQUIRE(stdx::get<0>(t) == 5); - STATIC_REQUIRE(stdx::get<1>(t)); - STATIC_REQUIRE(stdx::get<2>(t) == 10); + STATIC_CHECK(stdx::get<0>(t) == 5); + STATIC_CHECK(stdx::get<1>(t)); + STATIC_CHECK(stdx::get<2>(t) == 10); CHECK(stdx::get(t) == 5); CHECK(stdx::get(t)); CHECK(stdx::get(t) == 10); - STATIC_REQUIRE(stdx::get(t) == 5); - STATIC_REQUIRE(stdx::get(t)); - STATIC_REQUIRE(stdx::get(t) == 10); + STATIC_CHECK(stdx::get(t) == 5); + STATIC_CHECK(stdx::get(t)); + STATIC_CHECK(stdx::get(t) == 10); } TEST_CASE("free get (ADL)", "[tuple]") { @@ -76,31 +76,30 @@ TEST_CASE("free get (ADL)", "[tuple]") { CHECK(get<0>(t) == 5); CHECK(get<1>(t)); CHECK(get<2>(t) == 10); - STATIC_REQUIRE(get<0>(t) == 5); - STATIC_REQUIRE(get<1>(t)); - STATIC_REQUIRE(get<2>(t) == 10); + STATIC_CHECK(get<0>(t) == 5); + STATIC_CHECK(get<1>(t)); + STATIC_CHECK(get<2>(t) == 10); CHECK(get(t) == 5); CHECK(get(t)); CHECK(get(t) == 10); - STATIC_REQUIRE(get(t) == 5); - STATIC_REQUIRE(get(t)); - STATIC_REQUIRE(get(t) == 10); + STATIC_CHECK(get(t) == 5); + STATIC_CHECK(get(t)); + STATIC_CHECK(get(t) == 10); } TEST_CASE("free get value categories", "[tuple]") { { auto const t = stdx::tuple{42}; - STATIC_REQUIRE(std::is_same_v(t)), int const &>); - STATIC_REQUIRE(std::is_same_v(t)), int const &>); + STATIC_CHECK(std::is_same_v(t)), int const &>); + STATIC_CHECK(std::is_same_v(t)), int const &>); } { auto t = stdx::tuple{42}; - STATIC_REQUIRE(std::is_same_v(t)), int &>); - STATIC_REQUIRE(std::is_same_v(t)), int &>); - STATIC_REQUIRE(std::is_same_v(std::move(t))), int &&>); - STATIC_REQUIRE( - std::is_same_v(std::move(t))), int &&>); + STATIC_CHECK(std::is_same_v(t)), int &>); + STATIC_CHECK(std::is_same_v(t)), int &>); + STATIC_CHECK(std::is_same_v(std::move(t))), int &&>); + STATIC_CHECK(std::is_same_v(std::move(t))), int &&>); } } @@ -108,14 +107,14 @@ TEST_CASE("indexing", "[tuple]") { using namespace stdx::literals; constexpr auto t = stdx::tuple{5, true, 10l}; - STATIC_REQUIRE(t[0_idx] == 5); - STATIC_REQUIRE(t[1_idx] == true); - STATIC_REQUIRE(t[2_idx] == 10l); + STATIC_CHECK(t[0_idx] == 5); + STATIC_CHECK(t[1_idx] == true); + STATIC_CHECK(t[2_idx] == 10l); - STATIC_REQUIRE(std::is_same_v); + STATIC_CHECK(std::is_same_v); auto u = stdx::tuple{1}; - STATIC_REQUIRE(std::is_same_v); - STATIC_REQUIRE(std::is_same_v); + STATIC_CHECK(std::is_same_v); + STATIC_CHECK(std::is_same_v); } TEST_CASE("indexing with small literals", "[tuple]") { @@ -125,9 +124,9 @@ TEST_CASE("indexing with small literals", "[tuple]") { CHECK(get<"index"_0>(t) == 5); CHECK(get<"index"_1>(t)); CHECK(get<"index"_2>(t) == 10); - STATIC_REQUIRE(t["index"_0] == 5); - STATIC_REQUIRE(t["index"_1]); - STATIC_REQUIRE(t["index"_2] == 10l); + STATIC_CHECK(t["index"_0] == 5); + STATIC_CHECK(t["index"_1]); + STATIC_CHECK(t["index"_2] == 10l); } TEST_CASE("tuple of lvalue references", "[tuple]") { @@ -148,16 +147,16 @@ TEST_CASE("tuple of lambdas", "[tuple]") { TEST_CASE("tuple size/elements", "[tuple]") { using T = stdx::tuple; - STATIC_REQUIRE(stdx::tuple_size_v == 2); - STATIC_REQUIRE(std::is_same_v, int>); - STATIC_REQUIRE(std::is_same_v, bool>); + STATIC_CHECK(stdx::tuple_size_v == 2); + STATIC_CHECK(std::is_same_v, int>); + STATIC_CHECK(std::is_same_v, bool>); using A = stdx::tuple; - STATIC_REQUIRE(std::is_same_v, int &>); + STATIC_CHECK(std::is_same_v, int &>); using B = stdx::tuple; - STATIC_REQUIRE(std::is_same_v, int const &>); + STATIC_CHECK(std::is_same_v, int const &>); using C = stdx::tuple; - STATIC_REQUIRE(std::is_same_v, int &&>); + STATIC_CHECK(std::is_same_v, int &&>); } TEST_CASE("destructuring", "[tuple]") { @@ -175,65 +174,64 @@ struct B { } // namespace TEST_CASE("default constructability", "[tuple]") { - STATIC_REQUIRE(std::is_default_constructible_v>); - STATIC_REQUIRE(std::is_nothrow_default_constructible_v>); - STATIC_REQUIRE(not std::is_default_constructible_v>); - STATIC_REQUIRE(not std::is_nothrow_default_constructible_v>); + STATIC_CHECK(std::is_default_constructible_v>); + STATIC_CHECK(std::is_nothrow_default_constructible_v>); + STATIC_CHECK(not std::is_default_constructible_v>); + STATIC_CHECK(not std::is_nothrow_default_constructible_v>); } TEMPLATE_TEST_CASE("constructability", "[tuple]", (stdx::detail::element<0, int>), stdx::tuple<>, (stdx::tuple)) { - STATIC_REQUIRE(std::is_default_constructible_v); - STATIC_REQUIRE(std::is_nothrow_default_constructible_v); + STATIC_CHECK(std::is_default_constructible_v); + STATIC_CHECK(std::is_nothrow_default_constructible_v); } TEMPLATE_TEST_CASE("copyability", "[tuple]", (stdx::detail::element<0, int>), stdx::tuple<>, (stdx::tuple)) { - STATIC_REQUIRE(std::is_copy_constructible_v); - STATIC_REQUIRE(std::is_copy_assignable_v); - STATIC_REQUIRE(std::is_nothrow_copy_constructible_v); - STATIC_REQUIRE(std::is_nothrow_copy_assignable_v); - STATIC_REQUIRE(std::is_trivially_copy_constructible_v); - STATIC_REQUIRE(std::is_trivially_copy_assignable_v); + STATIC_CHECK(std::is_copy_constructible_v); + STATIC_CHECK(std::is_copy_assignable_v); + STATIC_CHECK(std::is_nothrow_copy_constructible_v); + STATIC_CHECK(std::is_nothrow_copy_assignable_v); + STATIC_CHECK(std::is_trivially_copy_constructible_v); + STATIC_CHECK(std::is_trivially_copy_assignable_v); } TEMPLATE_TEST_CASE("moveability", "[tuple]", (stdx::detail::element<0, int>), stdx::tuple<>, (stdx::tuple)) { - STATIC_REQUIRE(std::is_move_constructible_v); - STATIC_REQUIRE(std::is_move_assignable_v); - STATIC_REQUIRE(std::is_nothrow_move_constructible_v); - STATIC_REQUIRE(std::is_nothrow_move_assignable_v); - STATIC_REQUIRE(std::is_trivially_move_constructible_v); - STATIC_REQUIRE(std::is_trivially_move_assignable_v); + STATIC_CHECK(std::is_move_constructible_v); + STATIC_CHECK(std::is_move_assignable_v); + STATIC_CHECK(std::is_nothrow_move_constructible_v); + STATIC_CHECK(std::is_nothrow_move_assignable_v); + STATIC_CHECK(std::is_trivially_move_constructible_v); + STATIC_CHECK(std::is_trivially_move_assignable_v); } TEMPLATE_TEST_CASE("destructability", "[tuple]", (stdx::detail::element<0, int>), stdx::tuple<>, (stdx::tuple)) { - STATIC_REQUIRE(std::is_nothrow_destructible_v); - STATIC_REQUIRE(std::is_trivially_destructible_v); + STATIC_CHECK(std::is_nothrow_destructible_v); + STATIC_CHECK(std::is_trivially_destructible_v); } TEST_CASE("move-only types", "[tuple]") { - STATIC_REQUIRE(std::is_default_constructible_v>); - STATIC_REQUIRE( + STATIC_CHECK(std::is_default_constructible_v>); + STATIC_CHECK( std::is_nothrow_default_constructible_v>); - STATIC_REQUIRE(not std::is_copy_constructible_v>); - STATIC_REQUIRE(not std::is_copy_assignable_v>); + STATIC_CHECK(not std::is_copy_constructible_v>); + STATIC_CHECK(not std::is_copy_assignable_v>); - STATIC_REQUIRE(std::is_move_constructible_v>); - STATIC_REQUIRE(std::is_move_assignable_v>); - STATIC_REQUIRE( - std::is_nothrow_move_constructible_v>); - STATIC_REQUIRE(std::is_nothrow_move_assignable_v>); - STATIC_REQUIRE( + STATIC_CHECK(std::is_move_constructible_v>); + STATIC_CHECK(std::is_move_assignable_v>); + STATIC_CHECK(std::is_nothrow_move_constructible_v>); + STATIC_CHECK(std::is_nothrow_move_assignable_v>); + STATIC_CHECK( std::is_trivially_move_constructible_v>); - STATIC_REQUIRE(std::is_trivially_move_assignable_v>); + STATIC_CHECK(std::is_trivially_move_assignable_v>); - STATIC_REQUIRE(std::is_nothrow_destructible_v>); - STATIC_REQUIRE(std::is_trivially_destructible_v>); + STATIC_CHECK(std::is_nothrow_destructible_v>); + STATIC_CHECK(std::is_trivially_destructible_v>); } TEST_CASE("equality comparable", "[tuple]") { @@ -241,8 +239,8 @@ TEST_CASE("equality comparable", "[tuple]") { REQUIRE(t == t); REQUIRE(t != stdx::tuple{5, 11}); - STATIC_REQUIRE(t == stdx::tuple{5, 10}); - STATIC_REQUIRE(t != stdx::tuple{5, 11}); + STATIC_CHECK(t == stdx::tuple{5, 10}); + STATIC_CHECK(t != stdx::tuple{5, 11}); } TEST_CASE("equality comparable (tuple of references)", "[tuple]") { @@ -265,7 +263,7 @@ TEST_CASE("equality comparable (references and non-references)", "[tuple]") { TEST_CASE("equality comparable (conversions)", "[tuple]") { constexpr auto t = stdx::tuple{1}; - STATIC_REQUIRE(t == stdx::tuple{1.0}); + STATIC_CHECK(t == stdx::tuple{1.0}); } namespace { @@ -282,13 +280,13 @@ struct eq_derived : eq {}; TEST_CASE("equality comparable (user-defined)", "[tuple]") { constexpr auto t = stdx::tuple{eq{1}}; - STATIC_REQUIRE(t == stdx::tuple{eq{1}}); - STATIC_REQUIRE(t != stdx::tuple{eq{2}}); + STATIC_CHECK(t == stdx::tuple{eq{1}}); + STATIC_CHECK(t != stdx::tuple{eq{2}}); } TEST_CASE("equality comparable (conversions, user-defined)", "[tuple]") { constexpr auto t = stdx::tuple{eq{1}}; - STATIC_REQUIRE(t == stdx::tuple{eq_derived{1}}); + STATIC_CHECK(t == stdx::tuple{eq_derived{1}}); } TEST_CASE("order comparable", "[tuple]") { @@ -298,41 +296,41 @@ TEST_CASE("order comparable", "[tuple]") { REQUIRE(t < stdx::tuple{5, 11}); REQUIRE(not(t < t)); REQUIRE(not(t < stdx::tuple{4, 11})); - STATIC_REQUIRE(t < stdx::tuple{6, 9}); - STATIC_REQUIRE(t < stdx::tuple{5, 11}); - STATIC_REQUIRE(not(t < t)); // NOLINT(misc-redundant-expression) - STATIC_REQUIRE(not(t < stdx::tuple{4, 11})); + STATIC_CHECK(t < stdx::tuple{6, 9}); + STATIC_CHECK(t < stdx::tuple{5, 11}); + STATIC_CHECK(not(t < t)); // NOLINT(misc-redundant-expression) + STATIC_CHECK(not(t < stdx::tuple{4, 11})); REQUIRE(t <= t); REQUIRE(t <= stdx::tuple{6, 9}); REQUIRE(t <= stdx::tuple{5, 11}); REQUIRE(not(t <= stdx::tuple{5, 9})); REQUIRE(not(t <= stdx::tuple{4, 11})); - STATIC_REQUIRE(t <= t); // NOLINT(misc-redundant-expression) - STATIC_REQUIRE(t <= stdx::tuple{6, 9}); - STATIC_REQUIRE(t <= stdx::tuple{5, 11}); - STATIC_REQUIRE(not(t <= stdx::tuple{5, 9})); - STATIC_REQUIRE(not(t <= stdx::tuple{4, 11})); + STATIC_CHECK(t <= t); // NOLINT(misc-redundant-expression) + STATIC_CHECK(t <= stdx::tuple{6, 9}); + STATIC_CHECK(t <= stdx::tuple{5, 11}); + STATIC_CHECK(not(t <= stdx::tuple{5, 9})); + STATIC_CHECK(not(t <= stdx::tuple{4, 11})); REQUIRE(t > stdx::tuple{5, 9}); REQUIRE(t > stdx::tuple{4, 11}); REQUIRE(not(t > t)); REQUIRE(not(t > stdx::tuple{6, 9})); - STATIC_REQUIRE(t > stdx::tuple{5, 9}); - STATIC_REQUIRE(t > stdx::tuple{4, 11}); - STATIC_REQUIRE(not(t > t)); // NOLINT(misc-redundant-expression) - STATIC_REQUIRE(not(t > stdx::tuple{6, 9})); + STATIC_CHECK(t > stdx::tuple{5, 9}); + STATIC_CHECK(t > stdx::tuple{4, 11}); + STATIC_CHECK(not(t > t)); // NOLINT(misc-redundant-expression) + STATIC_CHECK(not(t > stdx::tuple{6, 9})); REQUIRE(t >= t); REQUIRE(t >= stdx::tuple{5, 9}); REQUIRE(t >= stdx::tuple{4, 11}); REQUIRE(not(t >= stdx::tuple{5, 11})); REQUIRE(not(t >= stdx::tuple{6, 9})); - STATIC_REQUIRE(t >= t); // NOLINT(misc-redundant-expression) - STATIC_REQUIRE(t >= stdx::tuple{5, 9}); - STATIC_REQUIRE(t >= stdx::tuple{4, 11}); - STATIC_REQUIRE(not(t >= stdx::tuple{5, 11})); - STATIC_REQUIRE(not(t >= stdx::tuple{6, 9})); + STATIC_CHECK(t >= t); // NOLINT(misc-redundant-expression) + STATIC_CHECK(t >= stdx::tuple{5, 9}); + STATIC_CHECK(t >= stdx::tuple{4, 11}); + STATIC_CHECK(not(t >= stdx::tuple{5, 11})); + STATIC_CHECK(not(t >= stdx::tuple{6, 9})); } TEST_CASE("order comparable (references and non-references)", "[tuple]") { @@ -354,13 +352,13 @@ TEST_CASE("spaceship comparable", "[tuple]") { REQUIRE(t <=> stdx::tuple{5, 9} == std::strong_ordering::greater); REQUIRE(t <=> stdx::tuple{4, 10} == std::strong_ordering::greater); REQUIRE(t <=> stdx::tuple{4, 11} == std::strong_ordering::greater); - STATIC_REQUIRE(t <=> t == std::strong_ordering::equal); - STATIC_REQUIRE(t <=> stdx::tuple{6, 9} == std::strong_ordering::less); - STATIC_REQUIRE(t <=> stdx::tuple{6, 10} == std::strong_ordering::less); - STATIC_REQUIRE(t <=> stdx::tuple{5, 11} == std::strong_ordering::less); - STATIC_REQUIRE(t <=> stdx::tuple{5, 9} == std::strong_ordering::greater); - STATIC_REQUIRE(t <=> stdx::tuple{4, 10} == std::strong_ordering::greater); - STATIC_REQUIRE(t <=> stdx::tuple{4, 11} == std::strong_ordering::greater); + STATIC_CHECK(t <=> t == std::strong_ordering::equal); + STATIC_CHECK(t <=> stdx::tuple{6, 9} == std::strong_ordering::less); + STATIC_CHECK(t <=> stdx::tuple{6, 10} == std::strong_ordering::less); + STATIC_CHECK(t <=> stdx::tuple{5, 11} == std::strong_ordering::less); + STATIC_CHECK(t <=> stdx::tuple{5, 9} == std::strong_ordering::greater); + STATIC_CHECK(t <=> stdx::tuple{4, 10} == std::strong_ordering::greater); + STATIC_CHECK(t <=> stdx::tuple{4, 11} == std::strong_ordering::greater); } TEST_CASE("spaceship comparable (references and non-references)", "[tuple]") { @@ -375,7 +373,7 @@ TEST_CASE("free get is SFINAE-friendly", "[tuple]") { constexpr auto t = [](stdx::tuple const &tup) { return stdx::tuple{get(tup)...}; }(stdx::tuple{}); - STATIC_REQUIRE(t == stdx::tuple{}); + STATIC_CHECK(t == stdx::tuple{}); } TEST_CASE("copy/move behavior for tuple", "[tuple]") { @@ -396,17 +394,17 @@ auto func_no_args() -> void; auto func_one_arg(int) -> void; TEST_CASE("make_tuple", "[tuple]") { - STATIC_REQUIRE(stdx::make_tuple() == stdx::tuple{}); - STATIC_REQUIRE(stdx::make_tuple(1, 2, 3) == stdx::tuple{1, 2, 3}); - STATIC_REQUIRE( + STATIC_CHECK(stdx::make_tuple() == stdx::tuple{}); + STATIC_CHECK(stdx::make_tuple(1, 2, 3) == stdx::tuple{1, 2, 3}); + STATIC_CHECK( std::is_same_v>); constexpr auto t = stdx::make_tuple(stdx::tuple{}); using T = std::remove_const_t; - STATIC_REQUIRE(std::is_same_v>>); - STATIC_REQUIRE(stdx::tuple_size_v == 1); - STATIC_REQUIRE(T::size() == 1); + STATIC_CHECK(std::is_same_v>>); + STATIC_CHECK(stdx::tuple_size_v == 1); + STATIC_CHECK(T::size() == 1); } namespace detail { @@ -427,8 +425,8 @@ struct concat, L> { TEST_CASE("tuple type-based concat", "[tuple]") { using T = stdx::tuple; using U = stdx::tuple; - STATIC_REQUIRE(std::is_same_v::type, - stdx::tuple>); + STATIC_CHECK(std::is_same_v::type, + stdx::tuple>); } TEST_CASE("forward_as_tuple", "[tuple]") { @@ -436,25 +434,25 @@ TEST_CASE("forward_as_tuple", "[tuple]") { auto y = 17; auto z = 17; auto t = stdx::forward_as_tuple(x, y, std::move(z)); - STATIC_REQUIRE( + STATIC_CHECK( std::is_same_v>); CHECK(t == stdx::tuple{17, 17, 17}); } TEST_CASE("one_of", "[tuple]") { - STATIC_REQUIRE(1 == stdx::one_of{1, 2, 3}); - STATIC_REQUIRE(4 != stdx::one_of{1, 2, 3}); - STATIC_REQUIRE(stdx::one_of{1, 2, 3} == 1); - STATIC_REQUIRE(stdx::one_of{1, 2, 3} != 4); + STATIC_CHECK(1 == stdx::one_of{1, 2, 3}); + STATIC_CHECK(4 != stdx::one_of{1, 2, 3}); + STATIC_CHECK(stdx::one_of{1, 2, 3} == 1); + STATIC_CHECK(stdx::one_of{1, 2, 3} != 4); - STATIC_REQUIRE(stdx::one_of{1, 2, 3} == stdx::one_of{3, 4, 5}); - STATIC_REQUIRE(stdx::one_of{1, 2, 3} != stdx::one_of{4, 5, 6}); + STATIC_CHECK(stdx::one_of{1, 2, 3} == stdx::one_of{3, 4, 5}); + STATIC_CHECK(stdx::one_of{1, 2, 3} != stdx::one_of{4, 5, 6}); } TEST_CASE("indexing unambiguously", "[tuple]") { using namespace stdx::literals; constexpr auto t = stdx::tuple{42, stdx::tuple{17}}; - STATIC_REQUIRE(t[0_idx] == 42); - STATIC_REQUIRE(t[1_idx][0_idx] == 17); + STATIC_CHECK(t[0_idx] == 42); + STATIC_CHECK(t[1_idx][0_idx] == 17); } From 625a2041c8f78f5f2fbda906b6e74d6070a49adb Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Mon, 2 Mar 2026 13:33:22 -0700 Subject: [PATCH 2/2] :zap: Fix the size of tuples of empty objects Problem: - A tuple containing distinct empty objects requires at least 1 byte for each, when it could be 1 byte total. Solution: - Apply `[[no_unique_address]]` to tuple elements so that a tuple of empty objects is minimal. --- include/stdx/tuple.hpp | 23 ++++++++++++++++++++--- test/tuple.cpp | 12 ++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/include/stdx/tuple.hpp b/include/stdx/tuple.hpp index b037e15..dd7b792 100644 --- a/include/stdx/tuple.hpp +++ b/include/stdx/tuple.hpp @@ -102,10 +102,10 @@ template struct element { return std::forward(value); } - T value; + [[no_unique_address]] T value; private: - [[nodiscard]] friend auto operator==(element const &x, element const &y) + [[nodiscard]] friend auto operator==(element const &, element const &) -> bool = default; [[nodiscard]] friend auto operator<=>(element const &, element const &) = default; @@ -355,7 +355,24 @@ struct tuple_impl, index_function_list, Ts...> operator==(tuple_impl const &lhs, tuple_impl, Funcs, Us...> const &rhs) -> bool { - return (... and (lhs[index] == rhs[index])); +#ifndef __clang__ + STDX_PRAGMA(diagnostic push) + STDX_PRAGMA(diagnostic ignored "-Wduplicated-branches") + if constexpr ((... and (std::is_copy_constructible_v and + std::is_copy_constructible_v))) { + if (std::is_constant_evaluated()) { + return (... and (static_cast(lhs[index]) == + static_cast(rhs[index]))); + } else { +#endif + return (... and (lhs[index] == rhs[index])); +#ifndef __clang__ + } + STDX_PRAGMA(diagnostic pop) + } else { + return (... and (lhs[index] == rhs[index])); + } +#endif } template diff --git a/test/tuple.cpp b/test/tuple.cpp index 4004af3..944a6ae 100644 --- a/test/tuple.cpp +++ b/test/tuple.cpp @@ -44,6 +44,18 @@ TEST_CASE("multi element tuple", "[tuple]") { STATIC_CHECK(T::size() == 2); } +namespace { +template struct empty {}; +} // namespace + +TEST_CASE("multi element tuple has minimal size", "[tuple]") { + constexpr auto t = stdx::tuple{empty<0>{}, empty<1>{}}; + STATIC_CHECK(sizeof(decltype(t)) == 1); + + constexpr auto u = stdx::tuple{42, empty<0>{}, empty<1>{}}; + STATIC_CHECK(sizeof(decltype(u)) == sizeof(int)); +} + TEST_CASE("constexpr tuple of references", "[tuple]") { constexpr static int x = 1; constexpr auto t = stdx::tuple{x};