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?

5
  • 1
    BTW, if you want to count the number of variadic arguments for more than 10, #define CV_VA_NUM_ARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value works in C++11. Commented Dec 19, 2018 at 10:49
  • @Hiroki: generally, it is done to dispatch on other MACRO depending of argument count, so this way "fail" Commented Dec 19, 2018 at 13:01
  • @Jarod42 thx but I still do not well understand your comment. Assuming such use cases, 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 :). Commented Dec 20, 2018 at 13:39
  • @Hiroki: I meant iterate over variadic MACRO as for example, Commented Dec 20, 2018 at 13:59
  • @Jarod42 Ah I see. many thx! Commented Dec 20, 2018 at 14:26

2 Answers 2

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)
Sign up to request clarification or add additional context in comments.

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.