perfect forwarding failing for lvalues - c++

I have a utility function which iterates over a tuple, and for each element, calls a function with that element, and finally calls another function with the result of all the tuple elements.
To better illustrate:
There is a tuple of various types tuple<Foo<int>, Bar<double>, Baz<char>>
Each type Foo, Bar and Baz have an implicit interface ClassT::data() which returns a reference to some internal member T&.
You have a function with the signature void (int, double, char)
The utility iterates over the tuple members, extracting the reference to the internal members, and calls the function with the required parameters.
Issue: perfect forwarding
I have tried to implement the utility using so-called "universal references" and perfect forwarding, thereby negating the need for multiple lvalue/rvalue and const/non-const overloads.
Whilst the parameter to the function is of type
template<typename Tuple>
auto invoke(Tuple&& tuple)
I cannot bind lvalues to it. Why is this?
I have asked a similar question leading up to this here which has been solved; however since this is an unrelated issue, I believe this warrants a new question
Example on ideone: https://ideone.com/lO5JOB
#include <tuple>
#include <iostream>
// sequence
template<size_t...>
struct Sequence
{ };
template<size_t N, size_t... Seq>
struct GenerateSequence : GenerateSequence<N - 1, N - 1, Seq...>
{ };
template<size_t... Seq>
struct GenerateSequence<0, Seq...>
{
using type = Sequence<Seq...>;
};
// invoke tuple
struct TupleForEachInvoker
{
template<typename Func, typename ForEachFunc, typename Tuple, size_t... Seq>
static auto invoke(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple, Sequence<Seq...>)
-> decltype(func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...))
{
return func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...);
}
template<typename Func, typename ForEachFunc, typename... Args>
static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)
-> decltype(invoke(std::forward<Func>(func),
std::forward<ForEachFunc>(forEachFunc),
std::forward<std::tuple<Args...>>(args),
typename GenerateSequence<sizeof...(Args)>::type()))
{
return invoke(std::forward<Func>(func),
std::forward<ForEachFunc>(forEachFunc),
std::forward<std::tuple<Args...>>(args),
typename GenerateSequence<sizeof...(Args)>::type());
}
};
template<typename Func, typename ForEachFunc, typename Tuple>
inline auto invokeWithMemberFromAll(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple)
-> decltype(TupleForEachInvoker::apply(std::forward<Func>(func),
std::forward<ForEachFunc>(forEachFunc),
std::forward<Tuple>(tuple)))
{
return TupleForEachInvoker::apply(std::forward<Func>(func),
std::forward<ForEachFunc>(forEachFunc),
std::forward<Tuple>(tuple));
}
// exemplar
template<typename T>
struct Foo
{
T& data() { return _val; }
T _val;
};
struct Extract
{
template<typename T>
T& operator() (Foo<T>& f) { return f.data(); }
};
int main()
{
Foo<int> i { 5 };
Foo<double> d { 6. };
Foo<const char*> s { "hello world" };
auto cb = [](int& i, const double& d, const char* s)
{
std::cout << "i=" << i << ", d=" << d << ", s=" << s << std::endl;
i += 2;
};
// rvalue reference to tuple
invokeWithMemberFromAll(cb, Extract{}, std::tie(i, d, s));
std::cout << i.data() << std::endl;
// lvalue reference to tuple - fails
auto tuple = std::tie(i, d, s);
invokeWithMemberFromAll(cb, Extract{}, tuple);
std::cout << i.data() << std::endl;
}

template<typename Func, typename ForEachFunc, typename... Args>
static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)
the 3rd argument is an rvalue tuple, not a tuple of forwarding references, or a forwarding reference to a tuple.
Universal references (currently called "forwarding references" in the under development C++1z standard) usually require a deduced type. They do require that the type the && is applied to could be either a reference or a non-reference type. A std::tuple<?> is always a non-reference type, so && makes it an rvalue reference, not a forwarding reference, when placed like std::tuple<?>&&.
It appears you switch from Tuple&& to std::tuple<?>&& for no reason other than to count the number of Args.... If I'm right, then std::tuple_size<std::decay_t<Tuple>>{} equals sizeof...(Args).

The problem is with your apply function - it requires an rvalue of type std::tuple<Args...>:
template<typename Func, typename ForEachFunc, typename... Args>
static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)
Here's one possible fix using std::tuple_size instead of sizeof...(Args) from your code:
struct TupleForEachInvoker
{
template<typename Func, typename ForEachFunc, typename Tuple, size_t... Seq>
static auto invoke(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple, Sequence<Seq...>)
-> decltype(func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...))
{
return func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...);
}
template<typename Func, typename ForEachFunc, typename Tuple>
static auto apply(Func&& func, ForEachFunc&& forEachFunc, Tuple&& args)
-> decltype(invoke(std::forward<Func>(func),
std::forward<ForEachFunc>(forEachFunc),
std::forward<Tuple>(args),
typename GenerateSequence<std::tuple_size<std::decay_t<Tuple>>::value>::type()))
{
return invoke(std::forward<Func>(func),
std::forward<ForEachFunc>(forEachFunc),
std::forward<Tuple>(args),
typename GenerateSequence<std::tuple_size<std::decay_t<Tuple>>::value>::type());
}
};
template<typename Func, typename ForEachFunc, typename Tuple>
inline auto invokeWithMemberFromAll(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple)
-> decltype(TupleForEachInvoker::apply(std::forward<Func>(func),
std::forward<ForEachFunc>(forEachFunc),
std::forward<Tuple>(tuple)))
{
return TupleForEachInvoker::apply(std::forward<Func>(func),
std::forward<ForEachFunc>(forEachFunc),
std::forward<Tuple>(tuple));
}
Live Demo

Related

Deducing Multiple Parameter Packs

