Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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});
}