288

I'm trying to store in a std::tuple a varying number of values, which will later be used as arguments for a call to a function pointer which matches the stored types.

I've created a simplified example showing the problem I'm struggling to solve:

#include <iostream>
#include <tuple>

void f(int a, double b, void* c) {
  std::cout << a << ":" << b << ":" << c << std::endl;
}

template <typename ...Args>
struct save_it_for_later {
  std::tuple<Args...> params;
  void (*func)(Args...);

  void delayed_dispatch() {
     // How can I "unpack" params to call func?
     func(std::get<0>(params), std::get<1>(params), std::get<2>(params));
     // But I *really* don't want to write 20 versions of dispatch so I'd rather 
     // write something like:
     func(params...); // Not legal
  }
};

int main() {
  int a=666;
  double b = -1.234;
  void *c = NULL;

  save_it_for_later<int,double,void*> saved = {
                                 std::tuple<int,double,void*>(a,b,c), f};
  saved.delayed_dispatch();
}

Normally for problems involving std::tuple or variadic templates I'd write another template like template <typename Head, typename ...Tail> to recursively evaluate all of the types one by one, but I can't see a way of doing that for dispatching a function call.

The real motivation for this is somewhat more complex and it's mostly just a learning exercise anyway. You can assume that I'm handed the tuple by contract from another interface, so can't be changed but that the desire to unpack it into a function call is mine. This rules out using std::bind as a cheap way to sidestep the underlying problem.

What's a clean way of dispatching the call using the std::tuple, or an alternative better way of achieving the same net result of storing/forwarding some values and a function pointer until an arbitrary future point?

Flexo
  • 87,323
  • 22
  • 191
  • 272
  • 5
    Why can't you just use `auto saved = std::bind(f, a, b, c);` ... then later just call `saved()`? – Charles Salvia Mar 11 '15 at 17:11
  • Not always my interface to control. I receive a tuple by contract from someone else and want to do things with it subsequently. – Flexo Jan 17 '17 at 17:58

10 Answers10

291

You need to build a parameter pack of numbers and unpack them

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;
};


// ...
  void delayed_dispatch() {
     callFunc(typename gens<sizeof...(Args)>::type());
  }

  template<int ...S>
  void callFunc(seq<S...>) {
     func(std::get<S>(params) ...);
  }
// ...
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 5
    Wow, I did not know the unpacking operator could be used like that, this is nice! – Luc Touraille Oct 22 '11 at 13:09
  • 6
    Johannes, I realize its been 2+ years since you posted this, but the one thing I'm struggling with is the `struct gens` generic definition (the one that inherits from an *expanded* derivation of said same). I see it eventually hits the specialization with 0. If the mood suits you and you have the spare cycles, if you can expand on that, and how it is utilized for this, I would be eternally grateful. And I wish I could up-vote this a hundred times. I've had more fun playing with tangents from this code. Thanks. – WhozCraig Nov 18 '13 at 09:38
  • The `type` definition will not appear until the recursion is terminated when the parameter counts down to 0 as you say. It is at this point that all the numbers in the range [0, N-1] have entered the template specification which is the parameter pack used to create the sequence. – jeremyong Jan 14 '14 at 05:58
  • 23
    @WhozCraig: What it does is generate a type `seq<0, 1, .., N-1>`. How it works: `gens<5>: gens<4, 4>: gens<3, 3, 4>: gens<2, 2, 3, 4> : gens<1, 1, 2, 3, 4> : gens<0, 0, 1, 2, 3, 4>`. The last type is specialized, creating `seq<0, 1, 2, 3, 4>`. Pretty clever trick. – mindvirus Apr 25 '14 at 15:38
  • 1
    Excellent. I 've used it in DLL calls. http://www.codeproject.com/Articles/845521/Call-a-Win-function-in-a-DLL-in-an-one-line-templa – Michael Chourdakis Nov 23 '14 at 16:26
  • Can this be done without inheritance? I started trying to write the same thing without inheritance simply because I tend to prefer it, and I now seem to be stuck, and curious. – Nir Friedman Mar 24 '15 at 02:57
  • 2
    @NirFriedman: Sure, just replace the unspecialized version of `gens` by: `template struct gens { typedef typename gens::type type; };` – marton78 Apr 16 '15 at 14:11
  • What purpose does `typename` serve in `callFunc(typename gens::type());`? – DigitalEye Apr 14 '16 at 00:02
  • 2
    @DigitalEye https://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords – Johannes Schaub - litb Apr 14 '16 at 10:51
  • 11
    It's worth echoing Walter's answer and comments thereon: folk don't need to invent their own wheels anymore. Generating a sequence was so common that it was standardised in C++14 as `std::integer_sequence` and the specialisation thereof for `std::size_t`, `std::index_sequence` - plus their associated helper functions `std::make_in(teger|dex)_sequence<>()` and `std::index_sequence_for()`. And in C++17 there are a lot of other good things integrated into the library - particularly including `std::apply` and `std::make_from_tuple`, which would handle the unpacking and calling bits – underscore_d Oct 09 '16 at 14:22
  • This does not compile on MSVC2013 for me. 2015, GCC and Clang are fine with it. Any ideas? It claims "could not deduce template argument for 'const std::function &' from 'const std::function'" on the function itself. – dascandy Nov 28 '16 at 08:48
