Skip to content

Commit 601edc9

Browse files
authored
Merge pull request intel#138 from elbeno/tuple-cons
✨ Add `tuple_cons` and `tuple_snoc`
2 parents 84cee66 + c87b785 commit 601edc9

File tree

3 files changed

+108
-0
lines changed

3 files changed

+108
-0
lines changed

docs/tuple_algorithms.adoc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ contains various (free function) algorithms that work on `stdx::tuple`.
2222
* `to_unordered_set` - produce a tuple of unique types that are in the order given
2323
* `transform` - a variadic transform on tuple(s)
2424
* `tuple_cat` - like `std::tuple_cat`
25+
* `tuple_cons` - add an element to the front of a tuple
26+
* `tuple_push_back` - alias for `tuple_snoc`
27+
* `tuple_push_front` - alias for `tuple_cons`
28+
* `tuple_snoc` - add an element to the back of a tuple
2529
* `unique` - produce a tuple where adjacent types that are the same are merged into one element (the first such)
2630

2731
=== `all_of`, `any_of`, `none_of`
@@ -278,6 +282,32 @@ auto x = get<X>(t).value; // 42
278282
`tuple_cat` works just like
279283
https://en.cppreference.com/w/cpp/utility/tuple/tuple_cat[`std::tuple_cat`].
280284

285+
=== `tuple_cons`/`tuple_push_front`
286+
287+
`tuple_cons` adds an item to the front of a tuple. `tuple_push_front` is an
288+
alias for `tuple_cons`.
289+
290+
[source,cpp]
291+
----
292+
auto t = stdx::tuple_cons(1, stdx:tuple{2, 3}); // {1, 2, 3}
293+
----
294+
295+
NOTE: `tuple_cons` preserves the reference qualifiers in the given tuple, but
296+
decays the "single" argument, as `make_tuple` does.
297+
298+
=== `tuple_snoc`/`tuple_push_back`
299+
300+
`tuple_snoc` adds an item to the back of a tuple. `tuple_push_back` is an alias
301+
for `tuple_snoc`.
302+
303+
[source,cpp]
304+
----
305+
auto t = stdx::tuple_snoc(stdx:tuple{2, 3}, 1); // {2, 3, 1}
306+
----
307+
308+
NOTE: `tuple_snoc` preserves the reference qualifiers in the given tuple, but
309+
decays the "single" argument, as `make_tuple` does.
310+
281311
=== `unique`
282312

283313
`unique` works like

include/stdx/tuple_algorithms.hpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,38 @@ template <tuplelike... Ts> [[nodiscard]] constexpr auto tuple_cat(Ts &&...ts) {
4949
}
5050
}
5151

52+
template <typename T, tuplelike Tup>
53+
[[nodiscard]] constexpr auto tuple_cons(T &&t, Tup &&tup) {
54+
using tuple_t = std::remove_cvref_t<Tup>;
55+
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
56+
return stdx::tuple<std::remove_cvref_t<T>,
57+
stdx::tuple_element_t<Is, tuple_t>...>{
58+
std::forward<T>(t), std::forward<Tup>(tup)[index<Is>]...};
59+
}(std::make_index_sequence<stdx::tuple_size_v<tuple_t>>{});
60+
}
61+
62+
template <tuplelike Tup, typename T>
63+
[[nodiscard]] constexpr auto tuple_snoc(Tup &&tup, T &&t) {
64+
using tuple_t = std::remove_cvref_t<Tup>;
65+
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
66+
return stdx::tuple<stdx::tuple_element_t<Is, tuple_t>...,
67+
std::remove_cvref_t<T>>{
68+
std::forward<Tup>(tup)[index<Is>]..., std::forward<T>(t)};
69+
}(std::make_index_sequence<stdx::tuple_size_v<tuple_t>>{});
70+
}
71+
72+
template <typename T, tuplelike Tup>
73+
[[nodiscard]] constexpr auto tuple_push_front(T &&t,
74+
Tup &&tup) -> decltype(auto) {
75+
return tuple_cons(std::forward<T>(t), std::forward<Tup>(tup));
76+
}
77+
78+
template <tuplelike Tup, typename T>
79+
[[nodiscard]] constexpr auto tuple_push_back(Tup &&tup,
80+
T &&t) -> decltype(auto) {
81+
return tuple_snoc(std::forward<Tup>(tup), std::forward<T>(t));
82+
}
83+
5284
template <template <typename T> typename Pred, tuplelike T>
5385
[[nodiscard]] constexpr auto filter(T &&t) {
5486
using tuple_t = std::remove_cvref_t<T>;

test/tuple_algorithms.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,3 +722,49 @@ TEST_CASE("gather_by with projection", "[tuple_algorithms]") {
722722
0, [](auto x, auto y) { return x + y.value; }) == 23);
723723
CHECK(get<2>(gathered) == stdx::tuple{named_int<C>{3}});
724724
}
725+
726+
TEST_CASE("tuple_cons", "[tuple_algorithms]") {
727+
static_assert(stdx::tuple_cons(1, stdx::tuple{}) == stdx::tuple{1});
728+
auto t = stdx::tuple_cons(1, stdx::tuple{});
729+
static_assert(std::is_same_v<decltype(t), stdx::tuple<int>>);
730+
}
731+
732+
TEST_CASE("tuple_cons (move only)", "[tuple_algorithms]") {
733+
auto t = stdx::tuple_cons(move_only{5}, stdx::tuple{move_only{10}});
734+
static_assert(
735+
std::is_same_v<decltype(t), stdx::tuple<move_only, move_only>>);
736+
CHECK(t == stdx::tuple{move_only{5}, move_only{10}});
737+
}
738+
739+
TEST_CASE("tuple_cons (references)", "[tuple_algorithms]") {
740+
auto x = 1;
741+
auto t = stdx::tuple_cons(1, stdx::tuple<int &>{x});
742+
static_assert(std::is_same_v<decltype(t), stdx::tuple<int, int &>>);
743+
}
744+
745+
TEST_CASE("tuple_snoc", "[tuple_algorithms]") {
746+
static_assert(stdx::tuple_snoc(stdx::tuple{}, 1) == stdx::tuple{1});
747+
auto t = stdx::tuple_snoc(stdx::tuple{}, 1);
748+
static_assert(std::is_same_v<decltype(t), stdx::tuple<int>>);
749+
}
750+
751+
TEST_CASE("tuple_snoc (move only)", "[tuple_algorithms]") {
752+
auto t = stdx::tuple_snoc(stdx::tuple{move_only{10}}, move_only{5});
753+
static_assert(
754+
std::is_same_v<decltype(t), stdx::tuple<move_only, move_only>>);
755+
CHECK(t == stdx::tuple{move_only{10}, move_only{5}});
756+
}
757+
758+
TEST_CASE("tuple_snoc (references)", "[tuple_algorithms]") {
759+
auto x = 1;
760+
auto t = stdx::tuple_snoc(stdx::tuple<int &>{x}, 1);
761+
static_assert(std::is_same_v<decltype(t), stdx::tuple<int &, int>>);
762+
}
763+
764+
TEST_CASE("tuple_push_front", "[tuple_algorithms]") {
765+
static_assert(stdx::tuple_push_front(1, stdx::tuple{}) == stdx::tuple{1});
766+
}
767+
768+
TEST_CASE("tuple_push_back", "[tuple_algorithms]") {
769+
static_assert(stdx::tuple_push_back(stdx::tuple{}, 1) == stdx::tuple{1});
770+
}

0 commit comments

Comments
 (0)