Skip to content

Commit

Permalink
Merge pull request #138 from elbeno/tuple-cons
Browse files Browse the repository at this point in the history
✨ Add `tuple_cons` and `tuple_snoc`
  • Loading branch information
lukevalenty authored Sep 11, 2024
2 parents 84cee66 + c87b785 commit 601edc9
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
30 changes: 30 additions & 0 deletions docs/tuple_algorithms.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ contains various (free function) algorithms that work on `stdx::tuple`.
* `to_unordered_set` - produce a tuple of unique types that are in the order given
* `transform` - a variadic transform on tuple(s)
* `tuple_cat` - like `std::tuple_cat`
* `tuple_cons` - add an element to the front of a tuple
* `tuple_push_back` - alias for `tuple_snoc`
* `tuple_push_front` - alias for `tuple_cons`
* `tuple_snoc` - add an element to the back of a tuple
* `unique` - produce a tuple where adjacent types that are the same are merged into one element (the first such)

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

=== `tuple_cons`/`tuple_push_front`

`tuple_cons` adds an item to the front of a tuple. `tuple_push_front` is an
alias for `tuple_cons`.

[source,cpp]
----
auto t = stdx::tuple_cons(1, stdx:tuple{2, 3}); // {1, 2, 3}
----

NOTE: `tuple_cons` preserves the reference qualifiers in the given tuple, but
decays the "single" argument, as `make_tuple` does.

=== `tuple_snoc`/`tuple_push_back`

`tuple_snoc` adds an item to the back of a tuple. `tuple_push_back` is an alias
for `tuple_snoc`.

[source,cpp]
----
auto t = stdx::tuple_snoc(stdx:tuple{2, 3}, 1); // {2, 3, 1}
----

NOTE: `tuple_snoc` preserves the reference qualifiers in the given tuple, but
decays the "single" argument, as `make_tuple` does.

=== `unique`

`unique` works like
Expand Down
32 changes: 32 additions & 0 deletions include/stdx/tuple_algorithms.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,38 @@ template <tuplelike... Ts> [[nodiscard]] constexpr auto tuple_cat(Ts &&...ts) {
}
}

template <typename T, tuplelike Tup>
[[nodiscard]] constexpr auto tuple_cons(T &&t, Tup &&tup) {
using tuple_t = std::remove_cvref_t<Tup>;
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return stdx::tuple<std::remove_cvref_t<T>,
stdx::tuple_element_t<Is, tuple_t>...>{
std::forward<T>(t), std::forward<Tup>(tup)[index<Is>]...};
}(std::make_index_sequence<stdx::tuple_size_v<tuple_t>>{});
}

template <tuplelike Tup, typename T>
[[nodiscard]] constexpr auto tuple_snoc(Tup &&tup, T &&t) {
using tuple_t = std::remove_cvref_t<Tup>;
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return stdx::tuple<stdx::tuple_element_t<Is, tuple_t>...,
std::remove_cvref_t<T>>{
std::forward<Tup>(tup)[index<Is>]..., std::forward<T>(t)};
}(std::make_index_sequence<stdx::tuple_size_v<tuple_t>>{});
}

template <typename T, tuplelike Tup>
[[nodiscard]] constexpr auto tuple_push_front(T &&t,
Tup &&tup) -> decltype(auto) {
return tuple_cons(std::forward<T>(t), std::forward<Tup>(tup));
}

template <tuplelike Tup, typename T>
[[nodiscard]] constexpr auto tuple_push_back(Tup &&tup,
T &&t) -> decltype(auto) {
return tuple_snoc(std::forward<Tup>(tup), std::forward<T>(t));
}

template <template <typename T> typename Pred, tuplelike T>
[[nodiscard]] constexpr auto filter(T &&t) {
using tuple_t = std::remove_cvref_t<T>;
Expand Down
46 changes: 46 additions & 0 deletions test/tuple_algorithms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -722,3 +722,49 @@ TEST_CASE("gather_by with projection", "[tuple_algorithms]") {
0, [](auto x, auto y) { return x + y.value; }) == 23);
CHECK(get<2>(gathered) == stdx::tuple{named_int<C>{3}});
}

TEST_CASE("tuple_cons", "[tuple_algorithms]") {
static_assert(stdx::tuple_cons(1, stdx::tuple{}) == stdx::tuple{1});
auto t = stdx::tuple_cons(1, stdx::tuple{});
static_assert(std::is_same_v<decltype(t), stdx::tuple<int>>);
}

TEST_CASE("tuple_cons (move only)", "[tuple_algorithms]") {
auto t = stdx::tuple_cons(move_only{5}, stdx::tuple{move_only{10}});
static_assert(
std::is_same_v<decltype(t), stdx::tuple<move_only, move_only>>);
CHECK(t == stdx::tuple{move_only{5}, move_only{10}});
}

TEST_CASE("tuple_cons (references)", "[tuple_algorithms]") {
auto x = 1;
auto t = stdx::tuple_cons(1, stdx::tuple<int &>{x});
static_assert(std::is_same_v<decltype(t), stdx::tuple<int, int &>>);
}

TEST_CASE("tuple_snoc", "[tuple_algorithms]") {
static_assert(stdx::tuple_snoc(stdx::tuple{}, 1) == stdx::tuple{1});
auto t = stdx::tuple_snoc(stdx::tuple{}, 1);
static_assert(std::is_same_v<decltype(t), stdx::tuple<int>>);
}

TEST_CASE("tuple_snoc (move only)", "[tuple_algorithms]") {
auto t = stdx::tuple_snoc(stdx::tuple{move_only{10}}, move_only{5});
static_assert(
std::is_same_v<decltype(t), stdx::tuple<move_only, move_only>>);
CHECK(t == stdx::tuple{move_only{10}, move_only{5}});
}

TEST_CASE("tuple_snoc (references)", "[tuple_algorithms]") {
auto x = 1;
auto t = stdx::tuple_snoc(stdx::tuple<int &>{x}, 1);
static_assert(std::is_same_v<decltype(t), stdx::tuple<int &, int>>);
}

TEST_CASE("tuple_push_front", "[tuple_algorithms]") {
static_assert(stdx::tuple_push_front(1, stdx::tuple{}) == stdx::tuple{1});
}

TEST_CASE("tuple_push_back", "[tuple_algorithms]") {
static_assert(stdx::tuple_push_back(stdx::tuple{}, 1) == stdx::tuple{1});
}

0 comments on commit 601edc9

Please sign in to comment.