114

The C++17 solution is simply to use std::apply:

auto f = [](int a, double b, std::string c) { std::cout<<a<<" "<<b<<" "<<c<< std::endl; };
auto params = std::make_tuple(1,2.0,"Hello");
std::apply(f, params);

Just felt that should be stated once in an answer in this thread (after it already appeared in one of the comments).


The basic C++14 solution is still missing in this thread. EDIT: No, it's actually there in the answer of Walter.

This function is given:

void f(int a, double b, void* c)
{
      std::cout << a << ":" << b << ":" << c << std::endl;
}

Call it with the following snippet:

template<typename Function, typename Tuple, size_t ... I>
auto call(Function f, Tuple t, std::index_sequence<I ...>)
{
     return f(std::get<I>(t) ...);
}

template<typename Function, typename Tuple>
auto call(Function f, Tuple t)
{
    static constexpr auto size = std::tuple_size<Tuple>::value;
    return call(f, t, std::make_index_sequence<size>{});
}

Example:

int main()
{
    std::tuple<int, double, int*> t;
    //or std::array<int, 3> t;
    //or std::pair<int, double> t;
    call(f, t);    
}

DEMO

davidhigh
  • 14,652
  • 2
  • 44
  • 75
  • I can't get this demo to work with smart pointers - whats wrong here? `http://coliru.stacked-crooked.com/a/8ea8bcc878efc3cb` – Xeverous Sep 07 '17 at 17:01
  • @Xeverous: do you want to get something like this [here](http://coliru.stacked-crooked.com/a/6d8310c726865c6d)? – davidhigh Sep 07 '17 at 17:57
  • thanks, I have 2 questions: 1. Why can't I pass `std::make_unique` directly? Does it need concrete function instance? 2. Why `std::move(ts)...` if we can change `[](auto... ts)` to `[](auto&&... ts)`? – Xeverous Sep 07 '17 at 18:01
  • @Xeverous: 1. doesn't work from the signatures: your `std::make_unique` expects a tuple, and a tuple can be created from an unpacked tuple only via another call to `std::make_tuple`. This is what I've done in the lambda (although it's highly redundant, as you can also simply copy the tuple into the unique pointer without any use for `call`). – davidhigh Sep 07 '17 at 18:09
  • @Xeverous: 2. You could do the second, but I guess you want to work with copies ... there's hardly a need for an owning pointer of references. (You could also get this with your second version by using something like `std::make_tuple>(std::forward(ts) ...)` as return, but that's much more to write.) – davidhigh Sep 07 '17 at 18:12
  • About owning ptr for reference - I just extracted the value not to place everything on one line. Normally I would just pass a temporary. – Xeverous Sep 07 '17 at 18:15
  • 1
    This should now be **the** answer. – Fureeish Jun 27 '19 at 22:27
46

This is a complete compilable version of Johannes' solution to awoodland's question, in the hope it may be useful to somebody. This was tested with a snapshot of g++ 4.7 on Debian squeeze.

###################
johannes.cc
###################
#include <tuple>
#include <iostream>
using std::cout;
using std::endl;

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; };

double foo(int x, float y, double z)
{
  return x + y + z;
}

template <typename ...Args>
struct save_it_for_later
{
  std::tuple<Args...> params;
  double (*func)(Args...);

  double delayed_dispatch()
  {
    return callFunc(typename gens<sizeof...(Args)>::type());
  }

