0

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 
}
Hank
  • 310
  • 1
  • 5
  • You're asking a lot of C++ here and are expecting it to be a dynamic programming language, which it isn't. Why can't that other function work the same way? Why can't you pass this in as a `std::vector` of something? – tadman May 14 '20 at 18:23
  • 2
    @arnes It compiles, but it doesn't work. You end up calling foo with only the `args.end` since the comma expression only returns the last expression. – NathanOliver May 14 '20 at 18:30
  • I hate to say it but unless you use a macro I can't see a way to do this that doesn't wind up making a bunch of copies. – NathanOliver May 14 '20 at 18:31
  • @NathanOliver i thought op meant doesn't compile, removing my comment. – calynr May 14 '20 at 19:08

1 Answers1

3

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

rafix07
  • 20,001
  • 3
  • 20
  • 33
  • 2
    Sorry, but... why `std::tuple_cat()` inside `std::apply()` and not immediately creating `t`? I mean: why non simply `template void bar (Conts && ... c) { auto t = std::tuple_cat ( std::make_tuple(c.begin(),c.end())... ); std::apply([](auto && ... vals) {foo(std::forward(vals)...);}, t); }` ? – max66 May 14 '20 at 19:38
  • @max66 Yep, now I see it, you are completely right, one `apply` too much. Nice short version :) – rafix07 May 14 '20 at 19:41