tuple_algorithms.hpp
contains various (free function) algorithms that work on stdx::tuple.
-
all_of,any_of,none_of- like the standard versions, but over a tuple -
apply- likestd::apply, but also a member function ontuple -
cartesian_product- create a tuple-of-tuples-of-references that is the cartesian product of the inputs -
cartesian_product_copy- create a tuple-of-tuples that is the cartesian product of the inputs -
chunk_by- split a tuple into a tuple-of-tuples according to a type function -
contains_type- a variable template that is true when a tuple contains a given type -
enumerate- likefor_each, but including a compile-time index -
filter- for compile-time filtering -
for_each- like the standard version, but over a tuple -
fold_leftandfold_right- member functions ontuple -
join- a member function ontuple, likefold_leftbut without an initial value -
sort- sort a tuple by a function on the contained types -
to_ordered_set- produce a tuple of unique types that are sorted -
to_unordered_set- produce a tuple of unique types that are in the order given -
transform- a variadic transform on tuple(s) -
tuple_cat- likestd::tuple_cat -
tuple_cons- add an element to the front of a tuple -
tuple_push_back- alias fortuple_snoc -
tuple_push_front- alias fortuple_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 and none_of work in the same way as the standard versions
on ranges, but over a tuple instead.
auto t = stdx::tuple{1, 2, 3};
auto x = stdx::any_of([](auto n) { return n % 2 == 0; }, t); // trueSee member functions. stdx::apply
is also available as a free function, for compatibility with std::apply.
auto t = stdx::tuple{1, 2, 3};
auto sum = stdx::apply([] (auto... args) { return (args + ... + 0); }, t); // 6stdx::apply can also be called with a variadic pack of tuples, which are
unpacked and passed to the function:
auto t1 = stdx::tuple{1, 2, 3};
auto t2 = stdx::tuple{4, 5, 6};
auto sum = stdx::apply([] (auto... args) { return (args + ... + 0); }, t1, t2); // 21cartesian_product takes any number of tuples and returns the tuple-of-tuples
that is the
cartesian
product of the members. Each returned tuple is a tuple of references.
auto t1 = stdx::tuple{1, 2};
auto t2 = stdx::tuple{'a', 'b'};
auto c = stdx::cartesian_product(t1, t2);
// produces {{1, 'a'}, {1, 'b'}, {2, 'a'}, {2, 'b'}}|
Note
|
The cartesian product of no tuples is a tuple containing an empty tuple. |
The same as cartesian_product, but the returned tuples have values, not
references.
|
Note
|
This can be useful for constexpr applications: in general one cannot
take the address of a local constexpr variable unless it is static.
|
chunk_by takes a tuple and returns a tuple-of-tuples, where each tuple is
grouped by type name.
auto t = stdx::tuple{1, 2, 3, true, false}; // tuple<int, int, int, bool, bool>
auto c1 = stdx::chunk_by(t); // tuple<tuple<int, int, int>, tuple<bool, bool>>
auto c2 = stdx::chunk(t); // without a template argument, the same as chunk_byNotice that chunk_by doesn’t sort the tuple first; it only chunks elements
that are adjacent.
auto t = stdx::tuple{1, true, 3}; // tuple<int, bool, int>
auto c = stdx::chunk_by(t); // tuple<tuple<int>, tuple<bool>, tuple<int>>chunk_by takes an optional template argument which is a type
function (a template of one argument). This will be applied to each type in the
tuple to obtain a type name that is then used to chunk. By default, this
type function is std::type_identity_t.
contains_type is a variable template that is true when a tuple contains a given type.
using T = stdx::tuple<int, bool, int &>;
static_assert(stdx::contains_type<T, int>);It also works on indexed tuples.
// see "Indexed tuples"
using T = stdx::indexed_tuple<stdx::detail::index_function_list<key_for>,
map_entry<X, int>, map_entry<Y, int>>;
static_assert(stdx::contains_type<T, X>);If contains_type<Tuple, Type> is true, then you can use get<Type> to
retrieve the appropriate member (assuming the type is contained exactly once).
enumerate runs a given function object on each element of a tuple in order.
Like for_each, it is variadic, taking an n-ary function and n tuples. The
operator() of the given function object takes the std::size_t index
(zero-based, of course) as an NTTP.
auto t = stdx::tuple{1, 2, 3};
stdx::enumerate([] <auto Idx> (auto x) { std::cout << Idx << ':' << x << '\n'; }, t);|
Note
|
Like for_each, enumerate returns the function object passed to it.
|
enumerate is also available for std::array, but to be explicit it is called unrolled_enumerate:
auto a = std::array{1, 2, 3};
stdx::unrolled_enumerate([] <auto Idx> (auto x) { std::cout << Idx << ':' << x << '\n'; }, a);filter allows compile-time filtering of a tuple based on the types contained.
auto t = stdx::tuple{
std::integral_constant<int, 1>{}, std::integral_constant<int, 2>{},
std::integral_constant<int, 3>{}, std::integral_constant<int, 4>{}};
template <typename T>
using is_even = std::bool_constant<T::value % 2 == 0>;
auto filtered = stdx::filter<is_even>(t);
// filtered is a stdx::tuple<std::integral_constant<int, 2>,
// std::integral_constant<int, 4>>|
Note
|
filtering a tuple can only be done on the types, not on the values! The type of the filtered result must obviously be known at compile time. However, the values within the tuple are also preserved. |
for_each runs a given function on each element of a tuple in order. Like
transform, it is variadic, taking an n-ary function and n tuples.
auto t = stdx::tuple{1, 2, 3};
stdx::for_each([] (auto x) { std::cout << x << '\n'; }, t);|
Note
|
Like
std::for_each,
stdx::for_each returns the function object passed to it. This can be useful
for stateful function objects.
|
for_each is also available for std::array, but to be explicit it is called unrolled_for_each:
auto a = std::array{1, 2, 3};
stdx::unrolled_for_each([] (auto x) { std::cout << x << '\n'; }, a);gather_by takes a tuple and returns a tuple-of-tuples, where each tuple is
grouped by type name.
auto t = stdx::tuple{1, true, 2, false, 3}; // tuple<int, int, int, bool, bool>
auto c1 = stdx::gather_by(t); // tuple<tuple<int, int, int>, tuple<bool, bool>>
auto c2 = stdx::gather(t); // without a template argument, the same as gather_bygather_by is like chunk_by, except that gather_by gathers elements that are not adjacent.
gather_by takes an optional template argument which is a type
function (a template of one argument). This will be applied to each type in the
tuple to obtain a type name that is then used to chunk. By default, this
type function is std::type_identity_t.
|
Warning
|
gather_by uses sort - not stable_sort! For a given type, the
order of values in the gathered tuple is not necessarily the same as that of the
input tuple.
|
sort is used to sort a tuple by type name.
auto t = stdx::tuple{42, true}; // tuple<int, bool>
auto s = stdx::sort(t); // tuple<bool, int> {true, 42}Like chunk_by, sort takes an optional template argument which is a type
function (a template of one argument). This will be applied to each type in the
tuple to obtain a type name that is then sorted alphabetically. By default, this
type function is std::type_identity_t.
|
Warning
|
sort is not stable_sort! For a given type, the order of values in
the sorted tuple is not necessarily the same as that of the input tuple.
|
to_sorted_set is sort followed by unique: it sorts the types in a tuple,
then collapses it so that there is only one element of each type.
auto t = stdx::tuple{1, true, 2, false};
auto u = stdx::to_sorted_set(t); // {<some bool>, <some integer>}|
Warning
|
sort is not stable_sort! The value in the example above is not
necessarily {true, 1} because there is no stable ordering between elements of
the same type.
|
to_unsorted_set produces a tuple of unique types in the same order as the
original tuple. In each case the value of that type is the first one in the
original tuple.
auto t = stdx::tuple{1, true, 2, false};
auto u = stdx::to_unsorted_set(t); // {1, true}transform is used to transform the values (and potentially the types) in one
tuple, producing another.
auto t = stdx::tuple{1, 2, 3};
auto u = stdx::transform([](auto x) { return x + 1; }, t); // {2, 3, 4}transform is not limited to working on a single tuple: given an n-ary function
and n tuples, it will do the correct thing and "zip" the tuples together:
auto t1 = stdx::tuple{1, 2, 3};
auto t2 = stdx::tuple{2, 3, 4};
auto u = stdx::transform(std::multiplies{}, t1, t2); // {2, 6, 12}|
Note
|
It’s OK to zip together different length tuples: transform will produce
a tuple that is the length of the shortest input.
|
transform can also apply indexing functions
while it transforms:
// see "Indexed tuples"
struct X;
auto t = stdx::transform<key_for>(
[](auto value) { return map_entry<X, int>{value}; },
stdx::tuple{42});
auto x = get<X>(t).value; // 42tuple_cat works just like
std::tuple_cat.
tuple_cons adds an item to the front of a tuple. tuple_push_front is an
alias for tuple_cons.
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 adds an item to the back of a tuple. tuple_push_back is an alias
for tuple_snoc.
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 works like
std::unique, but on types
rather than values. i.e. unique will collapse adjacent elements whose type is
the same. The first such element is preserved in the result.
auto t = stdx::tuple{1, 2, true};
auto u = stdx::unique(t); // {1, true}