  template<int ...S>
  double callFunc(seq<S...>)
  {
    return func(std::get<S>(params) ...);
  }
};

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
int main(void)
{
  gens<10> g;
  gens<10>::type s;
  std::tuple<int, float, double> t = std::make_tuple(1, 1.2, 5);
  save_it_for_later<int,float, double> saved = {t, foo};
  cout << saved.delayed_dispatch() << endl;
}
#pragma GCC diagnostic pop

One can use the following SConstruct file

#####################
SConstruct
#####################
#!/usr/bin/python

env = Environment(CXX="g++-4.7", CXXFLAGS="-Wall -Werror -g -O3 -std=c++11")
env.Program(target="johannes", source=["johannes.cc"])

On my machine, this gives

g++-4.7 -o johannes.o -c -Wall -Werror -g -O3 -std=c++11 johannes.cc
g++-4.7 -o johannes johannes.o
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
Faheem Mitha
  • 6,096
  • 7
  • 48
  • 83
43

Here is a C++14 solution.

template <typename ...Args>
struct save_it_for_later
{
  std::tuple<Args...> params;
  void (*func)(Args...);

  template<std::size_t ...I>
  void call_func(std::index_sequence<I...>)
  { func(std::get<I>(params)...); }
  void delayed_dispatch()
  { call_func(std::index_sequence_for<Args...>{}); }
};

This still needs one helper function (call_func). Since this is a common idiom, perhaps the standard should support it directly as std::call with possible implementation

// helper class
template<typename R, template<typename...> class Params, typename... Args, std::size_t... I>
R call_helper(std::function<R(Args...)> const&func, Params<Args...> const&params, std::index_sequence<I...>)
{ return func(std::get<I>(params)...); }

// "return func(params...)"
template<typename R, template<typename...> class Params, typename... Args>
R call(std::function<R(Args...)> const&func, Params<Args...> const&params)
{ return call_helper(func,params,std::index_sequence_for<Args...>{}); }

Then our delayed dispatch becomes

template <typename ...Args>
struct save_it_for_later
{
  std::tuple<Args...> params;
  std::function<void(Args...)> func;
  void delayed_dispatch()
  { std::call(func,params); }
};
Walter
  • 44,150
  • 20
  • 113
  • 196
  • 8
    Upvoted for the (proposed) implementation of `std::call`. C++14's chaotic zoo of `integer_sequence` and `index_sequence` helper types is explained here: http://en.cppreference.com/w/cpp/utility/integer_sequence Notice the conspicuous absence of `std::make_index_sequence(Args...)`, which is why Walter was forced into the clunkier syntax `std::index_sequence_for{}`. – Quuxplusone Jan 26 '14 at 21:02
  • 3
    And apparently voted into C++17 since 3/2016 as std::apply(func, tup): http://en.cppreference.com/w/cpp/utility/apply – ddevienne May 09 '16 at 15:28
18

This is a bit complicated to achieve (even though it is possible). I advise you to use a library where this is already implemented, namely Boost.Fusion (the invoke function). As a bonus, Boost Fusion works with C++03 compilers as well.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Karel Petranek
  • 15,005
  • 4
  • 44
  • 68
8

solution. First, some utility boilerplate:

template<std::size_t...Is>
auto index_over(std::index_sequence<Is...>){
  return [](auto&&f)->decltype(auto){
    return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
  };
}
template<std::size_t N>
auto index_upto(std::integral_constant<std::size_t, N> ={}){
  return index_over( std::make_index_sequence<N>{} );
}

These let you call a lambda with a series of compile-time integers.

void delayed_dispatch() {
  auto indexer = index_upto<sizeof...(Args)>();
  indexer([&](auto...Is){
    func(std::get<Is>(params)...);
  });
}

and we are done.

index_upto and index_over let you work with parameter packs without having to generate a new external overloads.

Of course, in you just

void delayed_dispatch() {
  std::apply( func, params );
}

Now, if we like that, in we can write:

namespace notstd {
  template<class T>
  constexpr auto tuple_size_v = std::tuple_size<T>::value;
  template<class F, class Tuple>
  decltype(auto) apply( F&& f, Tuple&& tup ) {
    auto indexer = index_upto<
      tuple_size_v<std::remove_reference_t<Tuple>>
    >();
    return indexer(
      [&](auto...Is)->decltype(auto) {
        return std::forward<F>(f)(
          std::get<Is>(std::forward<Tuple>(tup))...
        );
      }
    );
  }
}

