Skip to content

Latest commit

 

History

History
333 lines (265 loc) · 11.4 KB

File metadata and controls

333 lines (265 loc) · 11.4 KB

tuple_algorithms.hpp

tuple_algorithms.hpp contains various (free function) algorithms that work on stdx::tuple.

Summary of tuple algorithms

  • all_of, any_of, none_of - like the standard versions, but over a tuple

  • apply - like std::apply, but also a member function on tuple

  • 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 - like for_each, but including a compile-time index

  • filter - for compile-time filtering

  • for_each - like the standard version, but over a tuple

  • fold_left and fold_right - member functions on tuple

  • join - a member function on tuple, like fold_left but 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 - 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

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); // true

apply

See 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); // 6

stdx::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); // 21

cartesian_product

cartesian_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.

cartesian_product_copy

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

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_by

Notice 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

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

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

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

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

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_by

gather_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

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

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

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

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; // 42

tuple_cat

tuple_cat works just like 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.

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.

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 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}