Background
I'm trying to write some template functions for a template-only unit test library, specifically for Qt.
Problem
In this library, I have a variadic template that receives a variable amount of objects and functors (Qt5 Signals actually), always paired next to each other, as in QObject, signal, etc... then desirably followed by a variable amount of signal arguments.
Desired Solution
// implementation.h
template <typename T, typename U, typename... Sargs, typename... Fargs>
void test_signal_daisy_chain(T* t, void(T::*t_signal)(Fargs...),
U* u, void(U::*u_signal)(Fargs...),
Sargs... sargs,
Fargs... fargs) {...}
// client.cpp
test_signal_daisy_chain(object, &Object::signal1,
object, &Object::signal2,
object, &Object::signal3,
1, 2, 3); // where the signals are defined as void(Object::*)(int, int, int)
Where Fargs... corresponds to both the parameters in t_signal and u_signal as well as the arguments to pass to this function for testing, and Sargs... corresponds to a variable amount of QObject and signal member functions (void(T::*)(Fargs...)) to emit for the express purpose of testing.
Unsurprisingly I get "no matching function" due to "template argument deduction/substitution failed", and my ClangCodeModel plugin warns me that 6 arguments were expected, where 8 were given.
Working (ugly) solution
// implementation.h
template <typename... Fargs>
struct wrapper
{
template <typename T, typename U, typename... Sargs>
void test_signal_daisy_chain(Fargs... fargs,
T* t, void(T::*t_signal)(Fargs...),
U* u, void(U::*u_signal)(Fargs...),
Sargs... sargs) {...}
// client.cpp
wrapper<int, int, int>::test_signal_daisy_chain(1, 2, 3,
object, &Object::signal1,
object, &Object::signal2,
object, &Object::signal3);
I'm not content with having to explicitly define the variable function arguments at both the beginning of the function call and in the wrapper template type parameters. In fact, I was initially surprised that the could not be deduced simply by the fact that they were to match the variable arguments of the functors. I'm open to using wrapper functions as opposed to wrapper classes, as I already have a detail namespace set up which I'm willing to get messy for in order to provide a clean and user-friendly API.
Note: signal arguments can be anywhere from primitives to user-defined types to POD structs to template classes, all of variable length.
Edit 1: c++11 is a hard requirement so you can leave >c++11 features in your answer as long as they have some c++11 workaround, i.e. auto... is easy to fix, auto myFunction = []() constexpr {...}; much less so. If using if constexpr instead of a recursive template <std::size_t> helper function saves space and provides for a more succinct, complete, and future-proof answer, then please opt for whichever standard you deem best.
The simplest approach is to pack the parameters into a tuple at the beginning, and pass the tuple to test_signal_daisy_chain_impl:
template < typename... Fargs,
typename T, typename... Sargs>
void test_signal_daisy_chain_impl(const std::tuple<Fargs...> & fargs,
T* t, void(T::*t_signal)(Fargs...),
Sargs &&... sargs)
{
// apply unpacks the tuple
std::apply([&](auto ...params)
{
(t->*t_signal)(params...);
}, fargs);
// Although packed into the tuple, the elements in
// the tuple were not removed from the parameter list,
// so we have to ignore a tail of the size of Fargs.
if constexpr (sizeof...(Sargs) > sizeof...(Fargs))
test_signal_daisy_chain_impl(fargs, std::forward<Sargs>(sargs)...);
}
// Get a tuple out of the last I parameters
template <std::size_t I, typename Ret, typename T, typename... Qargs>
Ret get_last_n(T && t, Qargs && ...qargs)
{
static_assert(I <= sizeof...(Qargs) + 1,
"Not enough parameters to pass to the signal function");
if constexpr(sizeof...(Qargs)+1 == I)
return {std::forward<T>(t), std::forward<Qargs>(qargs)...};
else
return get_last_n<I, Ret>(std::forward<Qargs>(qargs)...);
}
template <typename T, typename... Fargs,
typename... Qargs>
void test_signal_daisy_chain(T* t, void(T::*t_signal)(Fargs...),
Qargs&&... qargs)
{
static_assert((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0,
"Expecting even number of parameters for object-signal pairs");
if constexpr ((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0) {
auto fargs = get_last_n<sizeof...(Fargs), std::tuple<Fargs...>>(
std::forward<Qargs>(qargs)...);
test_signal_daisy_chain_impl(fargs, t, t_signal,
std::forward<Qargs>(qargs)...);
}
}
And the usage:
class Object {
public:
void print_vec(const std::vector<int> & vec)
{
for (auto elem: vec) std::cout << elem << ", ";
}
void signal1(const std::vector<int> & vec)
{
std::cout << "signal1(";
print_vec(vec);
std::cout << ")\n";
}
void signal2(const std::vector<int> & vec)
{
std::cout << "signal2(";
print_vec(vec);
std::cout << ")\n";
}
void signal_int1(int a, int b)
{ std::cout << "signal_int1(" << a << ", " << b << ")\n"; }
void signal_int2(int a, int b)
{ std::cout << "signal_int2(" << a << ", " << b << ")\n"; }
void signal_int3(int a, int b)
{ std::cout << "signal_int3(" << a << ", " << b << ")\n"; }
};
int main()
{
Object object;
test_signal_daisy_chain(&object, &Object::signal1,
&object, &Object::signal2 ,
std::vector{1,2,3});
test_signal_daisy_chain(&object, &Object::signal_int1,
&object, &Object::signal_int2 ,
&object, &Object::signal_int3,
1,2);
}
Edit 1
Since C++11 is a hard constraint, there is a much uglier solution, based on the same principles. Things like std::apply and std::make_index_sequence have to be implemented. Overloading is used instead of if constexpr(....) :
template <std::size_t ...I>
struct indexes
{
using type = indexes;
};
template<std::size_t N, std::size_t ...I>
struct make_indexes
{
using type_aux = typename std::conditional<
(N == sizeof...(I)),
indexes<I...>,
make_indexes<N, I..., sizeof...(I)>>::type;
using type = typename type_aux::type;
};
template <typename Tuple, typename T, typename Method, std::size_t... I>
void apply_method_impl(
Method t_signal, T* t, const Tuple& tup, indexes<I...>)
{
return (t->*t_signal)(std::get<I>(tup)...);
}
template <typename Tuple, typename T, typename Method>
void apply_method(const Tuple & tup, T* t, Method t_signal)
{
apply_method_impl(
t_signal, t, tup,
typename make_indexes<
std::tuple_size<Tuple>::value>::type{});
}
template < typename... Fargs, typename... Sargs>
typename std::enable_if<(sizeof...(Fargs) == sizeof...(Sargs)), void>::type
test_signal_daisy_chain_impl(const std::tuple<Fargs...> & ,
Sargs &&...)
{}
template < typename... Fargs,
typename T, typename... Sargs>
void test_signal_daisy_chain_impl(const std::tuple<Fargs...> & fargs,
T* t, void(T::*t_signal)(Fargs...),
Sargs &&... sargs)
{
apply_method(fargs, t, t_signal);
// Although packed into the tuple, the elements in
// the tuple were not removed from the parameter list,
// so we have to ignore a tail of the size of Fargs.
test_signal_daisy_chain_impl(fargs, std::forward<Sargs>(sargs)...);
}
// Get a tuple out of the last I parameters
template <std::size_t I, typename Ret, typename T, typename... Qargs>
typename std::enable_if<sizeof...(Qargs)+1 == I, Ret>::type
get_last_n(T && t, Qargs && ...qargs)
{
return Ret{std::forward<T>(t), std::forward<Qargs>(qargs)...};
}
template <std::size_t I, typename Ret, typename T, typename... Qargs>
typename std::enable_if<sizeof...(Qargs)+1 != I, Ret>::type
get_last_n(T && , Qargs && ...qargs)
{
static_assert(I <= sizeof...(Qargs) + 1, "Not enough parameters to pass to the singal function");
return get_last_n<I, Ret>(std::forward<Qargs>(qargs)...);
}
template <typename T, typename... Fargs,
typename... Qargs>
void test_signal_daisy_chain(T* t, void(T::*t_signal)(Fargs...),
Qargs&&... qargs)
{
static_assert((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0,
"Expecting even number of parameters for object-signal pairs");
auto fargs = get_last_n<sizeof...(Fargs), std::tuple<Fargs...>>(
std::forward<Qargs>(qargs)...);
test_signal_daisy_chain_impl(fargs, t, t_signal,
std::forward<Qargs>(qargs)...);
}
Edit 2
It is possible to avoid runtime recursion by storing all parameters in a tuple. The following test_signal_daisy_chain_flat() does exactly that, while retaining the same interface as test_signal_daisy_chain():
template <typename Fargs, typename Pairs, std::size_t ...I>
void apply_pairs(Fargs && fargs, Pairs && pairs, const indexes<I...> &)
{
int dummy[] = {
(apply_method(std::forward<Fargs>(fargs),
std::get<I*2>(pairs),
std::get<I*2+1>(pairs)),
0)...
};
(void)dummy;
}
template <typename T, typename... Fargs,
typename... Qargs>
void test_signal_daisy_chain_flat(T* t, void(T::*t_signal)(Fargs...),
Qargs&&... qargs)
{
static_assert((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0,
"Expecting even number of parameters for object-signal pairs");
auto fargs = get_last_n<sizeof...(Fargs), std::tuple<Fargs...>>(
std::forward<Qargs>(qargs)...);
std::tuple<T*, void(T::*)(Fargs...), const Qargs&...> pairs{
t, t_signal, qargs...};
apply_pairs(fargs, pairs,
typename make_indexes<(sizeof...(Qargs) - sizeof...(Fargs))/2>
::type{});
}
Caveats:
Not asserting that parameter pairs match. The compiler simply fails to compile (possibly deep in recursion).
The types of the parameters passed to the function are deduced from the signature of the first function, regardless of the types of the trailing parameters - the trailing parameters are converted to the required types.
All functions are required to have the same signature.
template<class T>
struct tag_t { using type=T; };
template<class Tag>
using type_t = typename Tag::type;
template<class T>
using no_deduction = type_t<tag_t<T>>;
template <typename T, typename U, typename... Sargs, typename... Fargs>
void test_signal_daisy_chain(
T* t, void(T::*t_signal)(Sargs...),
U* u, void(U::*u_signal)(Fargs...),
no_deduction<Sargs>... sargs,
no_deduction<Fargs>... fargs)
I assume the Fargs... in t_signal was a typo, and was supposed to be Sargs.
If not, you are in trouble. There is no rule that "earlier deduction beats later deduction".
One thing you can do in c++14 is to have a function returning a function object:
template <typename T, typename U, typename... Fargs>
auto test_signal_daisy_chain(
T* t, void(T::*t_signal)(Fargs...),
U* u, void(U::*u_signal)(Fargs...),
no_deduction<Fargs>... fargs
) {
return [=](auto...sargs) {
// ...
};
}
Then use looks like:
A a; B b;
test_signal_daisy_chain( &a, &A::foo, &b, &B::bar, 1 )('a', 'b', 'c');
doing this in c++11 is possible with a manually written function object.

Invoke a function/functor with parameters more than arguments

I'd like to write a template function which can invoke a function with given parameters.
For instance, I can write a simple invoke function:
template<class F, class... Args>
inline auto invoke(F &&func, Args&&... args) -> decltype(auto)
{
return std::forward<F>(func)(std::forward<Args>(args)...);
}
This invoke accepts same count of parameter which f requires. However, I want to this template function allow additional unused parameters.
That is, I want to write some code like:
auto f = [] (auto a) {...};
invoke(f, 1, 2, 3);
Here, f accepts only one parameter so, I wish invoke ignore other parameters except the first one.
This can be accomplished very easily by get arity of the lambda unless the lambda is generic.
Since f here is generic lambda, as far as I know, there's no general way to figure out arity of f without explicit instantiation of its template operator()<...>.
How can I wirte my invoke?
One possibility:
#include <utility>
#include <cstddef>
#include <tuple>
template <std::size_t... Is, typename F, typename Tuple>
auto invoke_impl(int, std::index_sequence<Is...>, F&& func, Tuple&& args)
-> decltype(std::forward<F>(func)(std::get<Is>(std::forward<Tuple>(args))...))
{
return std::forward<F>(func)(std::get<Is>(std::forward<Tuple>(args))...);
}
template <std::size_t... Is, typename F, typename Tuple>
decltype(auto) invoke_impl(char, std::index_sequence<Is...>, F&& func, Tuple&& args)
{
return invoke_impl(0
, std::index_sequence<Is..., sizeof...(Is)>{}
, std::forward<F>(func)
, std::forward<Tuple>(args));
}
template <typename F, typename... Args>
decltype(auto) invoke(F&& func, Args&&... args)
{
return invoke_impl(0
, std::index_sequence<>{}
, std::forward<F>(func)
, std::forward_as_tuple(std::forward<Args>(args)...));
}
DEMO
Here's a code I've written for my needs based on Piotr's answer:
template <std::size_t... Is, typename F, typename Tuple>
void invoke_impl(
std::index_sequence<Is...>, F&& f, Tuple&& args,
std::enable_if_t<std::is_invocable_v<F, std::tuple_element_t<Is, Tuple>...>,
void>* = nullptr)
{
std::invoke(std::forward<F>(f), std::get<Is>(args)...);
}
template <std::size_t... Is, typename F, typename Tuple>
void invoke_impl(
std::index_sequence<Is...>, F&& f, Tuple&& args,
std::enable_if_t<
!std::is_invocable_v<F, std::tuple_element_t<Is, Tuple>...>, void>* =
nullptr)
{
static_assert(sizeof...(Is) > 0, "Not invocable with arguments supplied");
if constexpr (sizeof...(Is) > 0)
{
invoke_impl(std::make_index_sequence<sizeof...(Is) - 1>(),
std::forward<F>(f), std::move(args));
}
}
// invokes function with possibly too many parameters provided
template <typename F, typename... Args>
void invoke(F&& f, Args&&... args)
{
invoke_impl(std::make_index_sequence<sizeof...(Args)>(), std::forward<F>(f),
std::forward_as_tuple(std::forward<Args>(args)...));
}
It is a little bit improved:
it leverages std::invoke so it works with more use cases, like pionter to members
it matches parameters count starting from sizeof...(Args) so chooses better matched operator() overload
Here's a demo:
struct test
{
void foo()
{
std::cout << "foo" << std::endl;
}
void operator()()
{
std::cout << "operator()()" << std::endl;
}
void operator()(float)
{
std::cout << "operator()(float)" << std::endl;
}
};
int main()
{
test obj;
invoke(&test::foo, &obj, 1, 2, 3); // foo
invoke(obj); // operator()()
invoke(obj, 1, 2, 3); // operator()(float)
// invoke(5); // assert failed
}
Here's a live demo

Passing many functions and storing all their results in a tuple

Consider this output:
int foo (int, char) {std::cout << "foo\n"; return 0;}
double bar (bool, double, long ) {std::cout << "bar\n"; return 3.5;}
bool baz (char, short, float) {std::cout << "baz\n"; return true;}
int main() {
const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
multiFunction<2,3,3> (tuple, foo, bar, baz); // foo bar baz
}
So multiFunction<2,3,3> takes the first 2 elements of tuple and passes them to foo, the next 3 elements of tuple and passes them to bar, etc... I got this working (except when the functions have overloads, which is a separate problem). But the return values of each function called are lost. I want those return values stored somewhere, something like
std::tuple<int, double, bool> result = multiFunction<2,3,3> (tuple, foo, bar, baz);
But I don't know how to implement that. For those who want to help get this done, here is my (updated) working code so far, which stores the outputs into a stringstream only. Not easy to get all the values back, especially if the objects saved in the stream are complex classes.
#include <iostream>
#include <tuple>
#include <utility>
#include <sstream>
template <std::size_t N, typename Tuple>
struct TupleHead {
static auto get (const Tuple& tuple) { // The subtuple from the first N components of tuple.
return std::tuple_cat (TupleHead<N-1, Tuple>::get(tuple), std::make_tuple(std::get<N-1>(tuple)));
}
};
template <typename Tuple>
struct TupleHead<0, Tuple> {
static auto get (const Tuple&) { return std::tuple<>{}; }
};
template <std::size_t N, typename Tuple>
struct TupleTail {
static auto get (const Tuple& tuple) { // The subtuple from the last N components of tuple.
return std::tuple_cat (std::make_tuple(std::get<std::tuple_size<Tuple>::value - N>(tuple)), TupleTail<N-1, Tuple>::get(tuple));
}
};
template <typename Tuple>
struct TupleTail<0, Tuple> {
static auto get (const Tuple&) { return std::tuple<>{}; }
};
template <typename Tuple, typename F, std::size_t... Is>
auto functionOnTupleHelper (const Tuple& tuple, F f, const std::index_sequence<Is...>&) {
return f(std::get<Is>(tuple)...);
}
template <typename Tuple, typename F>
auto functionOnTuple (const Tuple& tuple, F f) {
return functionOnTupleHelper (tuple, f, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
}
template <typename Tuple, typename... Functions> struct MultiFunction;
template <typename Tuple, typename F, typename... Fs>
struct MultiFunction<Tuple, F, Fs...> {
template <std::size_t I, std::size_t... Is>
static inline auto execute (const Tuple& tuple, std::ostringstream& oss, const std::index_sequence<I, Is...>&, F f, Fs... fs) {
const auto headTuple = TupleHead<I, Tuple>::get(tuple);
const auto tailTuple = TupleTail<std::tuple_size<Tuple>::value - I, Tuple>::get(tuple);
// functionOnTuple (headTuple, f); // Always works, though return type is lost.
oss << std::boolalpha << functionOnTuple (headTuple, f) << '\n'; // What about return types that are void???
return MultiFunction<std::remove_const_t<decltype(tailTuple)>, Fs...>::execute (tailTuple, oss, std::index_sequence<Is...>{}, fs...);
}
};
template <>
struct MultiFunction<std::tuple<>> {
static auto execute (const std::tuple<>&, std::ostringstream& oss, std::index_sequence<>) { // End of recursion.
std::cout << std::boolalpha << oss.str();
// Convert 'oss' into the desired tuple? But how?
return std::tuple<int, double, bool>(); // This line is just to make the test compile.
}
};
template <std::size_t... Is, typename Tuple, typename... Fs>
auto multiFunction (const Tuple& tuple, Fs... fs) {
std::ostringstream oss;
return MultiFunction<Tuple, Fs...>::execute (tuple, oss, std::index_sequence<Is...>{}, fs...);
}
// Testing
template <typename T> int foo (int, char) {std::cout << "foo<T>\n"; return 0;}
double bar (bool, double, long ) {std::cout << "bar\n"; return 3.5;}
template <int...> bool baz (char, short, float) {std::cout << "baz<int...>\n"; return true;}
int main() {
const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
std::tuple<int, double, bool> result = multiFunction<2,3,3> (tuple, foo<bool>, bar, baz<2,5,1>); // foo<T> bar baz<int...>
}
Here's an approach where the number of arguments is deduced greedily:
#include <tuple>
namespace detail {
using namespace std;
template <size_t, size_t... Is, typename Arg>
constexpr auto call(index_sequence<Is...>, Arg&&) {return tuple<>{};}
template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
constexpr auto call(index_sequence<Is...>, ArgT&&, Fs&&...);
template <size_t offset, size_t... Is,
typename ArgT, typename F, typename... Fs,
typename=decltype(declval<F>()(get<offset+Is>(declval<ArgT>())...))>
constexpr auto call(index_sequence<Is...>, ArgT&& argt, F&& f, Fs&&... fs) {
return tuple_cat(make_tuple(f(get<offset+I>(forward<ArgT>(argt))...)),
call<offset+sizeof...(Is)>(index_sequence<>{},
forward<ArgT>(argt),
forward<Fs>(fs)...));}
template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
constexpr auto call(index_sequence<Is...>, ArgT&& argt, Fs&&... fs) {
return call<offset>(index_sequence<Is..., sizeof...(Is)>{},
forward<ArgT>(argt), forward<Fs>(fs)...);}
}
template <typename ArgT, typename... Fs>
constexpr auto multifunction(ArgT&& argt, Fs&&... fs) {
return detail::call<0>(std::index_sequence<>{},
std::forward<ArgT>(argt), std::forward<Fs>(fs)...);}
Demo. However, the above has quadratic time complexity in the number of return values, because tuple_cat is called recursively. Instead, we can use a slightly modified version of call to obtain the indices for each call - the actual tuple is then obtained directly:
#include <tuple>
namespace detail {
using namespace std;
template <size_t, size_t... Is, typename Arg>
constexpr auto indices(index_sequence<Is...>, Arg&&) {return tuple<>{};}
template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
constexpr auto indices(index_sequence<Is...>, ArgT&&, Fs&&...);
template <size_t offset, size_t... Is, typename ArgT, typename F, class... Fs,
typename=decltype(declval<F>()(get<offset+Is>(declval<ArgT>())...))>
constexpr auto indices(index_sequence<Is...>, ArgT&& argt, F&& f, Fs&&... fs){
return tuple_cat(make_tuple(index_sequence<offset+Is...>{}),
indices<offset+sizeof...(Is)>(index_sequence<>{},
forward<ArgT>(argt),
forward<Fs>(fs)...));}
template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
constexpr auto indices(index_sequence<Is...>, ArgT&& argt, Fs&&... fs) {
return indices<offset>(index_sequence<Is..., sizeof...(Is)>{},
forward<ArgT>(argt), forward<Fs>(fs)...);}
template <typename Arg, typename F, size_t... Is>
constexpr auto apply(Arg&& a, F&& f, index_sequence<Is...>) {
return f(get<Is>(a)...);}
template <typename ITuple, typename Args, size_t... Is, typename... Fs>
constexpr auto apply_all(Args&& args, index_sequence<Is...>, Fs&&... fs) {
return make_tuple(apply(forward<Args>(args), forward<Fs>(fs),
tuple_element_t<Is, ITuple>{})...);
}
}
template <typename ArgT, typename... Fs>
constexpr auto multifunction(ArgT&& argt, Fs&&... fs) {
return detail::apply_all<decltype(detail::indices<0>(std::index_sequence<>{},
std::forward<ArgT>(argt),
std::forward<Fs>(fs)...))>
(std::forward<ArgT>(argt), std::index_sequence_for<Fs...>{},
std::forward<Fs>(fs)...);}
Demo 2.
Building from the ground up and ignoring perfect forwarding so that I have to type less. We need a couple helpers. First, we need a partial version of apply that takes which indices from the tuple we want to apply to the function:
<class Tuple, class F, size_t... Is>
auto partial_apply(Tuple tuple, F f, std::index_sequence<Is...>) {
return f(get<Is>(tuple)...);
}
Then, we need to call that function for each subset of the tuple. Let's say we have all of our functions and indexes wrapped in a tuple already:
template <class Tuple, class FsTuple, class IsTuple, size_t... Is>
auto multi_apply(Tuple tuple, FsTuple fs, IsTuple indexes, std::index_sequence<Is...>) {
return std::make_tuple(
partial_apply(tuple,
std::get<Is>(fs),
std::get<Is>(indexes)
)...
);
}
So in this case, we'd want to end up calling multi_apply(tuple, <foo,bar,baz>, <<0,1>,<2,3,4>,<5,6,7>>, <0, 1, 2>).
All we need know is to build the indexes part. We're starting with <2,3,3>. We need to get the partial sums (<0,2,5>) and add that to the index sequences <<0,1>,<0,1,2>,<0,1,2>>. So we can write a partial sum function:
template <size_t I>
using size_t_ = std::integral_constant<size_t, I>;
template <class R, size_t N>
R partial_sum_(std::index_sequence<>, R, size_t_<N> ) {
return R{};
}
template <size_t I, size_t... Is, size_t... Js, size_t S>
auto partial_sum_(std::index_sequence<I, Is...>,
std::index_sequence<Js...>, size_t_<S> )
{
return partial_sum_(std::index_sequence<Is...>{},
std::index_sequence<Js..., S>{}, size_t_<S+I>{} );
}
template <size_t... Is>
auto partial_sum_(std::index_sequence<Is...> is)
{
return partial_sum_(is, std::index_sequence<>{}, size_t_<0>{} );
};
Which we can use to generate all of our indexes as a tuple:
template <size_t... Is, size_t N>
auto increment(std::index_sequence<Is...>, size_t_<N> )
{
return std::index_sequence<Is+N...>{};
}
template <class... Seqs, size_t... Ns>
auto make_all_indexes(std::index_sequence<Ns...>, Seqs... seqs)
{
return std::make_tuple(increment(seqs, size_t_<Ns>{})...);
}
Like so:
template <size_t... Is, class Tuple, class... Fs>
auto multiFunction(Tuple tuple, Fs... fs)
{
static_assert(sizeof...(Is) == sizeof...(Fs));
return multi_apply(tuple,
std::make_tuple(fs...),
make_all_indexes(
partial_sum_(std::index_sequence<Is...>{}),
std::make_index_sequence<Is>{}...
),
std::make_index_sequence<sizeof...(Is)>{}
);
}
If you want to handle void returns, then just make partial_apply return a tuple of a single element (or an empty tuple) and change the make_tuple() usage in multi_apply to tuple_cat().
Here's yet another impl:
template<std::size_t N>
constexpr Array<std::size_t, N> scan(std::size_t const (&a)[N])
{
Array<std::size_t, N> b{};
for (int i = 0; i != N - 1; ++i)
b[i + 1] = a[i] + b[i];
return b;
}
template<std::size_t O, std::size_t... N, class F, class Tuple>
inline decltype(auto) eval_from(std::index_sequence<N...>, F f, Tuple&& t)
{
return f(std::get<N + O>(std::forward<Tuple>(t))...);
}
template<std::size_t... O, std::size_t... N, class Tuple, class... F>
inline auto multi_function_impl1(std::index_sequence<O...>, std::index_sequence<N...>, Tuple&& t, F... f)
{
return pack(eval_from<O>(std::make_index_sequence<N>(), f, std::forward<Tuple>(t))...);
}
template<std::size_t... I, std::size_t... N, class Tuple, class... F>
inline auto multi_function_impl0(std::index_sequence<I...>, std::index_sequence<N...>, Tuple&& t, F... f)
{
constexpr std::size_t ns[] = {N...};
constexpr auto offsets = scan(ns);
return multi_function_impl1(std::index_sequence<offsets[I]...>(), std::index_sequence<N...>(), std::forward<Tuple>(t), f...);
}
template<std::size_t... N, class Tuple, class... F>
auto multi_function(Tuple&& t, F... f)
{
return multi_function_impl0(std::make_index_sequence<sizeof...(N)>(), std::index_sequence<N...>(), std::forward<Tuple>(t), f...);
}
where pack and Array are similar to std::make_tuple and std::array respectively, but to overcome some problems:
std::make_tuple decays it args, so references are lost
std::array cannot have its elems written in constexpr in c++14
DEMO
Here's my solution after following T.C.'s advice, adding to my previous (albeit inefficient) solution:
#include <iostream>
#include <tuple>
#include <utility>
struct NoReturnValue {
friend std::ostream& operator<< (std::ostream& os, const NoReturnValue&) {
return os << "[no value returned]";
}
};
template <std::size_t N, typename Tuple>
struct TupleHead {
static auto get (const Tuple& tuple) { // The subtuple from the first N components of tuple.
return std::tuple_cat (TupleHead<N-1, Tuple>::get(tuple), std::make_tuple(std::get<N-1>(tuple)));
}
};
template <typename Tuple>
struct TupleHead<0, Tuple> {
static auto get (const Tuple&) { return std::tuple<>{}; }
};
template <std::size_t N, typename Tuple>
struct TupleTail {
static auto get (const Tuple& tuple) { // The subtuple from the last N components of tuple.
return std::tuple_cat (std::make_tuple(std::get<std::tuple_size<Tuple>::value - N>(tuple)), TupleTail<N-1, Tuple>::get(tuple));
}
};
template <typename Tuple>
struct TupleTail<0, Tuple> {
static auto get (const Tuple&) { return std::tuple<>{}; }
};
template <typename Tuple, typename F, std::size_t... Is>
auto functionOnTupleHelper (const Tuple& tuple, F f, const std::index_sequence<Is...>&,
std::enable_if_t< !std::is_void<std::result_of_t<F(std::tuple_element_t<Is, Tuple>...)>>::value >* = nullptr) { // This overload is called only if f's return type is not void.
return std::make_tuple(f(std::get<Is>(tuple)...)); // Thanks to T.C.'s advice on returning a single tuple and then calling std::tuple_cat on all the single tuples.
}
template <typename Tuple, typename F, std::size_t... Is>
auto functionOnTupleHelper (const Tuple& tuple, F f, const std::index_sequence<Is...>&,
std::enable_if_t< std::is_void<std::result_of_t<F(std::tuple_element_t<Is, Tuple>...)>>::value >* = nullptr) { // This overload is called only if f's return type is void.
f(std::get<Is>(tuple)...);
return std::tuple<NoReturnValue>(); // Thanks to T.C.'s advice on returning std::tuple<NoReturnValue>() if the return type of 'f' is void.
}
template <typename Tuple, typename F>
auto functionOnTuple (const Tuple& tuple, F f) {
return functionOnTupleHelper (tuple, f, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
}
template <typename Tuple, typename... Functions> struct MultiFunction;
template <typename Tuple, typename F, typename... Fs>
struct MultiFunction<Tuple, F, Fs...> {
template <std::size_t I, std::size_t... Is>
static inline auto execute (const Tuple& tuple, const std::index_sequence<I, Is...>&, F f, Fs... fs) {
const auto headTuple = TupleHead<I, Tuple>::get(tuple);
const auto tailTuple = TupleTail<std::tuple_size<Tuple>::value - I, Tuple>::get(tuple);
const auto r = functionOnTuple(headTuple, f); // Which overload of 'functionOnTupleHelper' is called dedends on whether f's return type is void or not.
return std::tuple_cat (r, MultiFunction<std::remove_const_t<decltype(tailTuple)>, Fs...>::execute (tailTuple, std::index_sequence<Is...>{}, fs...)); // T.C.'s idea of tuple_cat with all the single return tuples.
}
};
template <>
struct MultiFunction<std::tuple<>> {
static auto execute (const std::tuple<>&, std::index_sequence<>) { return std::tuple<>(); }
};
template <std::size_t... Is, typename Tuple, typename... Fs>
auto multiFunction (const Tuple& tuple, Fs... fs) {
return MultiFunction<Tuple, Fs...>::execute (tuple, std::index_sequence<Is...>{}, fs...);
}
// Testing
template <typename T> int foo (int, char) {std::cout << "foo<T>\n"; return 0;}
double bar (bool, double, long) {std::cout << "bar\n"; return 3.5;}
double bar (bool, int) {return 1.4;}
void voidFunction() {std::cout << "voidFunction\n";}
template <int...> bool baz (char, short, float) {std::cout << "baz<int...>\n"; return true;}
int main() {
const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
const auto firstBar = [](bool b, double d, long l) {return bar(b, d, l);};
const auto t = multiFunction<2,3,0,3> (tuple, foo<bool>, firstBar, voidFunction, baz<2,5,1>); // Note that since 'bar' has an overload, we have to define 'firstBar' to indicate which 'bar' function we want to use.
std::cout << std::boolalpha << std::get<0>(t) << ' ' << std::get<1>(t) << ' ' << std::get<2>(t) << ' ' << std::get<3>(t) << '\n';
// 0 3.5 [no value returned] true
}
This solution should have linear time complexity. It uses std::tie instead of std::make_tuple, so neither the functions nor the arguments are copied unnecessarily. I think it should be fairly easy to follow compared to some other answers here.
First, we need a utility to invoke a function using a std::tuple of arguments.
template <typename F, typename Args, std::size_t... Is>
auto invoke_impl(F const& f, Args const& args, std::index_sequence<Is...>)
{
return f(std::get<Is>(args)...);
}
template <typename F, typename Args>
auto invoke(F const& f, Args const& args)
{
return invoke_impl(f, args, std::make_index_sequence<std::tuple_size<Args>::value>());
}
Secondly, we need a utility to std::tie a sub-range of tuple elements.
template <std::size_t Offset, typename Tuple, std::size_t... Is>
auto sub_tie_impl(Tuple const& tuple, std::index_sequence<Is...>)
{
return std::tie(std::get<Offset + Is>(tuple)...);
}
template <std::size_t Offset, std::size_t Count, typename Tuple>
auto sub_tie(Tuple const& tuple)
{
return sub_tie_impl<Offset>(tuple, std::make_index_sequence<Count>());
}
Now we can create our utility to consume a std::tuple of arguments using a sequence of functions.
First we std::tie the functions into a tuple, then we split the argument list into a parameter pack of sub-argument lists, and finally we invoke a function for each sub-argument list, packing the results into a tuple which we then return.
template <typename Fs, std::size_t... Is, typename... SubArgs>
auto consume_impl(Fs const& fs, std::index_sequence<Is...>, SubArgs const&... sub_args)
{
return std::make_tuple(invoke(std::get<Is>(fs), sub_args)...);
}
template <std::size_t, typename Args, typename Fs, typename... SubArgs>
auto consume_impl(Args const&, Fs const& fs, SubArgs const&... sub_args)
{
return consume_impl(fs, std::make_index_sequence<sizeof...(SubArgs)>(), sub_args...);
}
template <std::size_t Offset, std::size_t Count, std::size_t... Counts,
typename Args, typename Fs, typename... SubArgs>
auto consume_impl(Args const& args, Fs const& fs, SubArgs const&... sub_args)
{
return consume_impl<Offset + Count, Counts...>(args, fs, sub_args...,
sub_tie<Offset, Count>(args));
}
template <std::size_t... Counts, typename Args, typename... Fs>
auto consume(Args const& args, Fs const&... fs)
{
return consume_impl<0, Counts...>(args, std::tie(fs...));
}
Here's another solution borrowing Barry's partial_apply idea but avoiding the use of his partial_sum function altogether. It is shorter as a result. I think this is linear in time complexity.
#include <iostream>
#include <tuple>
#include <utility>
template <std::size_t Offset, typename F, typename Tuple, std::size_t... Is>
auto partial_apply_impl (F f, const Tuple& tuple, const std::index_sequence<Is...>&) {
return f(std::get<Offset + Is>(tuple)...);
}
template <typename Off, typename F, typename Tuple> // Off must be of type OffsetIndexSequence<A,B> only.
auto partial_apply (F f, const Tuple& tuple) {
return partial_apply_impl<Off::value>(f, tuple, typename Off::sequence{});
}
template <std::size_t Offset, std::size_t Size>
struct OffsetIndexSequence : std::integral_constant<std::size_t, Offset> {
using sequence = std::make_index_sequence<Size>;
};
template <typename Output, std::size_t... Is> struct OffsetIndexSequenceBuilder;
template <template <typename...> class P, typename... Out, std::size_t Offset, std::size_t First, std::size_t... Rest>
struct OffsetIndexSequenceBuilder<P<Out...>, Offset, First, Rest...> :
OffsetIndexSequenceBuilder<P<Out..., OffsetIndexSequence<Offset, First>>, Offset + First, Rest...> {};
template <template <typename...> class P, typename... Out, std::size_t Offset>
struct OffsetIndexSequenceBuilder<P<Out...>, Offset> {
using type = P<Out...>;
};
template <std::size_t... Is>
using offset_index_sequences = typename OffsetIndexSequenceBuilder<std::tuple<>, 0, Is...>::type;
template <typename> struct MultiFunction;
template <template <typename...> class P, typename... Offs>
struct MultiFunction<P<Offs...>> {
template <typename ArgsTuple, typename... Fs>
static auto execute (const ArgsTuple& argsTuple, Fs... fs) {
using ResultTuple = std::tuple<decltype(partial_apply<Offs>(fs, argsTuple))...>;
return ResultTuple{partial_apply<Offs>(fs, argsTuple)...};
}
};
template <std::size_t... Is, typename ArgsTuple, typename... Fs>
auto multiFunction (const ArgsTuple& argsTuple, Fs... fs) {
return MultiFunction<offset_index_sequences<Is...>>::execute(argsTuple, fs...);
}
// Testing
int foo (int, char) {std::cout << "foo\n"; return 0;}
double bar (bool, double, long) {std::cout << "bar\n"; return 3.5;}
bool baz (char, short, float) {std::cout << "baz\n"; return true;}
int main() {
const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
const std::tuple<int, double, bool> t = multiFunction<2,3,3> (tuple, foo, bar, baz); // foo bar baz
std::cout << std::boolalpha << std::get<0>(t) << ' ' << std::get<1>(t) << ' ' << std::get<2>(t) << '\n'; // 0 3.5 true
}

Expanding a tuple with perfect forwarding [duplicate]

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 call func() using the tuple values as arguments?
I've read about the bind() function object, with call() function, and also the apply() function in different some now-obsolete documents. The GNU GCC 4.4 implementation seems to have a call() function in the bind() 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?
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/.
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 )
{
}
}
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...
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);
}
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).
All this implementations are good. But due to use of pointer to member function compiler often cannot inline the target function call (at least gcc 4.8 can't, no matter what Why gcc can't inline function pointers that can be determined?)
But things changes if send pointer to member function as template arguments, not as function params:
/// from https://stackoverflow.com/a/9288547/1559666
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
template<typename TT>
using makeSeq = typename gens< std::tuple_size< typename std::decay<TT>::type >::value >::type;
// deduce function return type
template<class ...Args>
struct fn_type;
template<class ...Args>
struct fn_type< std::tuple<Args...> >{
// will not be called
template<class Self, class Fn>
static auto type_helper(Self &self, Fn f) -> decltype((self.*f)(declval<Args>()...)){
//return (self.*f)(Args()...);
return NULL;
}
};
template<class Self, class ...Args>
struct APPLY_TUPLE{};
template<class Self, class ...Args>
struct APPLY_TUPLE<Self, std::tuple<Args...>>{
Self &self;
APPLY_TUPLE(Self &self): self(self){}
template<class T, T (Self::* f)(Args...), class Tuple>
void delayed_call(Tuple &&list){
caller<T, f, Tuple >(forward<Tuple>(list), makeSeq<Tuple>() );
}
template<class T, T (Self::* f)(Args...), class Tuple, int ...S>
void caller(Tuple &&list, const seq<S...>){
(self.*f)( std::get<S>(forward<Tuple>(list))... );
}
};
#define type_of(val) typename decay<decltype(val)>::type
#define apply_tuple(obj, fname, tuple) \
APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< \
decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \
&decay<decltype(obj)>::type::fname \
> \
(tuple);
And ussage:
struct DelayedCall
{
void call_me(int a, int b, int c){
std::cout << a+b+c;
}
void fire(){
tuple<int,int,int> list = make_tuple(1,2,3);
apply_tuple(*this, call_me, list); // even simpler than previous implementations
}
};
Proof of inlinable http://goo.gl/5UqVnC
With small changes, we can "overload" apply_tuple:
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define VARARG_IMPL_(base, count, ...) base##count(__VA_ARGS__)
#define VARARG_IMPL(base, count, ...) VARARG_IMPL_(base, count, __VA_ARGS__)
#define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__)
#define apply_tuple2(fname, tuple) apply_tuple3(*this, fname, tuple)
#define apply_tuple3(obj, fname, tuple) \
APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< \
decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \
&decay<decltype(obj)>::type::fname \
/* ,decltype(tuple) */> \
(tuple);
#define apply_tuple(...) VARARG(apply_tuple, __VA_ARGS__)
...
apply_tuple(obj, call_me, list);
apply_tuple(call_me, list); // call this->call_me(list....)
Plus this is the only one solution which works with templated functions.
1) if you have a readymade parameter_pack structure as function argument, you can just use std::tie like this:
template <class... Args>
void tie_func(std::tuple<Args...> t, Args&... args)
{
std::tie<Args...>(args...) = t;
}
int main()
{
std::tuple<int, double, std::string> t(2, 3.3, "abc");
int i;
double d;
std::string s;
tie_func(t, i, d, s);
std::cout << i << " " << d << " " << s << std::endl;
}
2) if you don't have a readymade parampack arg, you'll have to unwind the tuple like this
#include <tuple>
#include <functional>
#include <iostream>
template<int N>
struct apply_wrap {
template<typename R, typename... TupleArgs, typename... UnpackedArgs>
static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>& t, UnpackedArgs... args )
{
return apply_wrap<N-1>::applyTuple( f, t, std::get<N-1>( t ), args... );
}
};
template<>
struct apply_wrap<0>
{
template<typename R, typename... TupleArgs, typename... UnpackedArgs>
static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>&, UnpackedArgs... args )
{
return f( args... );
}
};
template<typename R, typename... TupleArgs>
R applyTuple( std::function<R(TupleArgs...)>& f, std::tuple<TupleArgs...> const& t )
{
return apply_wrap<sizeof...(TupleArgs)>::applyTuple( f, t );
}
int fac(int n)
{
int r=1;
for(int i=2; i<=n; ++i)
r *= i;
return r;
}
int main()
{
auto t = std::make_tuple(5);
auto f = std::function<decltype(fac)>(&fac);
cout << applyTuple(f, t);
}
The news does not look good.
Having read over the just-released draft standard, I'm not seeing a built-in solution to this, which does seem odd.
The best place to ask about such things (if you haven't already) is comp.lang.c++.moderated, because some folks involved in drafting the standard post there regularly.
If you check out this thread, someone has the same question (maybe it's you, in which case you're going to find this whole answer a little frustrating!), and a few butt-ugly implementations are suggested.
I just wondered if it would be simpler to make the function accept a tuple, as the conversion that way is easier. But this implies that all functions should accept tuples as arguments, for maximum flexibility, and so that just demonstrates the strangeness of not providing a built-in expansion of tuple to function argument pack.
Update: the link above doesn't work - try pasting this:
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661
How about this:
// Warning: NOT tested!
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
using std::declval;
using std::forward;
using std::get;
using std::integral_constant;
using std::size_t;
using std::tuple;
namespace detail
{
template < typename Func, typename ...T, typename ...Args >
auto explode_tuple( integral_constant<size_t, 0u>, tuple<T...> const &t,
Func &&f, Args &&...a )
-> decltype( forward<Func>(f)(declval<T const>()...) )
{ return forward<Func>( f )( forward<Args>(a)... ); }
template < size_t Index, typename Func, typename ...T, typename ...Args >
auto explode_tuple( integral_constant<size_t, Index>, tuple<T...> const&t,
Func &&f, Args &&...a )
-> decltype( forward<Func>(f)(declval<T const>()...) )
{
return explode_tuple( integral_constant<size_t, Index - 1u>{}, t,
forward<Func>(f), get<Index - 1u>(t), forward<Args>(a)... );
}
}
template < typename Func, typename ...T >
auto run_tuple( Func &&f, tuple<T...> const &t )
-> decltype( forward<Func>(f)(declval<T const>()...) )
{
return detail::explode_tuple( integral_constant<size_t, sizeof...(T)>{}, t,
forward<Func>(f) );
}
template < typename Tret, typename ...T >
Tret func_T( tuple<T...> const &t )
{ return run_tuple( &func<Tret, T...>, t ); }
The run_tuple function template takes the given tuple and pass its elements individually to the given function. It carries out its work by recursively calling its helper function templates explode_tuple. It's important that run_tuple passes the tuple's size to explode_tuple; that number acts as a counter for how many elements to extract.
If the tuple is empty, then run_tuple calls the first version of explode_tuple with the remote function as the only other argument. The remote function is called with no arguments and we're done. If the tuple is not empty, a higher number is passed to the second version of explode_tuple, along with the remote function. A recursive call to explode_tuple is made, with the same arguments, except the counter number is decreased by one and (a reference to) the last tuple element is tacked on as an argument after the remote function. In a recursive call, either the counter isn't zero, and another call is made with the counter decreased again and the next-unreferenced element is inserted in the argument list after the remote function but before the other inserted arguments, or the counter reaches zero and the remote function is called with all the arguments accumulated after it.
I'm not sure I have the syntax of forcing a particular version of a function template right. I think you can use a pointer-to-function as a function object; the compiler will automatically fix it.
I am evaluating MSVS 2013RC, and it failed to compile some of the previous solutions proposed here in some cases. For example, MSVS will fail to compile "auto" returns if there are too many function parameters, because of a namespace imbrication limit (I sent that info to Microsoft to have it corrected). In other cases, we need access to the function's return, although that can also be done with a lamda: the following two examples give the same result..
apply_tuple([&ret1](double a){ret1 = cos(a); }, std::make_tuple<double>(.2));
ret2 = apply_tuple((double(*)(double))cos, std::make_tuple<double>(.2));
And thanks again to those who posted answers here before me, I wouldn't have gotten to this without it... so here it is:
template<size_t N>
struct apply_impl {
template<typename F, typename T, typename... A>
static inline auto apply_tuple(F&& f, T&& t, A&&... a)
-> decltype(apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
return apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
}
template<typename C, typename F, typename T, typename... A>
static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a)
-> decltype(apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
return apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
}
};
// This is a work-around for MSVS 2013RC that is required in some cases
#if _MSC_VER <= 1800 /* update this when bug is corrected */
template<>
struct apply_impl<6> {
template<typename F, typename T, typename... A>
static inline auto apply_tuple(F&& f, T&& t, A&&... a)
-> decltype(std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
return std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
}
template<typename C, typename F, typename T, typename... A>
static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a)
-> decltype((o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
return (o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
}
};
#endif
template<>
struct apply_impl<0> {
template<typename F, typename T, typename... A>
static inline auto apply_tuple(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 C, typename F, typename T, typename... A>
static inline auto apply_tuple(C*const o, F&& f, T&&, A&&... a)
-> decltype((o->*std::forward<F>(f))(std::forward<A>(a)...)) {
return (o->*std::forward<F>(f))(std::forward<A>(a)...);
}
};
// Apply tuple parameters on a non-member or static-member function by perfect forwarding
template<typename F, typename T>
inline auto apply_tuple(F&& f, T&& t)
-> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t))) {
return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t));
}
// Apply tuple parameters on a member function
template<typename C, typename F, typename T>
inline auto apply_tuple(C*const o, F&& f, T&& t)
-> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t))) {
return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t));
}
Extending on #David's solution, you can write a recursive template that
Doesn't use the (overly-verbose, imo) integer_sequence semantics
Doesn't use an extra temporary template parameter int N to count recursive iterations
(Optional for static/global functors) uses the functor as a template parameter for compile-time optimizaion
E.g.:
template <class F, F func>
struct static_functor {
template <class... T, class... Args_tmp>
static inline auto apply(const std::tuple<T...>& t, Args_tmp... args)
-> decltype(func(std::declval<T>()...)) {
return static_functor<F,func>::apply(t, args...,
std::get<sizeof...(Args_tmp)>(t));
}
template <class... T>
static inline auto apply(const std::tuple<T...>& t, T... args)
-> decltype(func(args...)) {
return func(args...);
}
};
static_functor<decltype(&myFunc), &myFunc>::apply(my_tuple);
Alternatively if your functor is not defined at compile-time (e.g., a non-constexpr functor instance, or a lambda expression), you can use it as a function parameter instead of a class template parameter, and in fact remove the containing class entirely:
template <class F, class... T, class... Args_tmp>
inline auto apply_functor(F&& func, const std::tuple<T...>& t,
Args_tmp... args) -> decltype(func(std::declval<T>()...)) {
return apply_functor(func, t, args..., std::get<sizeof...(Args_tmp)>(t));
}
template <class F, class... T>
inline auto apply_functor(F&& func, const std::tuple<T...>& t,
T... args) -> decltype(func(args...)) {
return func(args...);
}
apply_functor(&myFunc, my_tuple);
For pointer-to-member-function callables, you can adjust either of the above code pieces similarly as in #David's answer.
Explanation
In reference to the second piece of code, there are two template functions: the first one takes the functor func, the tuple t with types T..., and a parameter pack args of types Args_tmp.... When called, it recursively adds the objects from t to the parameter pack one at a time, from beginning (0) to end, and calls the function again with the new incremented parameter pack.
The second function's signature is almost identical to the first, except that it uses type T... for the parameter pack args. Thus, once args in the first function is completely filled with the values from t, it's type will be T... (in psuedo-code, typeid(T...) == typeid(Args_tmp...)), and thus the compiler will instead call the second overloaded function, which in turn calls func(args...).
The code in the static functor example works identically, with the functor instead used as a class template argument.
Why not just wrap your variadic arguments into a tuple class and then use compile time recursion (see link) to retrieve the index you are interested in. I find that unpacking variadic templates into a container or collection may not be type safe w.r.t. heterogeneous types
template<typename... Args>
auto get_args_as_tuple(Args... args) -> std::tuple<Args...>
{
return std::make_tuple(args);
}
This simple solution works for me:
template<typename... T>
void unwrap_tuple(std::tuple<T...>* tp)
{
std::cout << "And here I have the tuple types, all " << sizeof...(T) << " of them" << std::endl;
}
int main()
{
using TupleType = std::tuple<int, float, std::string, void*>;
unwrap_tuple((TupleType*)nullptr); // trick compiler into using template param deduction
}

