I am attempting to map function f over tuples t0, t1, etc. to return the tuple
std::tuple<f(std::get<0>(t0),std:get<0>(t1),...),f(std::get<1>(t0),std::get<1>(t1),...),...). I have a version working using car,cdr, and cons but am trying to get a version working using std::index_sequence.
The code:
// Helper
template<typename T>
using make_tuple_index = std::make_index_sequence<std::tuple_size<T>::value>;
// Implementation
template<typename F, typename... Ts, std::size_t... Is>
auto mapx_n_impl(const F& f, std::index_sequence<Is...>, const Ts&... t)
{ return std::make_tuple(f(std::get<Is>(t...))...); }
// Interface
template<typename T,
typename F,
typename Indices = make_tuple_index<T>>
auto map(const T& t, const F& f)
{ return mapx_impl(t, f, Indices{}); }
// Test
auto tup1 = std::make_tuple(1.0, 2.0, 3.0);
auto tup2 = std::make_tuple(0.0, 1.0, 2.0);
auto r = mapx_n([](auto x, auto y) { return x - y; }, tup1, tup2);
The problem is expanding the parameter packs in the implementation return statement. I need it to expand t in the "inner" loop and Is in the "outer" loop. How is the expansion controlled? And, how do I fix my return statement?
UPDATE:
Based on the response from #Yakk and the further elucidation by #max66, I have simplified my code as much as I think possible. The current version integrates a version of the parameter pack expansion helper from #Yakk's answer as well as factoring out the get_element call into a lambda.
// invoke_with_pack
template<std::size_t... Is, typename F>
auto invoke_with_pack(std::index_sequence<Is...>, F&& function)
{ return function(std::integral_constant<std::size_t, Is>{}...); }
// nth
template<natural N, typename... Ts>
using nth = typename std::tuple_element<N, std::tuple<Ts...>>::type;
// make_tuple_index -- Helper template for computing indices
// corresponding to a tuple.
template<typename T>
using make_tuple_index = std::make_index_sequence<std::tuple_size<T>::value>;
// map_n -- Map <function> over <tuples> t0,t1,...
template<typename F,
typename... Ts,
typename Indices = make_tuple_index<nth<0,Ts...>>>
auto map_n(F&& function, Ts&... tuples)
{
auto get_element = [&](auto I) { return function(std::get<I>(tuples)...); };
return invoke_with_pack(Indices{}, [&](auto... Is) {
return std::make_tuple(get_element(Is)...);
});
}
Now on to figuring out how to implement fold_left and fold_right with indexes instead of car,cdr and cons.
Start with this:
namespace utility {
template<std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&& f)->decltype(auto) {
return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto index_upto( std::integral_constant<std::size_t, N> ={} ) {
return index_over( std::make_index_sequence<N>{} );
}
}
that lets us avoid having to write a whole pile of functions just to expand some parameter packs. index_upto<7>()([](auto...Is){ /* here */ }) gives you a context where you have a bunch of compile time integral constants 0 through 6 in a pack.
template<class F, class T0, class...Tuples>
auto map_over_tuples( F&& f, T0&... t0, Tuples&&... tuples ) {
using tuple_size = typename std::tuple_size< std::decay_t<T0> >::type;
auto get_element = [&](auto I){
return f(std::get<I>(std::forward<T0>(t0)), std::get<I>(std::forward<Tuples>(tuples)...));
};
return index_upto<tuple_size{}>()([&](auto...Is){
return std::make_tuple( get_element(Is)... );
});
}
in some compilers, use of I has to be replaced with decltype(I)::value in get_element.
The problem is expanding the parameter packs in the implementation return statement. I need it to expand t in the "inner" loop and Is in the "outer" loop. How is the expansion controlled? And, how do I fix my return statement?
I don't see a simple and elegant way to do this.
It seems to me that you have to decouple the two packs in same way and expand first one then another.
If you see the Yakk solution, you see the inner expansion (t...) through a lambda function with single calling f() in it.
The following is a solution, based on the same principle with a template function, and the use of std::apply to leave the call of f() outside.
Frankly, I think the Yakk solution is more efficient (no need of unuseful tuples creation) so take this example as an oddity
#include <tuple>
#include <iostream>
template <std::size_t I, typename ... Ts>
auto getN (Ts const & ... t)
{ return std::make_tuple(std::get<I>(t)...); }
template<typename F, typename... Ts, std::size_t... Is>
auto mapx_n_impl(const F& f, std::index_sequence<Is...>, const Ts&... t)
{ return std::make_tuple(std::apply(f, getN<Is>(t...))...); }
template <typename F, typename T0, typename ... Ts>
auto mapx_n (F const & f, T0 const & t0, Ts const & ... ts)
{ return mapx_n_impl(f,
std::make_index_sequence<std::tuple_size<T0>::value> {}, t0, ts...); }
int main ()
{
// Test
auto tup1 = std::make_tuple(1.0, 2.0, 3.0);
auto tup2 = std::make_tuple(0.0, 1.0, 2.0);
auto r = mapx_n([](auto x, auto y) { return x - y; }, tup1, tup2);
std::cout << std::get<0U>(r) << std::endl;
std::cout << std::get<1U>(r) << std::endl;
std::cout << std::get<2U>(r) << std::endl;
}
Based on the great solutions I've developed a more general function to transform and fold (reduce) tuples. As you were mentioning fold_left and fold_right in your question, this might be of interest for the discussion.
The basic idea is to apply a second functor to the mapped (a.k.a. transformed) tuple rather than calling std::make_tuple as you did in your solution. This allows many algorithms (e.g. count_if, all_of, any_of etc.) to be implemented easily.
Live example here.
#include <tuple>
#include <functional>
#define FWD(x) std::forward<decltype(x)>(x)
namespace tuple_utils {
template<class UnaryFunc, std::size_t... Idx>
constexpr auto apply_for_each_index(std::index_sequence<Idx...>, UnaryFunc&& f) {
return FWD(f)(std::integral_constant<std::size_t, Idx>{}...);
}
template<typename T>
using make_tuple_index = std::make_index_sequence<std::tuple_size<std::decay_t<T>>::value>;
template<class... Ts>
using first_element_t = typename std::tuple_element<0, std::tuple<Ts...>>::type;
template<class T>
constexpr size_t tuple_size_v = std::tuple_size_v<std::decay_t<T>>;
template<class Map, class Reduce, class... Tuples>
constexpr auto
transform_reduce(Map &&transform_func, Reduce &&reduce_func, Tuples&&... tuples) {
using first_tuple_t = first_element_t<Tuples...>;
constexpr size_t first_tuple_size = tuple_size_v<first_tuple_t>;
static_assert(((tuple_size_v<Tuples> == first_tuple_size) && ...), "all tuples must be of same size!");
auto transform_elements_at = [&](auto Idx){
return FWD(transform_func)(std::get<Idx>(FWD(tuples))...);
};
using Indices = make_tuple_index<first_tuple_t>;
return apply_for_each_index(
Indices{},
[&](auto... Indices) {
return FWD(reduce_func)(transform_elements_at(Indices)...);
}
);
}
}
int main()
{
using tuple_utils::transform_reduce;
auto make_tuple = [](auto&&... xs) { return std::make_tuple(FWD(xs)...); };
auto equal = [](auto&& first, auto&&... rest){return ((FWD(first) == FWD(rest)) && ... ); };
constexpr auto all = [](auto... bs) { return (bs && ...);};
constexpr auto any = [](auto... bs) { return (bs || ...);};
constexpr auto count = [](auto... bs) { return (bs + ...); };
static_assert(transform_reduce(std::equal_to<>(), make_tuple, std::tuple{1,2,3,4}, std::tuple{1,2,7,8}) == std::tuple{true, true, false, false});
static_assert(transform_reduce(equal, all, std::tuple{1,2,3,4}, std::tuple{1,2,7,8}) == false);
static_assert(transform_reduce(equal, all, std::tuple{1,2,3,4}, std::tuple{1,2,3,4}, std::tuple{1,2,3,4}) == true);
static_assert(transform_reduce(equal, any, std::tuple{1,2,3,4}, std::tuple{1,2,7,8}) == true);
static_assert(transform_reduce(equal, count, std::tuple{1,2,3,4}, std::tuple{1,2,7,8}) == 2);
}
I have the following piece of code (c++11):
template <typename F,
typename FirstT,
typename... FIn>
auto min_on(F f, FirstT first, FIn... v) -> typename std::common_type<FirstT, FIn...>::type
{
using rettype = typename std::common_type<FirstT, FIn...>::type;
using f_rettype = decltype(f(first));
rettype result = first;
f_rettype result_trans = f(first);
f_rettype v_trans;
(void)std::initializer_list<int>{
((v_trans = f(v), v_trans < result_trans)
? (result = static_cast<rettype>(v), result_trans = v_trans, 0)
: 0)...};
return result;
}
Which basically returns the argument result that produced the minimum value for expression f(result). This can be called like this:
auto mod7 = [](int x)
{
return x % 7;
};
auto minimum = min_on(mod7, 2, 8, 17, 5);
assert( minimum == 8); // since 8%7 = 1 -> minimum value for all arguments passed
Now I would like to use this in a 'curried' way so that I can get a variadic lambda from min_on and then call it with arguments (that I might receive later), like so:
auto mod7 = [](int x)
{
return x % 7;
};
auto f_min = min_on(mod7);
auto minimum = f_min(2, 8, 17, 5);
// or
auto minimum = min_on(mod7)(2, 8, 17, 5);
Is this even possible?
In C++11, the following works if you’re willing to manually create the function object:
template <typename F>
struct min_on_t {
min_on_t(F f) : f(f) {}
template <typename T, typename... Ts>
auto operator ()(T x, Ts... xs) -> typename std::common_type<T, Ts...>::type
{
// Magic happens here.
return f(x);
}
private: F f;
};
template <typename F>
auto min_on(F f) -> min_on_t<F>
{
return min_on_t<F>{f};
}
And then call it:
auto minimum = min_on(mod7)(2, 8, 17, 5);
To use lambdas in C++14, you need to omit the trailing return type because you cannot specify the type of the lambda without assigning it to a variable first, because a lambda expression cannot occur in an unevaluated context.
template <typename F>
auto min_on(F f)
{
return [f](auto x, auto... xs) {
using rettype = std::common_type_t<decltype(x), decltype(xs)...>;
using f_rettype = decltype(f(x));
rettype result = x;
f_rettype result_trans = f(x);
(void)std::initializer_list<int>{
(f(xs) < result_trans
? (result = static_cast<rettype>(xs), result_trans = f(xs), 0)
: 0)...};
return result;
};
}
Not sure on C++11, but in C++14, you could create a lambda to wrap your function in:
auto min_on_t = [](auto f) {
return [=](auto ... params) {
return min_on(f, params...);
};
};
auto min_t = min_on_t(mod7);
auto minimum = min_t(2, 8, 17, 5);
Live on Coliru
In C++14 this is easy.
template<class F>
auto min_on( F&& f ) {
return [f=std::forward<F>(f)](auto&& arg0, auto&&...args) {
// call your function here, using decltype(args)(args) to perfect forward
};
}
Many compilers got auto return type deduction and arguments in lambdas working prior to full C++14 support. So a nominal C++11 compiler might be able to compile this:
auto min_on = [](auto&& f) {
return [f=decltype(f)(f)](auto&& arg0, auto&&...args) {
// call your function here, using decltype(args)(args) to perfect forward
};
}
in C++11:
struct min_on_helper {
template<class...Args>
auto operator()(Args&&...args)
-> decltype( min_on_impl(std::declval<Args>()...) )
{
return min_on_impl(std::forward<Args>(args)...);
}
};
is boilerplate. This lets us pass the entire overload set of min_on_impl around as one object.
template<class F, class T>
struct bind_1st_t {
F f;
T t;
template<class...Args>
typename std::result_of<F&(T&, Args...)>::type operator()(Args&&...args)&{
return f( t, std::forward<Args>(args)... );
}
template<class...Args>
typename std::result_of<F const&(T const&, Args...)>::type operator()(Args&&...args)const&{
return f( t, std::forward<Args>(args)... );
}
template<class...Args>
typename std::result_of<F(T, Args...)>::type operator()(Args&&...args)&&{
return std::move(f)( std::move(t), std::forward<Args>(args)... );
}
};
template<class F, class T>
bind_1st_t< typename std::decay<F>::type, typename std::decay<T>::type >
bind_1st( F&& f, T&& t ) {
return {std::forward<F>(f), std::forward<T>(t)};
}
gives us bind_1st.
template<class T>
auto min_on( T&& t )
-> decltype( bind_1st( min_on_helper{}, std::declval<T>() ) )
{
return bind_1st(min_on_helper{}, std::forward<T>(t));
}
is modular and solves your problem: both min_on_helper and bind_1st can be tested independently.
You can also replace bind_1st with a call to std::bind, but in my experience the quirks of std::bind make me extremely cautious about recommending that to anyone.
I have a map_n template that applies an N-arity function to each set of elements from N input tuples to produce a new output tuple. All input tuples must be of same length (which I should probably check with static assert).
The code works correctly, except I have not been able to write the recursion termination condition partial specialization in a generic way as shown in the following code snippet.
#include <tuple>
#include <cassert>
namespace impl {
// car, cdr, cons implementation
//
template<unsigned... XS>
struct sequence {
template<unsigned X>
using cons = sequence<X, XS...>;
};
template<unsigned start, unsigned end>
struct range {
static_assert(start < end, "Range: start > end");
using type = typename range<start + 1, end>::type::template cons<start>;
};
template<unsigned start>
struct range<start, start> {
using type = sequence<>;
};
template<typename T, unsigned... N>
auto select(const T& t, sequence<N...>) {
return std::make_tuple(std::get<N>(t)...);
}
} // end namespace impl
// car, cdr, cons
//
// empty list
//
constexpr const std::tuple<> empty;
// car
//
template<typename T>
auto car(const T& t) { return std::get<0>(t); }
// cdr
//
template<typename T, typename R = typename impl::range<1, std::tuple_size<T>::value>::type>
auto cdr(const T& t) {
return impl::select(t, R());
}
// cons
//
template<typename X, typename... XS>
auto cons(X x, const std::tuple<XS...>& t) {
return std::tuple_cat(std::make_tuple(x), t);
}
namespace impl {
// map_n implementation
template<typename F, typename... Ts>
struct map_n_impl {
static auto map(const F& f, const Ts&... t) {
return cons(
f(car(t)...),
map_n_impl<F, decltype(cdr(t))...>::map(f, cdr(t)...)
);
}
};
// NOTE: Need a more general specialization here
//
template<typename F>
struct map_n_impl<F, std::tuple<>, std::tuple<>> {
static std::tuple<> map(const F&, const std::tuple<>&, const std::tuple<>&)
{
return std::make_tuple();
}
};
} // end namespace impl
// map_n
//
template<typename F, typename... Ts>
auto map_n(const F& f, const Ts&... t) {
return impl::map_n_impl<F, Ts...>::map(f, t...);
}
int main(int, const char **) {
{
auto tup1 = std::make_tuple(1.0, 2.0, 3.0);
auto tup2 = std::make_tuple(0.0, 1.0, 2.0);
auto r = map_n([](auto x, auto y) { return x - y; }, tup1, tup2);
assert(std::get<0>(r) == 1.0);
assert(std::get<1>(r) == 1.0);
assert(std::get<2>(r) == 1.0);
}
// {
// auto tup1 = std::make_tuple(1.0, 2.0, 3.0);
// auto tup2 = std::make_tuple(0.0, 1.0, 2.0);
// auto tup3 = std::make_tuple(4.0, 5.0, 6.0);
// auto r = map_n([](auto x, auto y, auto z) { return x - y + z; }, tup1, tup2, tupe3);
// assert(std::get<0>(r) == 5.0);
// assert(std::get<1>(r) == 6.0);
// assert(std::get<2>(r) == 7.0);
// }
return 0;
}
It's a lot easier than what you're going for. You don't need map_n_impl at all. If we're going to stick with the functional, recursive approach - we need two overloads of map_n: one for all the tuples are non-empty and one for all the tuples are empty. We'll use Columbo's bool_pack trick to figure out if they're all empty or not:
template <bool... >
struct bool_pack;
template <bool... b>
using all_true = std::is_same<bool_pack<true, b...>, bool_pack<b..., true>>;
template <class... T>
using all_empty = all_true<std::is_same<T, std::tuple<>>::value...>;
And then just use that to SFINAE the two disjoint conditions:
template<typename F, typename... Ts,
std::enable_if_t<!all_empty<Ts...>::value, int*> = nullptr>
auto map_n(const F& f, const Ts&... t) {
return cons(
f(car(t)...),
map_n(f, cdr(t)...)
);
}
template<typename F, typename... Ts,
std::enable_if_t<all_empty<Ts...>::value, int*> = nullptr>
auto map_n(const F& , const Ts&... t) {
return std::make_tuple(t...);
}
Note that tuple isn't really the best way to do cons/car/cdr in C++ - it's not very cdr-able. More appropriate would be nested pairs.
You could also built up the whole tuple in one go with the index sequence trick. It's a little annoying here because we need to unpack two parameter packs differently, hence the extra call lambda. There may be a better way to do this:
template <size_t... Is, class F, class... Ts>
auto map_n_impl(std::index_sequence<Is...>, const F& f, const Ts&... ts) {
auto call = [&](auto idx){
return f(std::get<idx>(ts)...);
};
return std::make_tuple(
call(std::integral_constant<size_t, Is>{})...
);
}
template <class F, class T0, class... Ts>
auto map_n(const F& f, const T0& t0, const Ts&... ts) {
return map_n_impl(
std::make_index_sequence<std::tuple_size<T0>::value>{},
f,
t0,
ts...);
}
I would like to write function as this find:
multi_set<int, string, double, myType> m; //vector of tuples
m.insert(/*some data*/);
m.find<1,2>("something",2.123);
Or
m.find<0,3>(1,instanceOfMyType);
m.find<1>("somethingelse");
Where find can be parametrized corresponding to any subset of tuple parameters.
My code so far:
template <typename ... T>
class multi_set{
typedef tuple < T... > Tuple;
vector<tuple<T...>> data = vector<tuple<T...>>();
public:
void insert(T... t){
data.push_back(tuple<T...>(t...));
}
template<size_t ... Pos>
void find(???){
// then I would like to use those params to search through data and
// return first matching item
}
}
// test whether a particular tuple is a match
template<size_t... Pos>
static bool is_match(const Tuple& tuple, const typename std::tuple_element<Pos, Tuple>::type &... args) {
std::initializer_list<bool> results = { (std::get<Pos>(tuple) == args)... };
return std::all_of(results.begin(), results.end(), [](bool p) { return p; });
}
// Find the first one that is a match.
template<size_t... Pos>
typename vector<Tuple>::const_iterator find(const typename std::tuple_element<Pos, Tuple>::type &... args) const {
return std::find_if(data.begin(), data.end(), [&](const Tuple & tup) { return is_match<Pos...>(tup, args...); });
}
It's also possible to have find take a type parameter pack and perfectly forward, rather than taking fixed types with tuple_element. The benefit is that you can avoid an unnecessary conversion if == is transparent. The cost is that you can't take anything that can't be perfectly forwarded any more (e.g., braced initializer lists, 0 as a null pointer constant). A side benefit appears to be that MSVC 2013 doesn't choke on this version:
// test whether a particular tuple is a match
template<size_t... Pos, class... Args>
static bool is_match(const Tuple& tuple, Args&&... args) {
std::initializer_list<bool> results = { (std::get<Pos>(tuple) == std::forward<Args>(args))... };
return std::all_of(results.begin(), results.end(), [](bool p) { return p; });
}
// Find the first one that is a match.
template<size_t... Pos, class... Args>
typename vector<Tuple>::const_iterator find(Args&&... args) const {
return std::find_if(data.begin(), data.end(), [&](const Tuple & tup) { return is_match<Pos...>(tup, std::forward<Args>(args)...); });
}
You should look into boost::multi_index. It is very close to what you are looking for.
http://www.boost.org/doc/libs/1_54_0/libs/multi_index/doc/tutorial/index.html
This is a function that takes a seed value, and a set of lambdas. It feeds that seed value through each of the lambdas in turn:
template<class... Fs, class R>
R chain( R r, Fs&&... fs ) {
using in_order = int[];
(void)(in_order{0,
(
(r = std::forward<Fs>(fs)( r ))
, void(), 0
)...
});
return r;
}
Inside your class, we use the above:
template<size_t... Pos, class...Us>
typename std::vector<Tuple>::const_iterator
find(Us const&... us) const {
return std::find_if(
data.begin(), data.end(),
[&](const Tuple & tup) {
return chain(
true,
[&](bool old){
return old && (std::get<Pos>(tup) == us);
}...
);
}
);
}
this compiles in clang, but not g++ 4.9.2 -- g++ doesn't like parameter packs inside lambdas.
Note the fact we take Us const&... -- this allows for transparent ==, which is important in some cases. std::string == char const* is a classic example, where if you force find to take the same value as in the tuple, you'll force a needless allocation in calling find.
In C++1z, the chain call can be replaced with:
( ... && (std::get<Pos>(tup) == us) )
which is conceptually identical, but much easier to read. This is known as a "fold expression".
Now, a problem with the above is that it uses forwarding references, which causes imperfect forwarding problems of perfect forwarding.
The most annoying of which is the inability to use {} to construct arguments.
If we use matching types, we instead force non-transparent comparison, which can be expensive (examine std::string compared to "hello this is a c string" -- it causes possibly allocation if we force the c string into a std::string.)
A way around this is to type erase down to the concept of equality with a given type.
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<class T>struct tag{using type=T;};
template<class...>struct types{using type=types;};
template<class T>
using block_deduction = typename tag<T>::type;
template<class F, class Sig, class T=void>
struct erase_view_op;
template<class F, class R, class...Ts, class T>
struct erase_view_op<F, R(Ts...), T>
{
using fptr = R(*)(void const*, Ts&&...);
fptr f;
void const* ptr;
private:
template<class U>
erase_view_op(U&& u, int):
f([](void const* p, Ts&&...ts)->R{
U& u = reinterpret_cast<U&>( *static_cast<std::decay_t<U>*>(const_cast<void*>(p)) );
return F{}( u, std::forward<Ts>(ts)... );
}),
ptr( static_cast<void const*>(std::addressof(u)) )
{}
public:
template<class U, class=std::enable_if_t< !std::is_same<std::decay_t<U>,erase_view_op>{} && std::is_convertible< std::result_of_t<F(U,Ts...)>, R >{} >>
erase_view_op(U&& u):erase_view_op( std::forward<U>(u), 0 ){}
template<class U=T, class=std::enable_if_t< !std::is_same<U, void>{} >>
erase_view_op( block_deduction<U>&& u ):erase_view_op( std::move(u), 0 ){}
erase_view_op( erase_view_op const& ) = default;
erase_view_op( erase_view_op&& ) = default;
R operator()( Ts... ts ) const {
return f( ptr, std::forward<Ts>(ts)... );
}
};
struct equality {
template<class lhs, class rhs>
bool operator()(lhs const& l, rhs const& r)const {
return l==r;
}
};
template<class T>
using erase_equal_to = erase_view_op< equality, bool(T const&), T >;
using string_equal_to = erase_equal_to< std::string >;
int main() {
static_assert( std::is_same< bool, std::result_of_t< std::equal_to<>(decltype("hello"), std::string const&) > >{}, "hmm" );
string_equal_to s = "hello";
string_equal_to s2 = {{"hello"}};
(void)s2;
std::string x = "hello";
std::string y = "jello";
std::cout << s(x) << s(y) << '\n';
}
then we rewrite find:
template<size_t... Pos>
typename std::vector<Tuple>::const_iterator
find(erase_equal_to< std::remove_reference_t<std::tuple_element_t<Pos, Tuple>> >... us) const {
return std::find_if(
data.begin(), data.end(),
[&](const Tuple & tup) {
return chain(
true,
[&](bool old){
return old && us(std::get<Pos>(tup));
}...
);
}
);
}
which does both transparent equality and allows {} based construction (well, it does require {{}} based construction -- the outer to say we are constructing the eraser, the inner to construct the T).
How can I iterate over a tuple (using C++11)? I tried the following:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
but this doesn't work:
Error 1: sorry, unimplemented: cannot expand ‘Listener ...’ into a fixed-length argument list.
Error 2: i cannot appear in a constant expression.
So, how do I correctly iterate over the elements of a tuple?
I have an answer based on Iterating over a Tuple:
#include <tuple>
#include <utility>
#include <iostream>
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
print(std::tuple<Tp...>& t)
{ }
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
print(std::tuple<Tp...>& t)
{
std::cout << std::get<I>(t) << std::endl;
print<I + 1, Tp...>(t);
}
int
main()
{
typedef std::tuple<int, float, double> T;
T t = std::make_tuple(2, 3.14159F, 2345.678);
print(t);
}
The usual idea is to use compile time recursion. In fact, this idea is used to make a printf that is type safe as noted in the original tuple papers.
This can be easily generalized into a for_each for tuples:
#include <tuple>
#include <utility>
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
{ }
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>& t, FuncT f)
{
f(std::get<I>(t));
for_each<I + 1, FuncT, Tp...>(t, f);
}
Though this then requires some effort to have FuncT represent something with the appropriate overloads for every type the tuple might contain. This works best if you know all the tuple elements will share a common base class or something similar.
In C++17, you can use std::apply with fold expression:
std::apply([](auto&&... args) {((/* args.dosomething() */), ...);}, the_tuple);
A complete example for printing a tuple:
#include <tuple>
#include <iostream>
int main()
{
std::tuple t{42, 'a', 4.2}; // Another C++17 feature: class template argument deduction
std::apply([](auto&&... args) {((std::cout << args << '\n'), ...);}, t);
}
[Online Example on Coliru]
This solution solves the issue of evaluation order in M. Alaggan's answer.
C++ is introducing expansion statements for this purpose. They were originally on track for C++20 but narrowly missed the cut due to a lack of time for language wording review (see here and here).
The currently agreed syntax (see the links above) is:
{
auto tup = std::make_tuple(0, 'a', 3.14);
template for (auto elem : tup)
std::cout << elem << std::endl;
}
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());
In C++17 you can do this:
std::apply([](auto ...x){std::make_tuple(x.do_something()...);} , the_tuple);
This already works in Clang++ 3.9, using std::experimental::apply.
A more simple, intuitive and compiler-friendly way of doing this in C++17, using if constexpr:
// prints every element of a tuple
template<size_t I = 0, typename... Tp>
void print(std::tuple<Tp...>& t) {
std::cout << std::get<I>(t) << " ";
// do things
if constexpr(I+1 != sizeof...(Tp))
print<I+1>(t);
}
This is compile-time recursion, similar to the one presented by #emsr. But this doesn't use SFINAE so (I think) it is more compiler-friendly.
Use Boost.Hana and generic lambdas:
#include <tuple>
#include <iostream>
#include <boost/hana.hpp>
#include <boost/hana/ext/std/tuple.hpp>
struct Foo1 {
int foo() const { return 42; }
};
struct Foo2 {
int bar = 0;
int foo() { bar = 24; return bar; }
};
int main() {
using namespace std;
using boost::hana::for_each;
Foo1 foo1;
Foo2 foo2;
for_each(tie(foo1, foo2), [](auto &foo) {
cout << foo.foo() << endl;
});
cout << "foo2.bar after mutation: " << foo2.bar << endl;
}
http://coliru.stacked-crooked.com/a/27b3691f55caf271
Here's an easy C++17 way of iterating over tuple items with just standard library:
#include <tuple> // std::tuple
#include <functional> // std::invoke
template <
size_t Index = 0, // start iteration at 0 index
typename TTuple, // the tuple type
size_t Size =
std::tuple_size_v<
std::remove_reference_t<TTuple>>, // tuple size
typename TCallable, // the callable to be invoked for each tuple item
typename... TArgs // other arguments to be passed to the callable
>
void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
if constexpr (Index < Size)
{
std::invoke(callable, args..., std::get<Index>(tuple));
if constexpr (Index + 1 < Size)
for_each<Index + 1>(
std::forward<TTuple>(tuple),
std::forward<TCallable>(callable),
std::forward<TArgs>(args)...);
}
}
Example:
#include <iostream>
int main()
{
std::tuple<int, char> items{1, 'a'};
for_each(items, [](const auto& item) {
std::cout << item << "\n";
});
}
Output:
1
a
This can be extended to conditionally break the loop in case the callable returns a value (but still work with callables that do not return a bool assignable value, e.g. void):
#include <tuple> // std::tuple
#include <functional> // std::invoke
template <
size_t Index = 0, // start iteration at 0 index
typename TTuple, // the tuple type
size_t Size =
std::tuple_size_v<
std::remove_reference_t<TTuple>>, // tuple size
typename TCallable, // the callable to bo invoked for each tuple item
typename... TArgs // other arguments to be passed to the callable
>
void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
if constexpr (Index < Size)
{
if constexpr (std::is_assignable_v<bool&, std::invoke_result_t<TCallable&&, TArgs&&..., decltype(std::get<Index>(tuple))>>)
{
if (!std::invoke(callable, args..., std::get<Index>(tuple)))
return;
}
else
{
std::invoke(callable, args..., std::get<Index>(tuple));
}
if constexpr (Index + 1 < Size)
for_each<Index + 1>(
std::forward<TTuple>(tuple),
std::forward<TCallable>(callable),
std::forward<TArgs>(args)...);
}
}
Example:
#include <iostream>
int main()
{
std::tuple<int, char> items{ 1, 'a' };
for_each(items, [](const auto& item) {
std::cout << item << "\n";
});
std::cout << "---\n";
for_each(items, [](const auto& item) {
std::cout << item << "\n";
return false;
});
}
Output:
1
a
---
1
You need to use template metaprogramming, here shown with Boost.Tuple:
#include <boost/tuple/tuple.hpp>
#include <iostream>
template <typename T_Tuple, size_t size>
struct print_tuple_helper {
static std::ostream & print( std::ostream & s, const T_Tuple & t ) {
return print_tuple_helper<T_Tuple,size-1>::print( s, t ) << boost::get<size-1>( t );
}
};
template <typename T_Tuple>
struct print_tuple_helper<T_Tuple,0> {
static std::ostream & print( std::ostream & s, const T_Tuple & ) {
return s;
}
};
template <typename T_Tuple>
std::ostream & print_tuple( std::ostream & s, const T_Tuple & t ) {
return print_tuple_helper<T_Tuple,boost::tuples::length<T_Tuple>::value>::print( s, t );
}
int main() {
const boost::tuple<int,char,float,char,double> t( 0, ' ', 2.5f, '\n', 3.1416 );
print_tuple( std::cout, t );
return 0;
}
In C++0x, you can write print_tuple() as a variadic template function instead.
First define some index helpers:
template <size_t ...I>
struct index_sequence {};
template <size_t N, size_t ...I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};
template <size_t ...I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};
With your function you would like to apply on each tuple element:
template <typename T>
/* ... */ foo(T t) { /* ... */ }
you can write:
template<typename ...T, size_t ...I>
/* ... */ do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
std::tie(foo(std::get<I>(ts)) ...);
}
template <typename ...T>
/* ... */ do_foo(std::tuple<T...> &ts) {
return do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}
Or if foo returns void, use
std::tie((foo(std::get<I>(ts)), 1) ... );
Note: On C++14 make_index_sequence is already defined (http://en.cppreference.com/w/cpp/utility/integer_sequence).
If you do need a left-to-right evaluation order, consider something like this:
template <typename T, typename ...R>
void do_foo_iter(T t, R ...r) {
foo(t);
do_foo(r...);
}
void do_foo_iter() {}
template<typename ...T, size_t ...I>
void do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
do_foo_iter(std::get<I>(ts) ...);
}
template <typename ...T>
void do_foo(std::tuple<T...> &ts) {
do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}
If you want to use std::tuple and you have C++ compiler which supports variadic templates, try code bellow (tested with g++4.5). This should be the answer to your question.
#include <tuple>
// ------------- UTILITY---------------
template<int...> struct index_tuple{};
template<int I, typename IndexTuple, typename... Types>
struct make_indexes_impl;
template<int I, int... Indexes, typename T, typename ... Types>
struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...>
{
typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type;
};
template<int I, int... Indexes>
struct make_indexes_impl<I, index_tuple<Indexes...> >
{
typedef index_tuple<Indexes...> type;
};
template<typename ... Types>
struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...>
{};
// ----------- FOR EACH -----------------
template<typename Func, typename Last>
void for_each_impl(Func&& f, Last&& last)
{
f(last);
}
template<typename Func, typename First, typename ... Rest>
void for_each_impl(Func&& f, First&& first, Rest&&...rest)
{
f(first);
for_each_impl( std::forward<Func>(f), rest...);
}
template<typename Func, int ... Indexes, typename ... Args>
void for_each_helper( Func&& f, index_tuple<Indexes...>, std::tuple<Args...>&& tup)
{
for_each_impl( std::forward<Func>(f), std::forward<Args>(std::get<Indexes>(tup))...);
}
template<typename Func, typename ... Args>
void for_each( std::tuple<Args...>& tup, Func&& f)
{
for_each_helper(std::forward<Func>(f),
typename make_indexes<Args...>::type(),
std::forward<std::tuple<Args...>>(tup) );
}
template<typename Func, typename ... Args>
void for_each( std::tuple<Args...>&& tup, Func&& f)
{
for_each_helper(std::forward<Func>(f),
typename make_indexes<Args...>::type(),
std::forward<std::tuple<Args...>>(tup) );
}
boost::fusion is another option, but it requires its own tuple type: boost::fusion::tuple. Lets better stick to the standard! Here is a test:
#include <iostream>
// ---------- FUNCTOR ----------
struct Functor
{
template<typename T>
void operator()(T& t) const { std::cout << t << std::endl; }
};
int main()
{
for_each( std::make_tuple(2, 0.6, 'c'), Functor() );
return 0;
}
the power of variadic templates!
In MSVC STL there's a _For_each_tuple_element function (not documented):
#include <tuple>
// ...
std::tuple<int, char, float> values{};
std::_For_each_tuple_element(values, [](auto&& value)
{
// process 'value'
});
Another option would be to implement iterators for tuples. This has the advantage that you can use a variety of algorithms provided by the standard library and range-based for loops. An elegant approach to this is explained here https://foonathan.net/2017/03/tuple-iterator/. The basic idea is to turn tuples into a range with begin() and end() methods to provide iterators. The iterator itself returns a std::variant<...> which can then be visited using std::visit.
Here some examples:
auto t = std::tuple{ 1, 2.f, 3.0 };
auto r = to_range(t);
for(auto v : r)
{
std::visit(unwrap([](auto& x)
{
x = 1;
}), v);
}
std::for_each(begin(r), end(r), [](auto v)
{
std::visit(unwrap([](auto& x)
{
x = 0;
}), v);
});
std::accumulate(begin(r), end(r), 0.0, [](auto acc, auto v)
{
return acc + std::visit(unwrap([](auto& x)
{
return static_cast<double>(x);
}), v);
});
std::for_each(begin(r), end(r), [](auto v)
{
std::visit(unwrap([](const auto& x)
{
std::cout << x << std::endl;
}), v);
});
std::for_each(begin(r), end(r), [](auto v)
{
std::visit(overload(
[](int x) { std::cout << "int" << std::endl; },
[](float x) { std::cout << "float" << std::endl; },
[](double x) { std::cout << "double" << std::endl; }), v);
});
My implementation (which is heavily based on the explanations in the link above):
#ifndef TUPLE_RANGE_H
#define TUPLE_RANGE_H
#include <utility>
#include <functional>
#include <variant>
#include <type_traits>
template<typename Accessor>
class tuple_iterator
{
public:
tuple_iterator(Accessor acc, const int idx)
: acc_(acc), index_(idx)
{
}
tuple_iterator operator++()
{
++index_;
return *this;
}
template<typename T>
bool operator ==(tuple_iterator<T> other)
{
return index_ == other.index();
}
template<typename T>
bool operator !=(tuple_iterator<T> other)
{
return index_ != other.index();
}
auto operator*() { return std::invoke(acc_, index_); }
[[nodiscard]] int index() const { return index_; }
private:
const Accessor acc_;
int index_;
};
template<bool IsConst, typename...Ts>
struct tuple_access
{
using tuple_type = std::tuple<Ts...>;
using tuple_ref = std::conditional_t<IsConst, const tuple_type&, tuple_type&>;
template<typename T>
using element_ref = std::conditional_t<IsConst,
std::reference_wrapper<const T>,
std::reference_wrapper<T>>;
using variant_type = std::variant<element_ref<Ts>...>;
using function_type = variant_type(*)(tuple_ref);
using table_type = std::array<function_type, sizeof...(Ts)>;
private:
template<size_t Index>
static constexpr function_type create_accessor()
{
return { [](tuple_ref t) -> variant_type
{
if constexpr (IsConst)
return std::cref(std::get<Index>(t));
else
return std::ref(std::get<Index>(t));
} };
}
template<size_t...Is>
static constexpr table_type create_table(std::index_sequence<Is...>)
{
return { create_accessor<Is>()... };
}
public:
static constexpr auto table = create_table(std::make_index_sequence<sizeof...(Ts)>{});
};
template<bool IsConst, typename...Ts>
class tuple_range
{
public:
using tuple_access_type = tuple_access<IsConst, Ts...>;
using tuple_ref = typename tuple_access_type::tuple_ref;
static constexpr auto tuple_size = sizeof...(Ts);
explicit tuple_range(tuple_ref tuple)
: tuple_(tuple)
{
}
[[nodiscard]] auto begin() const
{
return tuple_iterator{ create_accessor(), 0 };
}
[[nodiscard]] auto end() const
{
return tuple_iterator{ create_accessor(), tuple_size };
}
private:
tuple_ref tuple_;
auto create_accessor() const
{
return [this](int idx)
{
return std::invoke(tuple_access_type::table[idx], tuple_);
};
}
};
template<bool IsConst, typename...Ts>
auto begin(const tuple_range<IsConst, Ts...>& r)
{
return r.begin();
}
template<bool IsConst, typename...Ts>
auto end(const tuple_range<IsConst, Ts...>& r)
{
return r.end();
}
template <class ... Fs>
struct overload : Fs... {
explicit overload(Fs&&... fs) : Fs{ fs }... {}
using Fs::operator()...;
template<class T>
auto operator()(std::reference_wrapper<T> ref)
{
return (*this)(ref.get());
}
template<class T>
auto operator()(std::reference_wrapper<const T> ref)
{
return (*this)(ref.get());
}
};
template <class F>
struct unwrap : overload<F>
{
explicit unwrap(F&& f) : overload<F>{ std::forward<F>(f) } {}
using overload<F>::operator();
};
template<typename...Ts>
auto to_range(std::tuple<Ts...>& t)
{
return tuple_range<false, Ts...>{t};
}
template<typename...Ts>
auto to_range(const std::tuple<Ts...>& t)
{
return tuple_range<true, Ts...>{t};
}
#endif
Read-only access is also supported by passing a const std::tuple<>& to to_range().
Others have mentioned some well-designed third-party libraries that you may turn to. However, if you are using C++ without those third-party libraries, the following code may help.
namespace detail {
template <class Tuple, std::size_t I, class = void>
struct for_each_in_tuple_helper {
template <class UnaryFunction>
static void apply(Tuple&& tp, UnaryFunction& f) {
f(std::get<I>(std::forward<Tuple>(tp)));
for_each_in_tuple_helper<Tuple, I + 1u>::apply(std::forward<Tuple>(tp), f);
}
};
template <class Tuple, std::size_t I>
struct for_each_in_tuple_helper<Tuple, I, typename std::enable_if<
I == std::tuple_size<typename std::decay<Tuple>::type>::value>::type> {
template <class UnaryFunction>
static void apply(Tuple&&, UnaryFunction&) {}
};
} // namespace detail
template <class Tuple, class UnaryFunction>
UnaryFunction for_each_in_tuple(Tuple&& tp, UnaryFunction f) {
detail::for_each_in_tuple_helper<Tuple, 0u>
::apply(std::forward<Tuple>(tp), f);
return std::move(f);
}
Note: The code compiles with any compiler supporing C++11, and it keeps consistency with design of the standard library:
The tuple need not be std::tuple, and instead may be anything that supports std::get and std::tuple_size; in particular, std::array and std::pair may be used;
The tuple may be a reference type or cv-qualified;
It has similar behavior as std::for_each, and returns the input UnaryFunction;
For C++14 (or laster version) users, typename std::enable_if<T>::type and typename std::decay<T>::type could be replaced with their simplified version, std::enable_if_t<T> and std::decay_t<T>;
For C++17 (or laster version) users, std::tuple_size<T>::value could be replaced with its simplified version, std::tuple_size_v<T>.
For C++20 (or laster version) users, the SFINAE feature could be implemented with the Concepts.
Using constexpr and if constexpr(C++17) this is fairly simple and straight forward:
template <std::size_t I = 0, typename ... Ts>
void print(std::tuple<Ts...> tup) {
if constexpr (I == sizeof...(Ts)) {
return;
} else {
std::cout << std::get<I>(tup) << ' ';
print<I+1>(tup);
}
}
I might have missed this train, but this will be here for future reference.
Here's my construct based on this answer and on this gist:
#include <tuple>
#include <utility>
template<std::size_t N>
struct tuple_functor
{
template<typename T, typename F>
static void run(std::size_t i, T&& t, F&& f)
{
const std::size_t I = (N - 1);
switch(i)
{
case I:
std::forward<F>(f)(std::get<I>(std::forward<T>(t)));
break;
default:
tuple_functor<I>::run(i, std::forward<T>(t), std::forward<F>(f));
}
}
};
template<>
struct tuple_functor<0>
{
template<typename T, typename F>
static void run(std::size_t, T, F){}
};
You then use it as follow:
template<typename... T>
void logger(std::string format, T... args) //behaves like C#'s String.Format()
{
auto tp = std::forward_as_tuple(args...);
auto fc = [](const auto& t){std::cout << t;};
/* ... */
std::size_t some_index = ...
tuple_functor<sizeof...(T)>::run(some_index, tp, fc);
/* ... */
}
There could be room for improvements.
As per OP's code, it would become this:
const std::size_t num = sizeof...(T);
auto my_tuple = std::forward_as_tuple(t...);
auto do_sth = [](const auto& elem){/* ... */};
for(int i = 0; i < num; ++i)
tuple_functor<num>::run(i, my_tuple, do_sth);
Of all the answers I've seen here, here and here, I liked #sigidagi's way of iterating best. Unfortunately, his answer is very verbose which in my opinion obscures the inherent clarity.
This is my version of his solution which is more concise and works with std::tuple, std::pair and std::array.
template<typename UnaryFunction>
void invoke_with_arg(UnaryFunction)
{}
/**
* Invoke the unary function with each of the arguments in turn.
*/
template<typename UnaryFunction, typename Arg0, typename... Args>
void invoke_with_arg(UnaryFunction f, Arg0&& a0, Args&&... as)
{
f(std::forward<Arg0>(a0));
invoke_with_arg(std::move(f), std::forward<Args>(as)...);
}
template<typename Tuple, typename UnaryFunction, std::size_t... Indices>
void for_each_helper(Tuple&& t, UnaryFunction f, std::index_sequence<Indices...>)
{
using std::get;
invoke_with_arg(std::move(f), get<Indices>(std::forward<Tuple>(t))...);
}
/**
* Invoke the unary function for each of the elements of the tuple.
*/
template<typename Tuple, typename UnaryFunction>
void for_each(Tuple&& t, UnaryFunction f)
{
using size = std::tuple_size<typename std::remove_reference<Tuple>::type>;
for_each_helper(
std::forward<Tuple>(t),
std::move(f),
std::make_index_sequence<size::value>()
);
}
Demo: coliru
C++14's std::make_index_sequence can be implemented for C++11.
Expanding on #Stypox answer, we can make their solution more generic (C++17 onward). By adding a callable function argument:
template<size_t I = 0, typename... Tp, typename F>
void for_each_apply(std::tuple<Tp...>& t, F &&f) {
f(std::get<I>(t));
if constexpr(I+1 != sizeof...(Tp)) {
for_each_apply<I+1>(t, std::forward<F>(f));
}
}
Then, we need a strategy to visit each type.
Let start with some helpers (first two taken from cppreference):
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
template<class ... Ts> struct variant_ref { using type = std::variant<std::reference_wrapper<Ts>...>; };
variant_ref is used to allow tuples' state to be modified.
Usage:
std::tuple<Foo, Bar, Foo> tuples;
for_each_apply(tuples,
[](variant_ref<Foo, Bar>::type &&v) {
std::visit(overloaded {
[](Foo &arg) { arg.foo(); },
[](Bar const &arg) { arg.bar(); },
}, v);
});
Result:
Foo0
Bar
Foo0
Foo1
Bar
Foo1
For completeness, here are my Bar & Foo:
struct Foo {
void foo() {std::cout << "Foo" << i++ << std::endl;}
int i = 0;
};
struct Bar {
void bar() const {std::cout << "Bar" << std::endl;}
};
I have stumbled on the same problem for iterating over a tuple of function objects, so here is one more solution:
#include <tuple>
#include <iostream>
// Function objects
class A
{
public:
inline void operator()() const { std::cout << "A\n"; };
};
class B
{
public:
inline void operator()() const { std::cout << "B\n"; };
};
class C
{
public:
inline void operator()() const { std::cout << "C\n"; };
};
class D
{
public:
inline void operator()() const { std::cout << "D\n"; };
};
// Call iterator using recursion.
template<typename Fobjects, int N = 0>
struct call_functors
{
static void apply(Fobjects const& funcs)
{
std::get<N>(funcs)();
// Choose either the stopper or descend further,
// depending if N + 1 < size of the tuple.
using caller = std::conditional_t
<
N + 1 < std::tuple_size_v<Fobjects>,
call_functors<Fobjects, N + 1>,
call_functors<Fobjects, -1>
>;
caller::apply(funcs);
}
};
// Stopper.
template<typename Fobjects>
struct call_functors<Fobjects, -1>
{
static void apply(Fobjects const& funcs)
{
}
};
// Call dispatch function.
template<typename Fobjects>
void call(Fobjects const& funcs)
{
call_functors<Fobjects>::apply(funcs);
};
using namespace std;
int main()
{
using Tuple = tuple<A,B,C,D>;
Tuple functors = {A{}, B{}, C{}, D{}};
call(functors);
return 0;
}
Output:
A
B
C
D
There're many great answers, but for some reason most of them don't consider returning the results of applying f to our tuple...
or did I overlook it? Anyway, here's yet another way you can do that:
Doing Foreach with style (debatable)
auto t = std::make_tuple(1, "two", 3.f);
t | foreach([](auto v){ std::cout << v << " "; });
And returning from that:
auto t = std::make_tuple(1, "two", 3.f);
auto sizes = t | foreach([](auto v) {
return sizeof(v);
});
sizes | foreach([](auto v) {
std::cout << v;
});
Implementation (pretty simple one)
Edit: it gets a little messier.
I won't include some metaprogramming boilerplate here, for it will definitely make things less readable and besides, I believe those have already been answered somewhere on stackoverflow.
In case you're feeling lazy, feel free to peek into my github repo for implementation of both
#include <utility>
// Optional includes, if you don't want to implement it by hand or google it
// you can find it in the repo (link below)
#include "typesystem/typelist.hpp"
// used to check if all return types are void,
// making it a special case
// (and, alas, not using constexpr-if
// for the sake of being compatible with C++14...)
template <bool Cond, typename T, typename F>
using select = typename std::conditional<Cond, T, F>::type;
template <typename F>
struct elementwise_apply {
F f;
};
template <typename F>
constexpr auto foreach(F && f) -> elementwise_apply<F> { return {std::forward<F>(f)}; }
template <typename R>
struct tuple_map {
template <typename F, typename T, size_t... Is>
static constexpr decltype(auto) impl(std::index_sequence<Is...>, F && f, T&& tuple) {
return R{ std::forward<F>(f)( std::get<Is>(tuple) )... };
}
};
template<>
struct tuple_map<void> {
template <typename F, typename T, size_t... Is>
static constexpr void impl(std::index_sequence<Is...>, F && f, T&& tuple) {
[[maybe_unused]] std::initializer_list<int> _ {((void)std::forward<F>(f)( std::get<Is>(tuple) ), 0)... };
}
};
template <typename F, typename... Ts>
constexpr decltype(auto) operator| (std::tuple<Ts...> & t, fmap<F> && op) {
constexpr bool all_void = core::Types<decltype( std::move(op).f(std::declval<Ts&>()) )...>.all( core::is_void );
using R = meta::select<all_void, void, std::tuple<decltype(std::move(op).f(std::declval<Ts&>()))...>>;
return tuple_map<R>::impl(std::make_index_sequence<sizeof...(Ts)>{}, std::move(op).f, t);
}
template <typename F, typename... Ts>
constexpr decltype(auto) operator| (std::tuple<Ts...> const& t, fmap<F> && op) {
constexpr bool all_void = check if all "decltype( std::move(op).f(std::declval<Ts>()) )..." types are void, since then it's a special case
// e.g. core::Types<decltype( std::move(op).f(std::declval<Ts>()) )...>.all( core::is_void );
using R = meta::select<all_void, void, std::tuple<decltype(std::move(op).f(std::declval<Ts const&>()))...>>;
return tuple_map<R>::impl(std::make_index_sequence<sizeof...(Ts)>{}, std::move(op).f, t);
}
template <typename F, typename... Ts>
constexpr decltype(auto) operator| (std::tuple<Ts...> && t, fmap<F> && op) {
constexpr bool all_void = core::Types<decltype( std::move(op).f(std::declval<Ts&&>()) )...>.all( core::is_void );
using R = meta::select<all_void, void, std::tuple<decltype(std::move(op).f(std::declval<Ts&&>()))...>>;
return tuple_map<R>::impl(std::make_index_sequence<sizeof...(Ts)>{}, std::move(op).f, std::move(t));
}
Yeah, that would be much nicer if we were to use C++17
This is also an example of std::moving object's members, for which I'll better refer to this nice brief article
P.S. If you're stuck checking if all "decltype( std::move(op).f(std::declval()) )..." types are void
you can find some metaprogramming library, or, if those libraries seem too hard to grasp (which some of them may be due to some crazy metaprogramming tricks), you know where to look
template <typename F, typename T>
static constexpr size_t
foreach_in_tuple(std::tuple<T> & tuple, F && do_, size_t index_ = 0)
{
do_(tuple, index_);
return index_;
}
template <typename F, typename T, typename U, typename... Types>
static constexpr size_t
foreach_in_tuple(std::tuple<T,U,Types...> & tuple, F && do_, size_t index_ = 0)
{
if(!do_(tuple, index_))
return index_;
auto & next_tuple = reinterpret_cast<std::tuple<U,Types...> &>(tuple);
return foreach_in_tuple(next_tuple, std::forward<F>(do_), index_+1);
}
int main()
{
using namespace std;
auto tup = make_tuple(1, 2.3f, 'G', "hello");
foreach_in_tuple(tup, [](auto & tuple, size_t i)
{
auto & value = std::get<0>(tuple);
std::cout << i << " " << value << std::endl;
// if(i >= 2) return false; // break;
return true; // continue
});
}
Here is a solution based on std::interger_sequence.
As I don't know if my_tuple is constructed from std::make_tuple<T>(T &&...) in your code. It's essential for how to construct std::integer_sequence in the solution below.
(1) if your already have a my_tuple outside your function(not using template<typename ...T>), You can use
[](auto my_tuple)
{
[&my_tuple]<typename N, N... n>(std::integer_sequence<N, n...> int_seq)
{
((std::cout << std::get<n>(my_tuple) << '\n'), ...);
}(std::make_index_sequence<std::tuple_size_v<decltype(my_tuple)>>{});
}(std::make_tuple());
(2) if your havn't constructed my_tuple in your function and want to handle your T ...arguments
[]<typename ...T>(T... args)
{
[&args...]<typename N, N... n>(std::integer_sequence<N, n...> int_seq)
{
((std::cout << std::get<n>(std::forward_as_tuple(args...)) << '\n'), ...);
}(std::index_sequence_for<T...>{});
}();
boost's tuple provides helper functions get_head() and get_tail() so your helper functions may look like this:
inline void call_do_sth(const null_type&) {};
template <class H, class T>
inline void call_do_sth(cons<H, T>& x) { x.get_head().do_sth(); call_do_sth(x.get_tail()); }
as described in here http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html
with std::tuple it should be similar.
Actually, unfortunately std::tuple does not seem to provide such interface, so methods suggested before should work, or you would need to switch to boost::tuple which has other benefits (like io operators already provided). Though there is downside of boost::tuple with gcc - it does not accept variadic templates yet, but that may be already fixed as I do not have latest version of boost installed on my machine.