How do I expand a tuple into variadic template function's arguments?
Solution 1
In C++17 you can do this:
std::apply(the_function, the_tuple);
This already works in Clang++ 3.9, using std::experimental::apply.
Responding to the comment saying that this won't work if the_function
is templated, the following is a work-around:
#include <tuple>
template <typename T, typename U> void my_func(T &&t, U &&u) {}
int main(int argc, char *argv[argc]) {
std::tuple<int, float> my_tuple;
std::apply([](auto &&... args) { my_func(args...); }, my_tuple);
return 0;
}
This work around is a simplified solution to the general problem of passing overload sets and function template where a function would be expected. The general solution (one that is taking care of perfect-forwarding, constexpr-ness, and noexcept-ness) is presented here: https://blog.tartanllama.xyz/passing-overload-sets/.
Solution 2
Here's my code if anyone is interested
Basically at compile time the compiler will recursively unroll all arguments in various inclusive function calls <N> -> calls <N-1> -> calls ... -> calls <0> which is the last one and the compiler will optimize away the various intermediate function calls to only keep the last one which is the equivalent of func(arg1, arg2, arg3, ...)
Provided are 2 versions, one for a function called on an object and the other for a static function.
#include <tr1/tuple>
/**
* Object Function Tuple Argument Unpacking
*
* This recursive template unpacks the tuple parameters into
* variadic template arguments until we reach the count of 0 where the function
* is called with the correct parameters
*
* @tparam N Number of tuple arguments to unroll
*
* @ingroup g_util_tuple
*/
template < uint N >
struct apply_obj_func
{
template < typename T, typename... ArgsF, typename... ArgsT, typename... Args >
static void applyTuple( T* pObj,
void (T::*f)( ArgsF... ),
const std::tr1::tuple<ArgsT...>& t,
Args... args )
{
apply_obj_func<N-1>::applyTuple( pObj, f, t, std::tr1::get<N-1>( t ), args... );
}
};
//-----------------------------------------------------------------------------
/**
* Object Function Tuple Argument Unpacking End Point
*
* This recursive template unpacks the tuple parameters into
* variadic template arguments until we reach the count of 0 where the function
* is called with the correct parameters
*
* @ingroup g_util_tuple
*/
template <>
struct apply_obj_func<0>
{
template < typename T, typename... ArgsF, typename... ArgsT, typename... Args >
static void applyTuple( T* pObj,
void (T::*f)( ArgsF... ),
const std::tr1::tuple<ArgsT...>& /* t */,
Args... args )
{
(pObj->*f)( args... );
}
};
//-----------------------------------------------------------------------------
/**
* Object Function Call Forwarding Using Tuple Pack Parameters
*/
// Actual apply function
template < typename T, typename... ArgsF, typename... ArgsT >
void applyTuple( T* pObj,
void (T::*f)( ArgsF... ),
std::tr1::tuple<ArgsT...> const& t )
{
apply_obj_func<sizeof...(ArgsT)>::applyTuple( pObj, f, t );
}
//-----------------------------------------------------------------------------
/**
* Static Function Tuple Argument Unpacking
*
* This recursive template unpacks the tuple parameters into
* variadic template arguments until we reach the count of 0 where the function
* is called with the correct parameters
*
* @tparam N Number of tuple arguments to unroll
*
* @ingroup g_util_tuple
*/
template < uint N >
struct apply_func
{
template < typename... ArgsF, typename... ArgsT, typename... Args >
static void applyTuple( void (*f)( ArgsF... ),
const std::tr1::tuple<ArgsT...>& t,
Args... args )
{
apply_func<N-1>::applyTuple( f, t, std::tr1::get<N-1>( t ), args... );
}
};
//-----------------------------------------------------------------------------
/**
* Static Function Tuple Argument Unpacking End Point
*
* This recursive template unpacks the tuple parameters into
* variadic template arguments until we reach the count of 0 where the function
* is called with the correct parameters
*
* @ingroup g_util_tuple
*/
template <>
struct apply_func<0>
{
template < typename... ArgsF, typename... ArgsT, typename... Args >
static void applyTuple( void (*f)( ArgsF... ),
const std::tr1::tuple<ArgsT...>& /* t */,
Args... args )
{
f( args... );
}
};
//-----------------------------------------------------------------------------
/**
* Static Function Call Forwarding Using Tuple Pack Parameters
*/
// Actual apply function
template < typename... ArgsF, typename... ArgsT >
void applyTuple( void (*f)(ArgsF...),
std::tr1::tuple<ArgsT...> const& t )
{
apply_func<sizeof...(ArgsT)>::applyTuple( f, t );
}
// ***************************************
// Usage
// ***************************************
template < typename T, typename... Args >
class Message : public IMessage
{
typedef void (T::*F)( Args... args );
public:
Message( const std::string& name,
T& obj,
F pFunc,
Args... args );
private:
virtual void doDispatch( );
T* pObj_;
F pFunc_;
std::tr1::tuple<Args...> args_;
};
//-----------------------------------------------------------------------------
template < typename T, typename... Args >
Message<T, Args...>::Message( const std::string& name,
T& obj,
F pFunc,
Args... args )
: IMessage( name ),
pObj_( &obj ),
pFunc_( pFunc ),
args_( std::forward<Args>(args)... )
{
}
//-----------------------------------------------------------------------------
template < typename T, typename... Args >
void Message<T, Args...>::doDispatch( )
{
try
{
applyTuple( pObj_, pFunc_, args_ );
}
catch ( std::exception& e )
{
}
}
Solution 3
In C++ there is many ways of expanding/unpacking tuple and apply those tuple elements to a variadic template function. Here is a small helper class which creates index array. It is used a lot in template metaprogramming:
// ------------- 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...>
{};
Now the code which does the job is not that big:
// ----------UNPACK TUPLE AND APPLY TO FUNCTION ---------
#include <tuple>
#include <iostream>
using namespace std;
template<class Ret, class... Args, int... Indexes >
Ret apply_helper( Ret (*pf)(Args...), index_tuple< Indexes... >, tuple<Args...>&& tup)
{
return pf( forward<Args>( get<Indexes>(tup))... );
}
template<class Ret, class ... Args>
Ret apply(Ret (*pf)(Args...), const tuple<Args...>& tup)
{
return apply_helper(pf, typename make_indexes<Args...>::type(), tuple<Args...>(tup));
}
template<class Ret, class ... Args>
Ret apply(Ret (*pf)(Args...), tuple<Args...>&& tup)
{
return apply_helper(pf, typename make_indexes<Args...>::type(), forward<tuple<Args...>>(tup));
}
Test is shown bellow:
// --------------------- TEST ------------------
void one(int i, double d)
{
std::cout << "function one(" << i << ", " << d << ");\n";
}
int two(int i)
{
std::cout << "function two(" << i << ");\n";
return i;
}
int main()
{
std::tuple<int, double> tup(23, 4.5);
apply(one, tup);
int d = apply(two, std::make_tuple(2));
return 0;
}
I'm not big expert in other languages, but I guess that if these languages do not have such functionality in their menu, there is no way to do that. At least with C++ you can, and I think it is not so much complicated...
Solution 4
I find this to be the most elegant solution (and it is optimally forwarded):
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
template<size_t N>
struct Apply {
template<typename F, typename T, typename... A>
static inline auto apply(F && f, T && t, A &&... a)
-> decltype(Apply<N-1>::apply(
::std::forward<F>(f), ::std::forward<T>(t),
::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
))
{
return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t),
::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
);
}
};
template<>
struct Apply<0> {
template<typename F, typename T, typename... A>
static inline auto apply(F && f, T &&, A &&... a)
-> decltype(::std::forward<F>(f)(::std::forward<A>(a)...))
{
return ::std::forward<F>(f)(::std::forward<A>(a)...);
}
};
template<typename F, typename T>
inline auto apply(F && f, T && t)
-> decltype(Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::apply(::std::forward<F>(f), ::std::forward<T>(t)))
{
return Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::apply(::std::forward<F>(f), ::std::forward<T>(t));
}
Example usage:
void foo(int i, bool b);
std::tuple<int, bool> t = make_tuple(20, false);
void m()
{
apply(&foo, t);
}
Unfortunately GCC (4.6 at least) fails to compile this with "sorry, unimplemented: mangling overload" (which simply means that the compiler doesn't yet fully implement the C++11 spec), and since it uses variadic templates, it wont work in MSVC, so it is more or less useless. However, once there is a compiler that supports the spec, it will be the best approach IMHO. (Note: it isn't that hard to modify this so that you can work around the deficiencies in GCC, or to implement it with Boost Preprocessor, but it ruins the elegance, so this is the version I am posting.)
GCC 4.7 now supports this code just fine.
Edit: Added forward around actual function call to support rvalue reference form *this in case you are using clang (or if anybody else actually gets around to adding it).
Edit: Added missing forward around the function object in the non-member apply function's body. Thanks to pheedbaq for pointing out that it was missing.
Edit: And here is the C++14 version just since it is so much nicer (doesn't actually compile yet):
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
template<size_t N>
struct Apply {
template<typename F, typename T, typename... A>
static inline auto apply(F && f, T && t, A &&... a) {
return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t),
::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
);
}
};
template<>
struct Apply<0> {
template<typename F, typename T, typename... A>
static inline auto apply(F && f, T &&, A &&... a) {
return ::std::forward<F>(f)(::std::forward<A>(a)...);
}
};
template<typename F, typename T>
inline auto apply(F && f, T && t) {
return Apply< ::std::tuple_size< ::std::decay_t<T>
>::value>::apply(::std::forward<F>(f), ::std::forward<T>(t));
}
Here is a version for member functions (not tested very much!):
using std::forward; // You can change this if you like unreadable code or care hugely about namespace pollution.
template<size_t N>
struct ApplyMember
{
template<typename C, typename F, typename T, typename... A>
static inline auto apply(C&& c, F&& f, T&& t, A&&... a) ->
decltype(ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...))
{
return ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...);
}
};
template<>
struct ApplyMember<0>
{
template<typename C, typename F, typename T, typename... A>
static inline auto apply(C&& c, F&& f, T&&, A&&... a) ->
decltype((forward<C>(c)->*forward<F>(f))(forward<A>(a)...))
{
return (forward<C>(c)->*forward<F>(f))(forward<A>(a)...);
}
};
// C is the class, F is the member function, T is the tuple.
template<typename C, typename F, typename T>
inline auto apply(C&& c, F&& f, T&& t) ->
decltype(ApplyMember<std::tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>(t)))
{
return ApplyMember<std::tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>(t));
}
// Example:
class MyClass
{
public:
void foo(int i, bool b);
};
MyClass mc;
std::tuple<int, bool> t = make_tuple(20, false);
void m()
{
apply(&mc, &MyClass::foo, t);
}
Solution 5
template<typename F, typename Tuple, std::size_t ... I>
auto apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}
template<typename F, typename Tuple>
auto apply(F&& f, Tuple&& t) {
using Indices = std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices());
}
This is adapted from the C++14 draft using index_sequence. I might propose to have apply in a future standard (TS).
Related videos on Youtube
Xeo
Game Programmer, Bookworm, Japanese Culture Fanatic, C++ Lover and Template Hacker. I'm usually found hanging out in the Lounge, where the cool kids are. Sanity is just a mask.
Updated on May 11, 2020Comments
-
Xeo about 4 years
Consider the case of a templated function with variadic template arguments:
template<typename Tret, typename... T> Tret func(const T&... t);
Now, I have a tuple
t
of values. How do I callfunc()
using the tuple values as arguments? I've read about thebind()
function object, withcall()
function, and also theapply()
function in different some now-obsolete documents. The GNU GCC 4.4 implementation seems to have acall()
function in thebind()
class, but there is very little documentation on the subject.Some people suggest hand-written recursive hacks, but the true value of variadic template arguments is to be able to use them in cases like above.
Does anyone have a solution to is, or hint on where to read about it?
-
Skeen over 10 yearsThe C++14 standard has a solution see; open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
-
Skeen over 10 yearsThe idea is to unpack the tuple in a single variadic blast, using
integer_sequence
, see en.cppreference.com/w/cpp/utility/integer_sequence -
Skeen over 10 yearsHaving an
integer_sequence S
, you simply call your function asfunc(std::get<S>(tuple)...)
, and let the compiler handle the rest. -
lewis about 5 yearsIf using C++17 or later, ignore this answer and see the one below using std::apply
-
-
Daniel Earwicker over 15 yearsI wonder why they even bother having separate notions of tuple and function argument pack. Maybe in a conforming compiler they are interchangeable but I haven't spotted an indication of that anywhere I've read about them.
-
user1685501 about 15 yearsBecause tuple<int, char, string> is necessary as a separate type; as is the ability to make a function that doesn't require make_type in the middle of every call.
-
user1685501 about 15 yearsAlso, the best place is not comp.lang.c++.moderated. Questions about C++1x are almost always better directed to comp.std.c++.
-
HighCommander4 almost 14 yearsIs it possible to adapt this to work in a case where the "function" in question is actually a constructor?
-
David almost 14 yearsCould you provide an example of what you want to do and we can go from there.
-
Goofy over 13 yearsThis solution proviedes only a compile time overhead and at the end it will be simplified to (pObj->*f)( arg0, arg,1, ... argN); right?
-
David over 13 yearsyes, the compiler will compress the multiple function calls into the final one as if you had written it yourself which is the beauty of all this meta programming stuff.
-
Xeo over 12 yearsThe question was the other way around. Not
Args...
->tuple
, buttuple
->Args...
. -
kfmfe04 over 11 years+1 out of the answers listed, yours was the closest I could get to working with arguments whose arguments are vectors... ...but I am still getting compile errors. ideone.com/xH5kBH If you compile this with -DDIRECT_CALL and run it, you will see what the output should be. I get a compile error otherwise (I think decltype is not smart enough to figure out my special case), with gcc 4.7.2.
-
DRayX over 11 yearsThe version of gcc on ideaone is to old for this to pass, it doesn't support the mangled decltype return type overloading. I have tested this code relatively thoroughly in gcc 4.7.2, and I haven't run into any problems. With gcc 4.8, you can use the new C++17 automatic return value feature to avoid all the nasty decltype trailing return types.
-
kfmfe04 over 11 years+1 I really like that automatic return value feature - thx for the heads up
-
DRayX over 11 yearsI know, in my book the lack of the automatic return value thing for single statement (non-lambda) functions was the biggest miss of C++11. That and shared_from_this.
-
Brett Rossier over 11 yearsOut of curiosity, in the non-member
apply
function, why isf
not wrapped with astd::forward
call, as it is in the return type? Is it not needed? -
DRayX about 11 yearsThat would be because I forgot it :) Fixed
-
DRayX almost 11 yearsOut of curiosity, I tried compiling this in GCC 4.8, and
foo('x', true)
compiled to the exact same assembly code asapply(foo, ::std::make_tuple('x', true))
with any level of optimization besides -O0. -
Ryan Haining almost 11 yearsall the
tr1
stuff can get taken out now with c++11 -
PeterSom almost 11 yearsWith C++14
integer_sequence
you even get an almost correct implementation ofapply()
in its example. see my answer below. -
tower120 almost 10 yearsThis is really short version, no doubts. But what about calling member function?
-
tower120 almost 10 yearsWhy you make object argument a const pointer? Not reference, not const reference, not just pointer? What if callable function will not
const
? -
Timmmm over 8 years@tower120: I added a version for member functions. It compiles but I haven't tested it. Should work though!
-
DrP3pp3r over 8 yearsOne thing I cannot quite grasp: Why does the functor/function object f in Apply<0> need to be forwarded for the call?
-
DRayX over 8 yearsThe functor gets forwarded for the call due the rvalue from *this spec. It allows you to have a different overload if *this is an rvalue. For example, a copy method could actually return the object itself if *this is an rvalue.
-
Zitrax almost 7 yearsAccording to the example code at std::apply it does not seem to work if
the_function
is templated. -
Zitrax almost 7 yearsWhen called as in the example on a non templated function this works, but OP's question specifically mentioned a variadic template. If I use that I get errors about
template argument deduction/substitution failed
. Just as the example for the C++17's std::apply which mentions that same error. Is this supposed to also work on variadic templates or am I missing something ? -
Zitrax almost 7 years"... and apply those tuple elements to a variadic template function". The test section only contains non template variadic functions though. If I add one like
template<class ... T> void three(T...) {}
and try to use apply on that it does not compile. -
CrepeGoat over 6 yearsany comments on the compile-time optimization of the first option would be appreciated, so I can make my answer more complete (and maybe learn something new).
-
Erbureth about 6 years@Zitrax You can specify the function's template arguments:
std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
-
lewis about 5 yearsIf using C++17 or later, ignore this answer and see the one below using std::apply
-
Elliott over 4 yearsThis is the simplest, most elegant solution. And it works wonders. Thanks so much, M. Alaggan!!!!!! +100 votes
-
alfC over 3 yearsUnfortunatelly, this technique doesn't work in nvcc (and probably other EDG-based compilers). It fails with
error: template parameter pack not at end of parameter list
, in the linetemplate<class Ret, class... Args, int... Indexes > Ret apply_helper( Ret (*pf)(Args...), index_tuple< Indexes... >, tuple<Args...>&& tup)
.