Gils
Gils

Reputation: 643

How to transform Parameter Pack into something else than std::tuple?

It is better explained by an example:

template <typename T1, typename T2>
struct OnePair
{
    using TupleOfArgs = std::tuple<T1, T2>;
    using TupleOfPairs = std::tuple<std::pair<T1, T2>>;
};

template <typename T1, typename T2, typename T3, typename T4>
struct TwoPairs
{
    using TupleOfArgs = std::tuple<T1, T2, T3, T4>;
    using TupleOfPairs = std::tuple<std::pair<T1, T2>, std::pair<T3, T4>>;
};

template <typename... Args>
struct NPairs
{
    using TupleOfArgs = std::tuple<Args...>;
//  using TupleOfPairs = ???
};

OnePair defines a tuple with one pair. TwoPairs defines a tuple with two pairs.

How do I define TupleOfPairs in NPairs so it transforms the Parameter Pack to std::tuple of pairs?

Is it possible to achieve that with the std library? Maybe with boost::mpl?

Two answers, both great. @chris uses an iterative method while @aschepler uses a recursive solution. Personally, I found the recursive solution easier to follow.

Upvotes: 2

Views: 162

Answers (2)

aschepler
aschepler

Reputation: 72271

Here's one straightforward way with a recursive helper:

template <typename PairsTuple, typename... Ts>
struct ZipPairs;

template <typename PairsTuple>
struct ZipPairs<PairsTuple> { using type = PairsTuple; };

template <typename... Pairs, typename T1, typename T2, typename... Ts>
struct ZipPairs<std::tuple<Pairs...>, T1, T2, Ts...>
{
    using type = typename ZipPairs<
        std::tuple<Pairs..., std::pair<T1, T2>>, Ts...>::type;
};

template <class... Args>
struct NPairs
{
    static_assert(sizeof...(Args) % 2 == 0);
    using TupleOfArgs = std::tuple<Args...>;
    using TupleOfPairs = typename ZipPairs<std::tuple<>, Args...>::type;
};

Upvotes: 1

Qaz
Qaz

Reputation: 61900

You can use the familiar index sequence trick, but with half the size (live example):

static constexpr auto get_tuple() {
    constexpr auto N = sizeof...(Args);
    static_assert(N%2 == 0);

    using ArgsTuple = std::tuple<Args...>;
    auto impl = []<std::size_t... Is>(std::index_sequence<Is...>) {
        return std::tuple<
            // Is goes from 0 to N/2, representing Ith pair
            std::pair<std::tuple_element_t<Is*2, ArgsTuple>, std::tuple_element_t<Is*2 + 1, ArgsTuple>>...
        >{};
    };

    return impl(std::make_index_sequence<N/2>{});
}

using TupleOfArgs = decltype(get_tuple());

If you don't have C++20, you'll have to expand the lambda out to a function instead of being able to use the nice inline index sequence expansion, but the core still functions. It might also be a bit cleaner with a typelist instead of std::tuple, but it's workable.

Upvotes: 2

Related Questions