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());
How to iterate over a std::tuple in C++ 11
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::array
s and std::pair
s. 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.
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
Is there some way perhaps with
std::apply
orstd::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);
Iterate over tuple of functors in c++11
Not particularly creative but...
If you create a foo()
function as follows
template <typename ... Args>
bool foo (std::tuple<Args...> const & tf)
{ return bar<0>(tf); }
that call, starting with template index 0, the following couple of template (SFINAE enabled/disabled) alternative functions
template <std::size_t I, typename ... Args>
typename std::enable_if<(I >= sizeof...(Args)), bool>::type
bar (std::tuple<Args...> const &)
{ return false; }
template <std::size_t I, typename ... Args>
typename std::enable_if<(I < sizeof...(Args)), bool>::type
bar (std::tuple<Args...> const & tf)
{ return baz(std::get<I>(tf)) || bar<I+1U>(tf); }
and an additional baz()
function to call single functors
bool baz (functor const & f)
{ return f(); }
I suppose you can obtain what do you want.
The following is a full working example
#include <tuple>
#include <iostream>
#include <functional>
using functor = std::function<bool()>;
bool baz (functor const & f)
{ return f(); }
template <std::size_t I, typename ... Args>
typename std::enable_if<(I >= sizeof...(Args)), bool>::type
bar (std::tuple<Args...> const &)
{ return false; }
template <std::size_t I, typename ... Args>
typename std::enable_if<(I < sizeof...(Args)), bool>::type
bar (std::tuple<Args...> const & tf)
{ return baz(std::get<I>(tf)) || bar<I+1U>(tf); }
template <typename ... Args>
bool foo (std::tuple<Args...> const & tf)
{ return bar<0>(tf); }
bool f0 () { std::cout << "f0 "; return false; }
bool f1 () { std::cout << "f1 "; return false; }
bool f2 () { std::cout << "f2 "; return false; }
bool f3 () { std::cout << "f3 "; return false; }
bool f4 () { std::cout << "f4 "; return true; }
bool f5 () { std::cout << "f5 "; return false; }
bool f6 () { std::cout << "f6 "; return false; }
bool f7 () { std::cout << "f7 "; return false; }
bool f8 () { std::cout << "f8 "; return false; }
bool f9 () { std::cout << "f9 "; return false; }
int main ()
{
auto tf = std::make_tuple(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9);
foo(tf);
}
Obviously you can avoid the use of baz()
and simply call the functors as follows
return std::get<I>(tf)() || bar<I+1U>(tf);
but you loose a check over the Args...
types.
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
Can Branches with Undefined Behavior Be Assumed Unreachable and Optimized as Dead Code
When Does Move Constructor Get Called
Switch "Transfer of Control Bypasses Initialization Of:" When Calling a Function
Std::Lexical_Cast - Is There Such a Thing
Memory Allocation Profiling in C++
Advantages of Classes with Only Static Methods in C++
Deciphering C++ Template Error Messages
Comma Operator in If Condition
Is a Linked-List Implementation Without Using Pointers Possible or Not
Making a Borderless Window with for Qt
Fastest Way to Get the Integer Part of Sqrt(N)
Debug Assertion Failed! Expression: _Acrt_First_Block == Header
Is There a Standard Way of Moving a Range into a Vector
Why Can't We Declare a Namespace Within a Class
Deprecated Throw-List in C++11
Do I Have to Use Atomic<Bool> for "Exit" Bool Variable