How do I expand a tuple into variadic template function's arguments?

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 call func() using the tuple values as arguments?
I've read about the bind() function object, with call() function, and also the apply() function in different some now-obsolete documents. The GNU GCC 4.4 implementation seems to have a call() function in the bind() 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?
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/.
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 )
{
}
}
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...
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);
}
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).
All this implementations are good. But due to use of pointer to member function compiler often cannot inline the target function call (at least gcc 4.8 can't, no matter what Why gcc can't inline function pointers that can be determined?)
But things changes if send pointer to member function as template arguments, not as function params:
/// from https://stackoverflow.com/a/9288547/1559666
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
template<typename TT>
using makeSeq = typename gens< std::tuple_size< typename std::decay<TT>::type >::value >::type;
// deduce function return type
template<class ...Args>
struct fn_type;
template<class ...Args>
struct fn_type< std::tuple<Args...> >{
// will not be called
template<class Self, class Fn>
static auto type_helper(Self &self, Fn f) -> decltype((self.*f)(declval<Args>()...)){
//return (self.*f)(Args()...);
return NULL;
}
};
template<class Self, class ...Args>
struct APPLY_TUPLE{};
template<class Self, class ...Args>
struct APPLY_TUPLE<Self, std::tuple<Args...>>{
Self &self;
APPLY_TUPLE(Self &self): self(self){}
template<class T, T (Self::* f)(Args...), class Tuple>
void delayed_call(Tuple &&list){
caller<T, f, Tuple >(forward<Tuple>(list), makeSeq<Tuple>() );
}
template<class T, T (Self::* f)(Args...), class Tuple, int ...S>
void caller(Tuple &&list, const seq<S...>){
(self.*f)( std::get<S>(forward<Tuple>(list))... );
}
};
#define type_of(val) typename decay<decltype(val)>::type
#define apply_tuple(obj, fname, tuple) \
APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< \
decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \
&decay<decltype(obj)>::type::fname \
> \
(tuple);
And ussage:
struct DelayedCall
{
void call_me(int a, int b, int c){
std::cout << a+b+c;
}
void fire(){
tuple<int,int,int> list = make_tuple(1,2,3);
apply_tuple(*this, call_me, list); // even simpler than previous implementations
}
};
Proof of inlinable http://goo.gl/5UqVnC
With small changes, we can "overload" apply_tuple:
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define VARARG_IMPL_(base, count, ...) base##count(__VA_ARGS__)
#define VARARG_IMPL(base, count, ...) VARARG_IMPL_(base, count, __VA_ARGS__)
#define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__)
#define apply_tuple2(fname, tuple) apply_tuple3(*this, fname, tuple)
#define apply_tuple3(obj, fname, tuple) \
APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< \
decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \
&decay<decltype(obj)>::type::fname \
/* ,decltype(tuple) */> \
(tuple);
#define apply_tuple(...) VARARG(apply_tuple, __VA_ARGS__)
...
apply_tuple(obj, call_me, list);
apply_tuple(call_me, list); // call this->call_me(list....)
Plus this is the only one solution which works with templated functions.
1) if you have a readymade parameter_pack structure as function argument, you can just use std::tie like this:
template <class... Args>
void tie_func(std::tuple<Args...> t, Args&... args)
{
std::tie<Args...>(args...) = t;
}
int main()
{
std::tuple<int, double, std::string> t(2, 3.3, "abc");
int i;
double d;
std::string s;
tie_func(t, i, d, s);
std::cout << i << " " << d << " " << s << std::endl;
}
2) if you don't have a readymade parampack arg, you'll have to unwind the tuple like this
#include <tuple>
#include <functional>
#include <iostream>
template<int N>
struct apply_wrap {
template<typename R, typename... TupleArgs, typename... UnpackedArgs>
static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>& t, UnpackedArgs... args )
{
return apply_wrap<N-1>::applyTuple( f, t, std::get<N-1>( t ), args... );
}
};
template<>
struct apply_wrap<0>
{
template<typename R, typename... TupleArgs, typename... UnpackedArgs>
static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>&, UnpackedArgs... args )
{
return f( args... );
}
};
template<typename R, typename... TupleArgs>
R applyTuple( std::function<R(TupleArgs...)>& f, std::tuple<TupleArgs...> const& t )
{
return apply_wrap<sizeof...(TupleArgs)>::applyTuple( f, t );
}
int fac(int n)
{
int r=1;
for(int i=2; i<=n; ++i)
r *= i;
return r;
}
int main()
{
auto t = std::make_tuple(5);
auto f = std::function<decltype(fac)>(&fac);
cout << applyTuple(f, t);
}
The news does not look good.
Having read over the just-released draft standard, I'm not seeing a built-in solution to this, which does seem odd.
The best place to ask about such things (if you haven't already) is comp.lang.c++.moderated, because some folks involved in drafting the standard post there regularly.
If you check out this thread, someone has the same question (maybe it's you, in which case you're going to find this whole answer a little frustrating!), and a few butt-ugly implementations are suggested.
I just wondered if it would be simpler to make the function accept a tuple, as the conversion that way is easier. But this implies that all functions should accept tuples as arguments, for maximum flexibility, and so that just demonstrates the strangeness of not providing a built-in expansion of tuple to function argument pack.
Update: the link above doesn't work - try pasting this:
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661
How about this:
// Warning: NOT tested!
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
using std::declval;
using std::forward;
using std::get;
using std::integral_constant;
using std::size_t;
using std::tuple;
namespace detail
{
template < typename Func, typename ...T, typename ...Args >
auto explode_tuple( integral_constant<size_t, 0u>, tuple<T...> const &t,
Func &&f, Args &&...a )
-> decltype( forward<Func>(f)(declval<T const>()...) )
{ return forward<Func>( f )( forward<Args>(a)... ); }
template < size_t Index, typename Func, typename ...T, typename ...Args >
auto explode_tuple( integral_constant<size_t, Index>, tuple<T...> const&t,
Func &&f, Args &&...a )
-> decltype( forward<Func>(f)(declval<T const>()...) )
{
return explode_tuple( integral_constant<size_t, Index - 1u>{}, t,
forward<Func>(f), get<Index - 1u>(t), forward<Args>(a)... );
}
}
template < typename Func, typename ...T >
auto run_tuple( Func &&f, tuple<T...> const &t )
-> decltype( forward<Func>(f)(declval<T const>()...) )
{
return detail::explode_tuple( integral_constant<size_t, sizeof...(T)>{}, t,
forward<Func>(f) );
}
template < typename Tret, typename ...T >
Tret func_T( tuple<T...> const &t )
{ return run_tuple( &func<Tret, T...>, t ); }
The run_tuple function template takes the given tuple and pass its elements individually to the given function. It carries out its work by recursively calling its helper function templates explode_tuple. It's important that run_tuple passes the tuple's size to explode_tuple; that number acts as a counter for how many elements to extract.
If the tuple is empty, then run_tuple calls the first version of explode_tuple with the remote function as the only other argument. The remote function is called with no arguments and we're done. If the tuple is not empty, a higher number is passed to the second version of explode_tuple, along with the remote function. A recursive call to explode_tuple is made, with the same arguments, except the counter number is decreased by one and (a reference to) the last tuple element is tacked on as an argument after the remote function. In a recursive call, either the counter isn't zero, and another call is made with the counter decreased again and the next-unreferenced element is inserted in the argument list after the remote function but before the other inserted arguments, or the counter reaches zero and the remote function is called with all the arguments accumulated after it.
I'm not sure I have the syntax of forcing a particular version of a function template right. I think you can use a pointer-to-function as a function object; the compiler will automatically fix it.
I am evaluating MSVS 2013RC, and it failed to compile some of the previous solutions proposed here in some cases. For example, MSVS will fail to compile "auto" returns if there are too many function parameters, because of a namespace imbrication limit (I sent that info to Microsoft to have it corrected). In other cases, we need access to the function's return, although that can also be done with a lamda: the following two examples give the same result..
apply_tuple([&ret1](double a){ret1 = cos(a); }, std::make_tuple<double>(.2));
ret2 = apply_tuple((double(*)(double))cos, std::make_tuple<double>(.2));
And thanks again to those who posted answers here before me, I wouldn't have gotten to this without it... so here it is:
template<size_t N>
struct apply_impl {
template<typename F, typename T, typename... A>
static inline auto apply_tuple(F&& f, T&& t, A&&... a)
-> decltype(apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
return apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
}
template<typename C, typename F, typename T, typename... A>
static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a)
-> decltype(apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
return apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
}
};
// This is a work-around for MSVS 2013RC that is required in some cases
#if _MSC_VER <= 1800 /* update this when bug is corrected */
template<>
struct apply_impl<6> {
template<typename F, typename T, typename... A>
static inline auto apply_tuple(F&& f, T&& t, A&&... a)
-> decltype(std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
return std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
}
template<typename C, typename F, typename T, typename... A>
static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a)
-> decltype((o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
return (o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
}
};
#endif
template<>
struct apply_impl<0> {
template<typename F, typename T, typename... A>
static inline auto apply_tuple(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 C, typename F, typename T, typename... A>
static inline auto apply_tuple(C*const o, F&& f, T&&, A&&... a)
-> decltype((o->*std::forward<F>(f))(std::forward<A>(a)...)) {
return (o->*std::forward<F>(f))(std::forward<A>(a)...);
}
};
// Apply tuple parameters on a non-member or static-member function by perfect forwarding
template<typename F, typename T>
inline auto apply_tuple(F&& f, T&& t)
-> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t))) {
return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t));
}
// Apply tuple parameters on a member function
template<typename C, typename F, typename T>
inline auto apply_tuple(C*const o, F&& f, T&& t)
-> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t))) {
return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t));
}
Extending on #David's solution, you can write a recursive template that
Doesn't use the (overly-verbose, imo) integer_sequence semantics
Doesn't use an extra temporary template parameter int N to count recursive iterations
(Optional for static/global functors) uses the functor as a template parameter for compile-time optimizaion
E.g.:
template <class F, F func>
struct static_functor {
template <class... T, class... Args_tmp>
static inline auto apply(const std::tuple<T...>& t, Args_tmp... args)
-> decltype(func(std::declval<T>()...)) {
return static_functor<F,func>::apply(t, args...,
std::get<sizeof...(Args_tmp)>(t));
}
template <class... T>
static inline auto apply(const std::tuple<T...>& t, T... args)
-> decltype(func(args...)) {
return func(args...);
}
};
static_functor<decltype(&myFunc), &myFunc>::apply(my_tuple);
Alternatively if your functor is not defined at compile-time (e.g., a non-constexpr functor instance, or a lambda expression), you can use it as a function parameter instead of a class template parameter, and in fact remove the containing class entirely:
template <class F, class... T, class... Args_tmp>
inline auto apply_functor(F&& func, const std::tuple<T...>& t,
Args_tmp... args) -> decltype(func(std::declval<T>()...)) {
return apply_functor(func, t, args..., std::get<sizeof...(Args_tmp)>(t));
}
template <class F, class... T>
inline auto apply_functor(F&& func, const std::tuple<T...>& t,
T... args) -> decltype(func(args...)) {
return func(args...);
}
apply_functor(&myFunc, my_tuple);
For pointer-to-member-function callables, you can adjust either of the above code pieces similarly as in #David's answer.
Explanation
In reference to the second piece of code, there are two template functions: the first one takes the functor func, the tuple t with types T..., and a parameter pack args of types Args_tmp.... When called, it recursively adds the objects from t to the parameter pack one at a time, from beginning (0) to end, and calls the function again with the new incremented parameter pack.
The second function's signature is almost identical to the first, except that it uses type T... for the parameter pack args. Thus, once args in the first function is completely filled with the values from t, it's type will be T... (in psuedo-code, typeid(T...) == typeid(Args_tmp...)), and thus the compiler will instead call the second overloaded function, which in turn calls func(args...).
The code in the static functor example works identically, with the functor instead used as a class template argument.
Why not just wrap your variadic arguments into a tuple class and then use compile time recursion (see link) to retrieve the index you are interested in. I find that unpacking variadic templates into a container or collection may not be type safe w.r.t. heterogeneous types
template<typename... Args>
auto get_args_as_tuple(Args... args) -> std::tuple<Args...>
{
return std::make_tuple(args);
}
This simple solution works for me:
template<typename... T>
void unwrap_tuple(std::tuple<T...>* tp)
{
std::cout << "And here I have the tuple types, all " << sizeof...(T) << " of them" << std::endl;
}
int main()
{
using TupleType = std::tuple<int, float, std::string, void*>;
unwrap_tuple((TupleType*)nullptr); // trick compiler into using template param deduction
}