3

Here are two lines I saw in OpenCV

#define CV_VA_NUM_ARGS_HELPER(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...)    N
#define CV_VA_NUM_ARGS(...)      CV_VA_NUM_ARGS_HELPER(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

I guess it is to count the number of variadic arguments passing to CV_VA_NUM_ARGS. Take following code for example:

CV_VA_NUM_ARGS(a,b,c)

will be extended to

CV_VA_NUM_ARGS_HELPER(a,b,c,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

At this point, I am stuck on understanding what is going on. Concretely, I don't know the utility of _1,_2,etc. Can anyone help me out? Here is a similar post while more complex to me.

EDIT: When I pass no arguments(say CV_VA_NUM_ARGS_HELPER()), the macro will be replaced by 1 instead of 0, why is that?

Finley
  • 795
  • 1
  • 8
  • 26
  • 1
    BTW, if you want to count the number of variadic arguments for more than 10, `#define CV_VA_NUM_ARGS(...) std::tuple_size::value` works in C++11. – Hiroki Dec 19 '18 at 10:49
  • @Hiroki: generally, it is done to dispatch on other MACRO depending of argument count, so this way "fail" – Jarod42 Dec 19 '18 at 13:01
  • @Jarod42 thx but I still do not well understand your comment. Assuming [such use cases](https://wandbox.org/permlink/Q9PZAyprPPPE3QWd), I commented on the above line. So what does your “fail” mean? Since I know that you are a great C++ programmer through my this 2 months SO life, I want to understand precisely what you are saying with examples and learn from you :). – Hiroki Dec 20 '18 at 13:39
  • @Hiroki: I meant iterate over variadic MACRO as for [example](https://stackoverflow.com/a/43194701/2684539), – Jarod42 Dec 20 '18 at 13:59
  • @Jarod42 Ah I see. many thx! – Hiroki Dec 20 '18 at 14:26

2 Answers2

2

_1 to _10 are just placeholders from the start in order to make sure N is positioned over the correct number from 10 to 0 (depending on __VA_ARGS__). They serve no purpose other than occupying a position.

Since each macro parameter must have a distinct identifier, those identifiers are as good as any.


Regarding your edit, that is limitation of the preprocessor. An empty token sequence is a valid argument to a macro. So it really is a single argument, because __VA_ARGS__ , (note the comma) becomes such an argument.

It's quite a known issue, so much so that C++20 added the __VA_OPT__ preprocessor macro. It allows to add the comma conditionally. On C++20, the implementation can be fixed to work as expected like this:

#define CV_VA_NUM_ARGS(...)      CV_VA_NUM_ARGS_HELPER(__VA_ARGS__ __VA_OPT__(,) 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
1

There is nothing special about _1, _2 etc. You can have i, j, k in their place.

The value of N depends on the number of arguments you have passed because N becomes the 11th element in the macro expansion (because of the 10 place holders).

So for CV_VA_NUM_ARGS(a,b,c), the 11th element is 3. For CV_VA_NUM_ARGS(a,b,c,d,e,f), the 11th element is 6 and so on.

Note that with this particular code you can count only up to 10 arguments. Using more than 10 arguments will generate a compiler error.

P.W
  • 26,289
  • 6
  • 39
  • 76