relatively easily and get the cleaner syntax ready to ship.

void delayed_dispatch() {
  notstd::apply( func, params );
}

just replace notstd with std when your compiler upgrades and bob is your uncle.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
3

Thinking about the problem some more based on the answer given I've found another way of solving the same problem:

template <int N, int M, typename D>
struct call_or_recurse;

template <typename ...Types>
struct dispatcher {
  template <typename F, typename ...Args>
  static void impl(F f, const std::tuple<Types...>& params, Args... args) {
     call_or_recurse<sizeof...(Args), sizeof...(Types), dispatcher<Types...> >::call(f, params, args...);
  }
};

template <int N, int M, typename D>
struct call_or_recurse {
  // recurse again
  template <typename F, typename T, typename ...Args>
  static void call(F f, const T& t, Args... args) {
     D::template impl(f, t, std::get<M-(N+1)>(t), args...);
  }
};

template <int N, typename D>
struct call_or_recurse<N,N,D> {
  // do the call
  template <typename F, typename T, typename ...Args>
  static void call(F f, const T&, Args... args) {
     f(args...);
  }
};

Which requires changing the implementation of delayed_dispatch() to:

  void delayed_dispatch() {
     dispatcher<Args...>::impl(func, params);
  }

This works by recursively converting the std::tuple into a parameter pack in its own right. call_or_recurse is needed as a specialization to terminate the recursion with the real call, which just unpacks the completed parameter pack.

I'm not sure this is in anyway a "better" solution, but it's another way of thinking about and solving it.


As another alternative solution you can use enable_if, to form something arguably simpler than my previous solution:

#include <iostream>
#include <functional>
#include <tuple>

void f(int a, double b, void* c) {
  std::cout << a << ":" << b << ":" << c << std::endl;
}

template <typename ...Args>
struct save_it_for_later {
  std::tuple<Args...> params;
  void (*func)(Args...);

  template <typename ...Actual>
  typename std::enable_if<sizeof...(Actual) != sizeof...(Args)>::type
  delayed_dispatch(Actual&& ...a) {
    delayed_dispatch(std::forward<Actual>(a)..., std::get<sizeof...(Actual)>(params));
  }

  void delayed_dispatch(Args ...args) {
    func(args...);
  }
};

int main() {
  int a=666;
  double b = -1.234;
  void *c = NULL;

  save_it_for_later<int,double,void*> saved = {
                                 std::tuple<int,double,void*>(a,b,c), f};
  saved.delayed_dispatch();
}

The first overload just takes one more argument from the tuple and puts it into a parameter pack. The second overload takes a matching parameter pack and then makes the real call, with the first overload being disabled in the one and only case where the second would be viable.

Flexo
  • 87,323
  • 22
  • 191
  • 272
  • 1
    I worked on something awfully similar to this a while back. If I have time I'll go have a second look and see how it compares to the current answers. – Michael Price Oct 22 '11 at 18:45
  • @MichaelPrice - purely from the learning perspective I'd be interested in seeing any alternative solutions that don't boil down to some awful hack botching the stack pointer (or similarly calling convention specific tricks). – Flexo Oct 22 '11 at 19:45
2

My variation of the solution from Johannes using the C++14 std::index_sequence (and function return type as template parameter RetT):

template <typename RetT, typename ...Args>
struct save_it_for_later
{
    RetT (*func)(Args...);
    std::tuple<Args...> params;

    save_it_for_later(RetT (*f)(Args...), std::tuple<Args...> par) : func { f }, params { par } {}

    RetT delayed_dispatch()
    {
        return callFunc(std::index_sequence_for<Args...>{});
    }

    template<std::size_t... Is>
    RetT callFunc(std::index_sequence<Is...>)
    {
        return func(std::get<Is>(params) ...);
    }
};

double foo(int x, float y, double z)
{
  return x + y + z;
}

