C++11: I Can Go from Multiple Args to Tuple, But How to Go from Tuple to Multiple Args

C++11: I can go from multiple args to tuple, but can I go from tuple to multiple args?

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.

Unpacking arguments from tuples

I think that @Xeo's comment summed it up well. From 14.5.3 of the C++11 standard:

A pack expansion consists of a pattern and an ellipsis, the
instantiation of which produces zero or more instantiations of the
pattern in a list.

In your case, by the time you finish with the recursive template instantiation and end up in the partial specialization, you have

f(std::get<N>(std::forward<Tuple>(t))...);

...where N is parameter pack of four ints (0, 1, 2, and 3). From the standardese above, the pattern here is

std::get<N>(std::forward<Tuple>(t))

The application of the ... ellipsis to the above pattern causes it to be expanded into four instantiations in list form, i.e.

f(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t));

How to extract tuple into a function parameters

As mentioned in the comments, std::apply is suitable for your case.

pthread_create(
&td, nullptr,
[](void* p) {
auto param = static_cast<typename signature<decltype(f)>::argument_type*>(p);
std::apply([](auto& f, auto&&... args) {
f(std::forward<decltype(args)>(args)...);
}, *param);
return (void*)nullptr;
},
&tp);

Demo.

Pass multiple arguments in form of tuple

You can use the * operator to unpack the argument list:

input_tuple = (1,2,3)
instance_1 = InputStuff(*input_tuple)

Passing a tuple to a variadic mixin class

http://cpptruths.blogspot.fr/2012/06/perfect-forwarding-of-parameter-groups.html

Davidbrcz's solution as specified in his blog above was sufficient to solve my conundrum. The solution is rather complex so I'll direct you to his page for it, but the basic idea is to on-the-fly create a numeric index tuple, à la std::maketuple(0, 1, 2, ...) where the tuple contains each of the indices of the various members of the tuple you need to enumerate. Then you just use:

M(std::forward<ArgM>(std::get<IdxM>(argm))...)

For M as either C or H in the example above and ArgM the arguments to M and IdxM the equally sized tuple of indices. Because the lists are the same length, the list gets rolled out into parameters in tandem and the tuple is unpacked.

The limitation is because you want the complicated step of building the index tuple to be hidden as an implementation detail, you need to use Constructor Delegation such that the public constructor takes the 2 tuples which then delegates to the private constructor which takes 2 value tuples and 2 index tuples. GCC 4.7.2 supports Delegated Constructors, but 4.6.3 doesn't.

To get around this, you need to make the 4-parameter constructor (2 tuples of values, 2 tuples of indices) public and then I wrote a macro to fill in the index tuple parameters:

#if __GNUC__ < 4 || __GNUC_MINOR__ <= 6
#define ZEROPARAM , detail::make_indices<>::type()
#define ONEPARAM , detail::make_indices<int>::type()
#define TWOPARAM , detail::make_indices<int, int>::type()
#define THREEPARAM , detail::make_indices<int, int, int>::type()
#define FOURPARAM , detail::make_indices<int, int, int, int>::type()
#define FIVEPARAM , detail::make_indices<int, int, int, int, int>::type()
#define SIXPARAM , detail::make_indices<int, int, int, int, int, int>::type()
#define SEVENPARAM , detail::make_indices<int, int, int, int, int, int, int>::type()
#define EIGHTPARAM , detail::make_indices<int, int, int, int, int, int, int, int>::type()
#define NINEPARAM , detail::make_indices<int, int, int, int, int, int, int, int, int>::type()
#define TENPARAM , detail::make_indices<int, int, int, int, int, int, int, int, int, int>::type()
#else // __GNUC__ < 4 || __GNUC_MINOR__ <= 6
#define ZEROPARAM
#define ONEPARAM
#define TWOPARAM
#define THREEPARAM
#define FOURPARAM
#define FIVEPARAM
#define SIXPARAM
#define SEVENPARAM
#define EIGHTPARAM
#define NINEPARAM
#define TENPARAM
#endif // __GNUC__ < 4 || __GNUC_MINOR__ <= 6

And then add the appropriate macro following the construction of the given Tester, at least while there are still folks on GCC 4.6.3 while I work to get everyone to at least 4.7.2 and preferable 4.8.1. :)

I wish I could give Davidbrcz credit for the solution but at least this may be helpful for folks facing a similar problem to apply his solution in their specific case. The main thing is copy his make_indices template class to do the actual work; the rest is a cake walk!

Expanding tuples into arguments

myfun(*some_tuple) does exactly what you request. The * operator simply unpacks the tuple (or any iterable) and passes them as the positional arguments to the function. Read more about unpacking arguments.

unpacking a tuple to call a matching function pointer

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

Storing variable arguments to be forwarded later


What am i doing wrong here ?

Well... first of all you need to unpack the tuple; by example, using an helper function, something as follows (std::index_sequence and std::make_index_sequence are available from C++14; you're compiling C++17 so you can use they)

template <std::size_t ... Is>
void exec_helper (std::index_sequence<Is...> const &)
{ T* item = new T{std::get<Is>(_args)...}; }

virtual void exec() override {
exec_helper(std::make_index_sequence<sizeof...(Ps)>{});

std::cout << "done " << _actionId << std::endl;
}

But there are other problems.

By example: you can use perfect forwarding only with template arguments of the functions/methods, so not in this constructor

CustomAction(int id, Ps&&... args) : _actionId(id), 
_args(std::make_tuple(std::forward<Ps>(args)...)) {
}

because Ps... are template arguments of the class, not of the constructor.

Should works something as

template <typename ... Us>
CustomAction (int id, Us && ... args) : _actionId{id},
_args{std::forward<Us>(args)...}
{ }

And I suppose that

aq.push<CustomAction<Item>>(actionId, param);

should be

// ........................VVV
aq.push<CustomAction<Item, int>>(actionId, param);


Related Topics



Leave a reply



Submit