Quest
Quest

Reputation: 2843

std::tuple unpack over multiple arguments

I have a function taking pairs of (argument type, data ptr) as a variadic list (C function). I would like to unpack the tuple into that function as follows:
foo(TypeIndex<std::tuple_element_t<I, Tuple>>(), &std::get<I>(tuple)); thus I wrote a following function:

template<typename Tuple, size_t ...I>
void doUnpack(Tuple const& tp, std::index_sequence<I...>)
{
    foo((type<std::tuple_element_t<I, Tuple>>(), std::get<I>(tp))...);
}

the only problem is comma operator ignores everything on the left uses the right hand side. Imagine type<> function returns 0 for now, so the above evaluates (using input tuple{1,2,3,4,5}) to foo(1,2,3,4,5) instead of foo(0,1, 0,2, 0,3, 0,4, 0,5)

Is there any way to accomplish this?


Code to reproduce:

template<typename Tp>
int type() { return 0; }

template<typename ...Args>
void fun(Args&& ...args)
{
    (std::cout << ... << args) << std::endl;
}

template<typename Tuple, size_t ...I>
void doUnpack(Tuple const& tp, std::index_sequence<I...>)
{
    fun((type<std::tuple_element_t<I, Tuple>>(), std::get<I>(tp))...);
}

int main()
{
    doUnpack(std::tuple{1,2,3,4,5}, std::make_index_sequence<5>{});
    return 0;
}

Upvotes: 0

Views: 608

Answers (1)

n314159
n314159

Reputation: 5095

The following does what you want:

#include <tuple>
#include <iostream>

template<typename Tp>
int type() {
    return 0;
}

template<typename Tuple, size_t... I>
auto doUnpack(Tuple const &tp, std::index_sequence<I...>) {
    auto fun = [](auto &&...args) { (std::cout << ... << args) << std::endl; };
    std::apply(fun, std::tuple_cat(std::pair{type<std::tuple_element_t<I, Tuple>>(), std::get<I>(tp)}...));
}

int main() {
    doUnpack(std::tuple{1, 2, 3, 4, 5}, std::make_index_sequence<5>{});
    return 0;
}

We use two nice STL-functions. std::tuple_cat takes a bunch of tuples (here pairs of the type and the value of the tuple) and concatenates them to one big tuple. So you go from (type0, val0), (type1, val1)... to (type0, val0, type1, val1, ...). This goes around the problem with the comma operator. After that, we apply the function to that with std::apply. Note that I use a lambda since std::apply needs (basically) a callable with a type and a function template does not qualify for that (whereas a lambda is (basically) a struct with templated operator() which works for that.

Upvotes: 3

Related Questions