6

I've read some articles about this new C++11 feature but I didn't understand all stuff (I'm new to C++). How do I access a specific argument like I can do using va_arg from stdarg.h in C?

template <typename ... Args>
void f(Args ... args)
{
    for(size_t i = 0; i < sizeof ...(args); i++)
    {
        // obviously, args...[i] didn't work...
    }
}
The Mask
  • 17,007
  • 37
  • 111
  • 185
  • I think this answer has the best solution if all the arguments have the same type (`T`) : http://stackoverflow.com/a/3703826/364818 Basically, create a local `std::vector x = { { args } };` on the first line of `f()` and then you can iterate over the elements of `x[]` – Mark Lakata Mar 24 '15 at 00:07

2 Answers2

15

The problem is with TYPE var = args[c];, what do you write for TYPE? Each i has a different type, so you won't be able to use a for loop like this.

Generally, the normal approach is to use recursion.

void f() { } //end loop

template<class FirstType, typename...Args> 
void f(FirstType&& first, Args&&...rest) {  
    //do loop body
    loop_body(std::forward<FirstType>(first)...)
    //do next "iteration"
    f(std::forward<Args>(rest)...);
}

There's also this way to do it without recursion, but it's a bit more advanced:

template<typename...Args> 
void f(Args&&...args) {  
    typedef int[] for_each;
    for_each{((void)(   loop_body(std::forward<Args>(args))  ),0)...,0};
}

And finally, if you really want to access one by index:

//note that "i" must be a compile time constant
auto var = std::get<i>(std::tie(std::forward<Args>(args)...));


The typedef int[] code is very strange, and so I'll lay it out here.
We'd like to call a function loop_body(std::forward<Args>(args))...; but unfortunately, parameter packs can only be expanded in certain contexts, and that's not one of them. The easiest and most obvious solution is to pass the results of all those calls to a function that does nothing: do_nothing(loop_body(std::forward<Args>(args))...), but unfortunately, this fails for void return types, because you can't instantiate a void to pass to the do_nothing. Worse, it could call each of the functions in the wrong order. One way to "convert" a void expression to something else is with an arcane trick with the comma operator, (func(), 0) executaes func and then "returns" 0.
Worse, for reasons that I admittedly don't understand, f(vs)...,0; nor (f(vs),0)...,0; are valid contexts for expanding parameter packs. However, type array[] = {vs...} is a valid context. So now we have a way to combine this context with expressions with return values: int array[] = {(f(vs),0)...}; And it works! Mostly!
If the parameter pack has zero types (yes, that's valid. never forget it.), then this results in a compiler error. So we have to add one additional zero on the end so there is always at least one element: int array[] = {(f(vs),0)..., 0};. Also, most compilers warn that array is an unused variable. One way to bypass that warning is to make the type a temporary. int a = (expr); is a local, but (int)(expr) creates an unnamed temporary. So we want (int []){(f(vs),0)..., 0};. For reasons I can't recall, this (int[]) normally hidden behind a typedef. As a final detail, since some classes can overload the comma operator, it's safest to cast the functions to void: int array[] = {((void)(f(vs)),0)..., 0};

Unrelated, I've contemplated this macro in the past, which hides the ugly details a little more. But I feel like there's a downside I'm overlooking or else it would be more common.

#define FOREACH_VARIADIC(EXPR) (int[]){((void)(EXPR),0)...,0}    

template<typename...Args> 
void f(Args&&...args) {  
    FOREACH_VARIADIC(loop_body(std::forward<Args>(args)));
}
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • Can you explain the `typedef char[] for_each` approach a bit more? – Remy Lebeau Apr 14 '14 at 20:42
  • You should add some `void()` to your `for_each` to break possible overloaded `operator,`. Or `for_each x{ (void)(pattern)..., 0}`. The declarator-id is required, btw. – dyp Apr 14 '14 at 21:06
  • Passing a pack expansion to a function solely for the side effects is dangerous, because the evaluation is unsequenced. – dyp Apr 14 '14 at 21:11
  • (The declarator-id might be unnecessary because this isn't a declaration, but a valid expression; a form of the functional cast notation.) – dyp Apr 14 '14 at 21:16
  • 2
    I wouldn't recommend this macro because it uses non-C++ C99 compound literals, while it would be very easy to make this portable C++ with alias templates. @dyp this is not a functional cast notation, because it does not match the syntax `(type)expression`. – Johannes Schaub – Johannes Schaub - litb Apr 14 '14 at 21:21
  • @JohannesSchaub-litb `(type)expression` would be an explicit type conversion in the cast notation [expr.cast]. `type(expression)` OTOH is a postfix-expression, an explicit type conversion in the functional notation [expr.type.conv]. /3 specifies the same syntax for braces. – dyp Apr 14 '14 at 21:37
  • @JohannesSchaub-litb My original comment was about the second codeblock (not the macro), where I mistook `for_each{ ( (void)(loop_body(std::forward(args))) ,0)...,0};` for a declaration. I agree with your concerns about the syntax in the macro. – dyp Apr 14 '14 at 22:09
  • @dyp now this became a very confusing comment thread. I'm glad we finally got to understand each other :)) – Johannes Schaub - litb Apr 14 '14 at 22:10
  • @JohannesSchaub-litbL: I couldn't think of a way to make the macro replacement for C++03 or C++11, so apparently (and not surprisingly) you know/recall something I don't. Sadly, my knowledge of C++ is mostly limited to the MSVC 2013 subset, as that's what I use primarily. – Mooing Duck Apr 14 '14 at 22:11
  • `struct evil{void operator,(int)const{return 0;}}; evil loop_body(...){return {};}` -- your array trick is insufficient. – Yakk - Adam Nevraumont Apr 14 '14 at 22:49
  • @Yakk: My array trick already handled that situation, except in the explanation of the details of how the array trick works, where I mention that specifically, but elide it from many of the sample substeps. (Also in the macro at the bottom, which I have now fixed) – Mooing Duck Apr 14 '14 at 23:12
  • Ah, I missed the `(void)`. Darn vacuum, hard to spot! – Yakk - Adam Nevraumont Apr 14 '14 at 23:22
  • You are missing my favorite, a function that takes a pack of nullary lambdas and invokes them in order. `do_in_order([&]{ code }...);`, where the code contains packs. – Yakk - Adam Nevraumont Apr 14 '14 at 23:50
  • @Yakk I'm having trouble with the details on that one: http://coliru.stacked-crooked.com/a/a66ebaf336ce33f7 – Mooing Duck Apr 14 '14 at 23:58
  • @MooingDuck As Johannes explained, g++ doesn't support this correctly yet (expansion of patterns containing macros). – dyp Apr 15 '14 at 00:01
  • @Yakk Why don't you add a link to one of your answers? I remember you've discussed this in detail in some answer recently.. Maybe this should get (has already?) a FAQ entry? – dyp Apr 15 '14 at 00:02
  • @dyp would have to find it: commenting while putting baby to sleep. ;) But yes, this is a pretty frequently asked C++11 question: so frequent someone should propose a non-hacky way to do it in C++1z. – Yakk - Adam Nevraumont Apr 15 '14 at 00:07
2

Accepted answer is very good, but here's an idea using C++14 generic lambdas:

template <typename F>
void variadic_for_each(F) {}

template <typename F, typename Head, typename... Tail>
void variadic_for_each(Head&& head, Tail&&... tail, F f)
{
    f(std::forward<Head>(head));
    variadic_for_each(std::forward<Tail>(tail)..., f);
}

Example usage:

template <typename... Ts>
void myFunc(Ts&&... vs)
{
    variadic_for_each(std::forward<Ts>(vs)..., [](auto&& v)
    {
        // loop body
    });
}
Oktalist
  • 14,336
  • 3
  • 43
  • 63