How to Iterate Over the Elements of an Std::Tuple

How can you iterate over the elements of an std::tuple?

Boost.Fusion is a possibility:

Untested example:

struct DoSomething
{
template<typename T>
void operator()(T& t) const
{
t.do_sth();
}
};

tuple<....> t = ...;
boost::fusion::for_each(t, DoSomething());

Iterate over types of tuple in C++

You can use std::tuple_size and std::tuple_element to get the type of every tuple element (this will put it in reverse order, but with a little modification you can revert the order):

#include <iostream>
#include <variant>
#include <vector>

using namespace std;

using Controllers = tuple<int, char, float>;
using ControllersContainer = vector<variant<int, char, float>>;

template <size_t N>
void add(ControllersContainer& ctrls)
{
ctrls.emplace_back(tuple_element_t<N-1, Controllers>{});
add<N - 1>(ctrls);
}

template <>
void add<0>(ControllersContainer& ctrls)
{
ctrls.emplace_back(tuple_element_t<0, Controllers>{});
}

int main()
{
ControllersContainer ctrls;
add<tuple_size_v<Controllers>>(ctrls);
}

Iterating over tuple in C++17/20 [duplicate]

Is there some way perhaps with std::apply or std::invoke ?

std::apply fit the need indeed with fold expression:

std::tuple tplb{ Test{1} , Test{2} ,  Test{3} };

std::apply([](const auto&... tests){(tests.Go(), ...);}, tplb);

Here, we call method Go() for every type value of the tuple.

The idea would be to be able to have other object types in the tuple that support the same method. If the method is present in each object then the code would compile and execute without having to use a base-class, virtuals, vtable, etc.

So above method works.
It you would go further and dispatch to different implementation according to type, you might use overloaded class from std::visit's example:

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

auto f = overloaded {
[](const Test& test) { test.Go(); },
[](double d) { std::cout << d << ' '; },
[](const std::string& s) { std::cout << s << ' '; },
};
std::apply([&](const auto&... e){ (f(e), ...);}, my_tuple);

How to iterate over a std::tuple in C++ 11 [duplicate]

Here is an attempt to break down iterating over a tuple into component parts.

First, a function that represents doing a sequence of operations in order. Note that many compilers find this hard to understand, despite it being legal C++11 as far as I can tell:

template<class... Fs>
void do_in_order( Fs&&... fs ) {
int unused[] = { 0, ( (void)std::forward<Fs>(fs)(), 0 )... }
(void)unused; // blocks warnings
}

Next, a function that takes a std::tuple, and extracts the indexes required to access each element. By doing so, we can perfect forward later on.

As a side benefit, my code supports std::pair and std::array iteration:

template<class T>
constexpr std::make_index_sequence<std::tuple_size<T>::value>
get_indexes( T const& )
{ return {}; }

The meat and potatoes:

template<size_t... Is, class Tuple, class F>
void for_each( std::index_sequence<Is...>, Tuple&& tup, F&& f ) {
using std::get;
do_in_order( [&]{ f( get<Is>(std::forward<Tuple>(tup)) ); }... );
}

and the public-facing interface:

template<class Tuple, class F>
void for_each( Tuple&& tup, F&& f ) {
auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f) );
}

while it states Tuple it works on std::arrays and std::pairs. It also forward the r/l value category of said object down to the function object it invokes. Also note that if you have a free function get<N> on your custom type, and you override get_indexes, the above for_each will work on your custom type.

As noted, do_in_order while neat isn't supported by many compilers, as they don't like the lambda with unexpanded parameter packs being expanded into parameter packs.

We can inline do_in_order in that case

template<size_t... Is, class Tuple, class F>
void for_each( std::index_sequence<Is...>, Tuple&& tup, F&& f ) {
using std::get;
int unused[] = { 0, ( (void)f(get<Is>(std::forward<Tuple>(tup)), 0 )... }
(void)unused; // blocks warnings
}

this doesn't cost much verbosity, but I personally find it less clear. The shadow magic of how do_in_order works is obscured by doing it inline in my opinion.

index_sequence (and supporting templates) is a C++14 feature that can be written in C++11. Finding such an implementation on stack overflow is easy. A current top google hit is a decent O(lg(n)) depth implementation, which if I read the comments correctly may be the basis for at least one iteration of the actual gcc make_integer_sequence (the comments also point out some further compile-time improvements surrounding eliminating sizeof... calls).

Alternatively we can write:

template<class F, class...Args>
void for_each_arg(F&&f,Args&&...args){
using discard=int[];
(void)discard{0,((void)(
f(std::forward<Args>(args))
),0)...};
}

And then:

template<size_t... Is, class Tuple, class F>
void for_each( std::index_sequence<Is...>, Tuple&& tup, F&& f ) {
using std::get;
for_each_arg(
std::forward<F>(f),
get<Is>(std::forward<Tuple>(tup))...
);
}

Which avoids the manual expand yet compiles on more compilers. We pass the Is via the auto&&i parameter.

In C++1z we can also use std::apply with a for_each_arg function object to do away with the index fiddling.

How to iterate over a tuple of elemenets that have the same base class

You might just using std::apply:

std::apply([](auto&...args){ (args.call_base_method(), ...); }, tuple);

Else to answer your question, you might do something like:

template <std::size_t... Is, typename tuple_type>
my_class_base& at_impl(std::index_sequence<Is...>, tuple_type& tuple, size_t n)
{
my_class_base* bases[] = {&std::get<Is>(tuple)...};
*return bases[n];
}

template <typename tuple_type>
my_class_base& at(tuple_type& tuple, size_t n)
{
auto seq = std::make_index_sequence<std::tuple_size<tuple_type>::value>();
return at_impl(seq, tuple, n);
}

Demo

Iterate over tuple elements with std::apply

The order in which the arguments to your inner, empty lambda [](...){} are evaluated is unspecified (even after the paper on evaluation order).

Just use a fold on the comma operator:

std::apply([](auto&&... xs) {
((std::cout << std::forward<decltype(xs)>(xs) << '\n'), ...); },
std::make_tuple(1, 2.f, 3.0));

Demo. Or binary-fold over cout directly. Once we introduce an "packize" operator, for which there's a proposal out already (using [:]), you could also write (std::cout << [:]tup << '\n'), ....



Related Topics



Leave a reply



Submit