34

Possible Duplicate:
How do I expand a tuple into variadic template function's arguments?
“unpacking” a tuple to call a matching function pointer

In C++11 templates, is there a way to use a tuple as the individual args of a (possibly template) function?

Example:
Let's say I have this function:

void foo(int a, int b)  
{  
}

And I have the tuple auto bar = std::make_tuple(1, 2).

Can I use that to call foo(1, 2) in a templaty way?

I don't mean simply foo(std::get<0>(bar), std::get<1>(bar)) since I want to do this in a template that doesn't know the number of args.

More complete example:

template<typename Func, typename... Args>  
void caller(Func func, Args... args)  
{  
    auto argtuple = std::make_tuple(args...);  
    do_stuff_with_tuple(argtuple);  
    func(insert_magic_here(argtuple));  // <-- this is the hard part  
}

I should note that I'd prefer to not create one template that works for one arg, another that works for two, etc…

Community
  • 1
  • 1
Thomas
  • 3,851
  • 2
  • 26
  • 30
  • 3
    Sure. You want something like `template call(F f, Tuple const & t) { f(std::get(t)...); }`. Now just fill in the blanks :-) – Kerrek SB May 26 '12 at 12:15
  • Do you mean skipping variadic templates and creating multiple `caller()` templates instead? – Thomas May 26 '12 at 12:17
  • @Thomas: You'll have to make a little dispatching harness that builds up the integer pack `N...`, and partially specializing when `N == std::tuple_size::value`, you want to call the original function in the way I suggested. – Kerrek SB May 26 '12 at 12:21
  • (It should have been `int ...N`, of course.) – Kerrek SB May 26 '12 at 12:59

2 Answers2

60

Try something like this:

// implementation details, users never invoke these directly
namespace detail
{
    template <typename F, typename Tuple, bool Done, int Total, int... N>
    struct call_impl
    {
        static void call(F f, Tuple && t)
        {
            call_impl<F, Tuple, Total == 1 + sizeof...(N), Total, N..., sizeof...(N)>::call(f, std::forward<Tuple>(t));
        }
    };

    template <typename F, typename Tuple, int Total, int... N>
    struct call_impl<F, Tuple, true, Total, N...>
    {
        static void call(F f, Tuple && t)
        {
            f(std::get<N>(std::forward<Tuple>(t))...);
        }
    };
}

// user invokes this
template <typename F, typename Tuple>
void call(F f, Tuple && t)
{
    typedef typename std::decay<Tuple>::type ttype;
    detail::call_impl<F, Tuple, 0 == std::tuple_size<ttype>::value, std::tuple_size<ttype>::value>::call(f, std::forward<Tuple>(t));
}

Example:

#include <cstdio>
int main()
{
    auto t = std::make_tuple("%d, %d, %d\n", 1,2,3);
    call(std::printf, t);
}

With some extra magic and using std::result_of, you can probably also make the entire thing return the correct return value.

ildjarn
  • 59,718
  • 8
  • 115
  • 201
Kerrek SB
  • 428,875
  • 83
  • 813
  • 1,025
  • 9
    @JonathanWakely : `call_impl` is an implementation detail -- users never call it directly, global `call` is all that users interface with. Is the example that unclear? – ildjarn May 26 '12 at 15:45
  • Ah no, I just hadn't looked properly, I thought `call` was a replacement for the OP's `caller` and so the unwieldy use of `call_impl` happened in the user's code. – Jonathan Wakely May 26 '12 at 15:54
  • This, my good sir, is confounding, and deserves an up vote. – Richard J. Ross III May 26 '12 at 16:04
  • 5
    That's the best way of unpacking std::tuple I've found on stackoverflow yet and it has half of the upvotes of much worse solutions... – cubuspl42 Dec 03 '12 at 13:43
  • 1
    Just a suggestion, you might wanna throw an `auto` in there for call so you can use the return value of a function as well, BTW other than that really like the solution – aaronman Sep 30 '13 at 22:14
4

Create an "index tuple" (a tuple of compile-time integers) then forward to another function that deduces the indices as a parameter pack and uses them in a pack expansion to call std::get on the tuple:

#include <redi/index_tuple.h>

template<typename Func, typename Tuple, unsigned... I>  
  void caller_impl(Func func, Tuple&& t, redi::index_tuple<I...>)  
  {  
    func(std::get<I>(t)...);
  }

template<typename Func, typename... Args>  
  void caller(Func func, Args... args)  
  {  
    auto argtuple = std::make_tuple(args...);  
    do_stuff_with_tuple(argtuple);
    typedef redi::to_index_tuple<Args...> indices;
    caller_impl(func, argtuple, indices());
  }

My implementation of index_tuple is at https://gitlab.com/redistd/redistd/blob/master/include/redi/index_tuple.h but it relies on template aliases so if your compiler doesn't support that you'd need to modify it to use C++03-style "template typedefs" and replace the last two lines of caller with

    typedef typename redi::make_index_tuple<sizeof...(Args)>::type indices;
    caller_impl(func, argtuple, indices());

A similar utility was standardised as std::index_sequence in C++14 (see index_seq.h for a standalone C++11 implementation).

Jonathan Wakely
  • 153,269
  • 21
  • 303
  • 482
  • can you please explain the `type::template` syntax ? – ubik Nov 03 '15 at 12:17
  • @ubik https://womble.decadent.org.uk/c++/template-faq.html#disambiguation – Jonathan Wakely Nov 04 '15 at 12:11
  • @JonathanWakely Very nice. Maybe I'm missing an obvious trick, but is there any way to apply a `tuple` like this when the function in question is a constructor... or is that asking for a little bit too much magic? :D – underscore_d Jan 17 '16 at 13:54
  • Hmm, think I've gotten it working by having the `_impl` function equivalent templated on `T_ReturnedObject` and getting it to call that object's ctor with all the stuff unpacked. I guess that was the "obvious trick" I mentioned! – underscore_d Jan 17 '16 at 14:57
  • @underscore_d, yes, that's the obvious way to do it. – Jonathan Wakely Jan 17 '16 at 18:59