Search code examples
c++variadic-functionsparameter-pack

How to expand parameter pack pattern in an "unzipping" manner?


Say I have a variadic function, foo:

template <typename... Args>
void foo(Args... args)
{
    // some work
}

I want to have a magic function, bar, that forwards its arguments to foo in the following manner:

Say if I call

bar(x, y, z);

It'll have the same effect as

foo(x.begin(), x.end(), y.begin(), y.end(), z.begin(), z.end());

How to implement such bar()?

template <typename... Args>
void bar(Args... args)
{
    // what should I put here?
    // foo( (args.begin(), args.end()) ... );  // doesn't work 
}

Solution

  • If you can use C++17, apply std::apply:

    template<class ...  Conts>
    void bar(Conts&& ... c) {
        auto t = std::make_tuple( std::make_tuple(c.begin(),c.end())... );
        // tuple< tuple<C1.begin,C1.end>, tuple<C2.begin,C2.end>, ... >    [1]
        std::apply( [](auto&&... tuples){
                auto oneTuple = std::tuple_cat(std::forward<decltype(tuples)>(tuples)...);
                // tuple< C1.begin(), C1.end(), C2.begin(), C2.end(), ...>   [2]
                std::apply([](auto&&... its){
                    foo(std::forward<decltype(its)>(its)...); /// all iterators begin/end    [3]
                }, oneTuple);
            }, t);
    }
    
    1. Create tuple of tuple<begin,end> for all entries
    2. Use apply to get all tuples created in first step and make one out of them by concatenating - use tuple_cat
    3. Use again apply to extract all iterators from tuple created in second step, pass them all into foo

    Demo