From 882a244efe0a6af6b5606365416828616499b67c Mon Sep 17 00:00:00 2001 From: William Throwe Date: Tue, 31 Oct 2023 23:06:04 -0400 Subject: [PATCH 1/2] Mark make_not_null constexpr --- src/Utilities/Gsl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utilities/Gsl.hpp b/src/Utilities/Gsl.hpp index e6d25ba365e9..e18370a1a785 100644 --- a/src/Utilities/Gsl.hpp +++ b/src/Utilities/Gsl.hpp @@ -863,6 +863,6 @@ std::ostream& operator<<(std::ostream& os, const span t) { /// \note This is not a standard GSL function, and so is not in the /// gsl namespace. template -gsl::not_null make_not_null(T* ptr) { +constexpr gsl::not_null make_not_null(T* ptr) { return gsl::not_null(ptr); } From 363ae1acebf246e9298a22886fd60f3cd3063004 Mon Sep 17 00:00:00 2001 From: William Throwe Date: Tue, 31 Oct 2023 23:06:26 -0400 Subject: [PATCH 2/2] Add split_tuple function --- src/Utilities/CMakeLists.txt | 1 + src/Utilities/SplitTuple.hpp | 73 +++++++++++++++++++ tests/Unit/Utilities/CMakeLists.txt | 1 + tests/Unit/Utilities/Test_SplitTuple.cpp | 91 ++++++++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 src/Utilities/SplitTuple.hpp create mode 100644 tests/Unit/Utilities/Test_SplitTuple.cpp diff --git a/src/Utilities/CMakeLists.txt b/src/Utilities/CMakeLists.txt index 98ebec89065b..a0a990891566 100644 --- a/src/Utilities/CMakeLists.txt +++ b/src/Utilities/CMakeLists.txt @@ -64,6 +64,7 @@ spectre_target_headers( Requires.hpp SetNumberOfGridPoints.hpp Spherepack.hpp + SplitTuple.hpp StaticCache.hpp StdArrayHelpers.hpp StdHelpers.hpp diff --git a/src/Utilities/SplitTuple.hpp b/src/Utilities/SplitTuple.hpp new file mode 100644 index 000000000000..14010c4bceb7 --- /dev/null +++ b/src/Utilities/SplitTuple.hpp @@ -0,0 +1,73 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#pragma once + +#include +#include +#include + +#include "Utilities/Gsl.hpp" +#include "Utilities/TMPL.hpp" + +namespace split_tuple_detail { +template +constexpr std::tuple< + std::tuple_element_t>...> +extract_subset( + std::index_sequence /*meta*/, + [[maybe_unused]] const gsl::not_null*> tuple) { + return {std::forward< + std::tuple_element_t>>( + std::get(*tuple))...}; +} + +template +constexpr auto impl(tmpl::list /*meta*/, + tmpl::list /*meta*/, + [[maybe_unused]] std::tuple tuple) { + static_assert((0 + ... + Sizes::value) == sizeof...(TupleTypes), + "Tuple size does not match output sizes."); + return std::make_tuple(extract_subset( + std::make_index_sequence{}, make_not_null(&tuple))...); +} +} // namespace split_tuple_detail + +/// \ingroup UtilitiesGroup +/// Split a `std::tuple` into multiple tuples +/// +/// \note There are two functions with this name, but doxygen can't +/// figure that out. They have signatures +/// ``` +/// template +/// constexpr auto split_tuple(std::tuple tuple); +/// template +/// constexpr auto split_tuple(std::tuple tuple); +/// ``` +/// +/// Given a list of sizes, either directly as template parameters or +/// as a typelist of integral constant types, split the passed tuple +/// into pieces containing the specified number of entries. The +/// passed sizes must sum to the size of the tuple. +/// +/// \returns a `std::tuple` of `std::tuple`s +/// +/// \see std::tuple_cat for the inverse operation. +/// +/// \snippet Utilities/Test_SplitTuple.cpp split_tuple +/// @{ +template +constexpr auto split_tuple(std::tuple tuple) { + using offsets = tmpl::pop_back< + tmpl::fold>, + tmpl::bind, + tmpl::_element>>>>; + return split_tuple_detail::impl(offsets{}, SizeList{}, std::move(tuple)); +} + +template +constexpr auto split_tuple(std::tuple tuple) { + return split_tuple...>>(std::move(tuple)); +} +/// @} diff --git a/tests/Unit/Utilities/CMakeLists.txt b/tests/Unit/Utilities/CMakeLists.txt index 0aaf10c06b87..7ecf5db40d5c 100644 --- a/tests/Unit/Utilities/CMakeLists.txt +++ b/tests/Unit/Utilities/CMakeLists.txt @@ -39,6 +39,7 @@ set(LIBRARY_SOURCES Test_Registration.cpp Test_Requires.cpp Test_SetNumberOfGridPoints.cpp + Test_SplitTuple.cpp Test_StaticCache.cpp Test_StdArrayHelpers.cpp Test_StdHelpers.cpp diff --git a/tests/Unit/Utilities/Test_SplitTuple.cpp b/tests/Unit/Utilities/Test_SplitTuple.cpp new file mode 100644 index 000000000000..5c2e6d2f49c8 --- /dev/null +++ b/tests/Unit/Utilities/Test_SplitTuple.cpp @@ -0,0 +1,91 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#include "Framework/TestingFramework.hpp" + +#include +#include +#include +#include + +#include "Utilities/SplitTuple.hpp" +#include "Utilities/TMPL.hpp" + +namespace { +// Test constexpr +// [split_tuple] +static_assert(split_tuple<2, 1>(std::tuple{1, 2, 3}) == + std::tuple{std::tuple{1, 2}, std::tuple{3}}); +static_assert(split_tuple>(std::tuple{ + 1, 2, 3}) == std::tuple{std::tuple{1, 2}, std::tuple{3}}); +// [split_tuple] + +// Needed for type-deduction of the nested tuples. +template +std::tuple construct_tuple(T... args) { + return {std::move(args)...}; +} + +template +void check(std::tuple tuple, const Expected& expected) { + auto split = split_tuple(tuple); + static_assert(std::is_same_v); + auto split_typelist = + split_tuple>(tuple); + static_assert(std::is_same_v); + CHECK(split == expected); + CHECK(split_typelist == expected); +} +} // namespace + +SPECTRE_TEST_CASE("Unit.Utilities.SplitTuple", "[Unit][Utilities]") { + // Normal case + check<2, 1>(construct_tuple(1, 2.0, 3.0), + construct_tuple, std::tuple>( + {1, 2.0}, {3.0})); + // Empty piece + check<2, 0, 1>( + construct_tuple(1, 2.0, 3.0), + construct_tuple, std::tuple<>, std::tuple>( + {1, 2.0}, {}, {3.0})); + // No output + check<>(construct_tuple<>(), construct_tuple<>()); + // Single output + check<2>(construct_tuple(1, 2.0), + construct_tuple>({1, 2.0})); + // Non-copyable + { + using Expected = + std::tuple, std::tuple>>; + auto split = + split_tuple<1, 1>(construct_tuple>( + 1, std::make_unique(2.0))); + static_assert(std::is_same_v); + auto split_typelist = split_tuple>( + construct_tuple>( + 1, std::make_unique(2.0))); + static_assert(std::is_same_v); + CHECK(std::get<0>(std::get<0>(split)) == 1); + CHECK(*std::get<0>(std::get<1>(split)) == 2.0); + CHECK(std::get<0>(std::get<0>(split_typelist)) == 1); + CHECK(*std::get<0>(std::get<1>(split_typelist)) == 2.0); + } + // References + { + // NOLINTBEGIN(bugprone-use-after-move) + const int a = 0; + double b = 0.0; + using Expected = std::tuple, std::tuple>; + auto split = split_tuple<1, 1>( + construct_tuple(a, std::move(b))); + static_assert(std::is_same_v); + auto split_typelist = split_tuple>( + construct_tuple(a, std::move(b))); + static_assert(std::is_same_v); + CHECK(&std::get<0>(std::get<0>(split)) == &a); + CHECK(&std::get<0>(std::get<1>(split)) == &b); + CHECK(&std::get<0>(std::get<0>(split_typelist)) == &a); + CHECK(&std::get<0>(std::get<1>(split_typelist)) == &b); + // NOLINTEND(bugprone-use-after-move) + } +}