int testTuple(void)
{
  std::tuple<int, float, double> t = std::make_tuple(1, 1.2, 5);
  save_it_for_later<double, int, float, double> saved (&foo, t);
  cout << saved.delayed_dispatch() << endl;
  return 0;
}
schwart
  • 137
  • 10
  • All those solutions may solve the initial problem, but honestly guys, isn't this template stuff going into a wrong direction - in terms of simplicity and **maintainability**? – x y Oct 16 '15 at 14:30
  • I think templates got much better and more understandable with C++11 and 14. A few years ago when I looked at what boost makes with templates under the hood, I got really discouraged. I agree that developing good templates is significantly more difficult than just using them. – schwart Oct 26 '15 at 12:01
  • 2
    @xy Firstly, in terms of template complexity, this is _nothing_. Secondly, most helpers templates are an initial investment for a tonne of time saved when instantiating them later. Lastly, what, would you rather _not_ have the ability to do what templates allow you to do? You could just not use it, and not leave irrelevant comments that seem to be policing other programmers. – underscore_d Jan 30 '16 at 10:30
0

a lot of answers have been provided but I found them too complicated and not very natural. I did it another way, without using sizeof or counters. I used my own simple structure (ParameterPack) for parameters to access the tail of parameters instead of a tuple. Then, I appended all the parameters from my structure into function parameters, and finnally, when no more parameters were to be unpacked, I run the function. Here is the code in C++11, I agree that there is more code than in others answers, but I found it more understandable.

template <class ...Args>
struct PackParameters;

template <>
struct PackParameters <>
{
    PackParameters() = default;
};

template <class T, class ...Args>
struct PackParameters <T, Args...>
{
    PackParameters ( T firstElem, Args... args ) : value ( firstElem ), 
    rest ( args... ) {}

    T value;
    PackParameters<Args...> rest;
};

template <class ...Args>
struct RunFunction;

template <class T, class ...Args>
struct RunFunction<T, Args...>
{
    template <class Function>
    static void Run ( Function f, const PackParameters<T, Args...>& args );

    template <class Function, class... AccumulatedArgs>
    static void RunChild ( 
                          Function f, 
                          const PackParameters<T, Args...>& remainingParams, 
                          AccumulatedArgs... args 
                         );
};

template <class T, class ...Args>
template <class Function>
void RunFunction<T, Args...>::Run ( 
                                   Function f, 
                                   const PackParameters<T, Args...>& remainingParams 
                                  )
{
    RunFunction<Args...>::template RunChild ( f, remainingParams.rest,
                                              remainingParams.value );
}

template <class T, class ...Args>
template<class Function, class ...AccumulatedArgs>
void RunFunction<T, Args...>::RunChild ( Function f, 
                                         const PackParameters<T, Args...>& remainingParams, 
                                         AccumulatedArgs... args )
{
    RunFunction<Args...>:: template RunChild ( f, remainingParams.rest,
                                               args..., remainingParams.value );
}


template <>
struct RunFunction<>
{
    template <class Function, class... AccumulatedArgs>
    static void RunChild ( Function f, PackParameters<>, AccumulatedArgs... args )
    {
        f ( args... );
    }

    template <class Function>
    static void Run ( Function f, PackParameters<> )
    {
        f ();
    }
};

struct Toto
{
    std::string k = "I am toto";
};

void f ( int i, Toto t, float b, std::string introMessage )
{
    float res = i * b;

    std::cerr << introMessage << " " << res << std::endl;
    std::cerr << "Toto " << t.k << std::endl;
}

int main(){
    Toto t;
    PackParameters<int, Toto, float, std::string> pack ( 3, t, 4.0, " 3 * 4 =" );

    RunFunction<int, Toto, float, std::string>::Run ( f, pack );
    return 0;
}
0

I recently had to handle this problem and found the discussion here useful, but the examples a bit cumbersome especially for my usecase using functors. Hopefully my solution will benefit those that come later.

#include <iostream>
#include <tuple>

void f(int a, double b, void* c) {
  std::cout << a << ":" << b << ":" << c << std::endl;
}

template<void func(int, double, void*)>
struct foo {
    
    template<class... args_t>
    constexpr void operator()(args_t... args) {
      func(args...);
    }

    template<class... T, size_t... I>
    constexpr void operator()(std::tuple<T...>& t, std::index_sequence<I...>) {
      this->operator()(std::get<I>(std::forward<std::tuple<T...>>(t))...);
    }

    template<typename... T>
    constexpr void operator()(std::tuple<T...>&& t)
    {        
      this->operator()(t, std::index_sequence_for<T...>{});
    }
};

int main() {
  foo<&f>()(std::make_tuple(1, 45.2, nullptr));
  return 0;
}