Counting arguments#
Work In Progress
This page is a draft
If you’ve made it this far, congratulations ! You’ve completed the introduction.
We will now start abusing macros and delve into black magic.
The pivotal trick is going to be able to count the number of arguments given to a variadic macro.
Objective#
We want a macro ARG_COUNT(...)
that expands to an integer literal representing the number of arguments passed to it.
It shall be usable from other macros
int zero = ARG_COUNT();
int one = ARG_COUNT(A);
int three = ARG_COUNT(A, B, C);
#define FOO(...) printf("FOO was called with %i arguments", ARG_COUNT(__VA_ARGS__))
FOO("one", "two");
int zero = 0;
int one = 1;
int three = 3;
printf("Foo was called with %i arguments", 2);
Solution#
Let’s look at the simplest solution:
1#include <stdio.h>
2
3#define ARG_10(A, B, C, D, E, F, G, H, I, J, ...) J
4#define ARG_COUNT(...) ARG_10(__VA_ARGS__ __VA_OPT__(,) 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
5
6int main()
7{
8 printf("() : %i\n", ARG_COUNT());
9 printf("(foo) : %i\n", ARG_COUNT(foo));
10 printf("('A', 'B') : %i\n", ARG_COUNT('A', 'B'));
11 printf("(1,2,3,4,5): %i\n", ARG_COUNT(1, 2, 3, 4, 5));
12}
1#include <stdio.h>
2
3#define ARG_10(A, B, C, D, E, F, G, H, I, J, ...) J
4#define ARG_COUNT(...) ARG_10(__VA_ARGS__ __VA_OPT__(,) 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
5
6int main()
7{
8 printf("() : %i\n", ARG_COUNT());
9 printf("(foo) : %i\n", ARG_COUNT(foo));
10 printf("('A', 'B') : %i\n", ARG_COUNT('A', 'B'));
11 printf("(1,2,3,4,5): %i\n", ARG_COUNT(1, 2, 3, 4, 5));
12}
int main()
{
printf("() : %i\n", 0);
printf("(foo) : %i\n", 1);
printf("('A', 'B') : %i\n", 2);
printf("(1,2,3,4,5): %i\n", 5);
}
() : 0
(foo) : 1
('A', 'B') : 2
(1,2,3,4,5): 5
Two macros are involved:
The
ARG_10
macro takes any number of arguments, and expands to the tenth argument.The
ARG_COUNT
macro takes any number of arguments, forwards them toARG_10
, adding the 10 arguments9, 8, 7, 6, 5, 4, 3, 2, 1, 0
.
We can then see that if no arguments is passed to ARG_COUNT
, the 10th argument is 0
, and for every argument added, the extra arguements are pushed to the right.
It is quite similar to the optional argument trick shown in the previous chapter: a variadic macro allows the user not to provide enough arguement to another macro, that in turn discards extraneous arguments.
Usage example#
OK, great, we have a macro that can count the number of arguments given to it. What does this enable us to do ?
1#include <stdio.h>
2
3#include "blackmagic/token.h" // STRINGIZE
4
5#define ARG_10(A, B, C, D, E, F, G, H, I, J, ...) J
6#define ARG_COUNT(...) ARG_10(__VA_ARGS__ __VA_OPT__(,) 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
7
8#define SPELL_0 "zero"
9#define SPELL_1 "one"
10#define SPELL_2 "two"
11#define SPELL_3 "three"
12#define SPELL_4 "four"
13#define SPELL_5 "five"
14
15#define SPELL_(N) SPELL_ ## N
16#define SPELL(N) SPELL_(N)
17
18#define FOO(...) printf("FOO was called with " SPELL(ARG_COUNT(__VA_ARGS__)) " (" STRINGIZE(ARG_COUNT(__VA_ARGS__)) ") arguments\n")
19
20int main()
21{
22 FOO();
23 FOO(1, 2);
24 FOO(1, 2, 3, 4, 5);
25}
1#include <stdio.h>
2
3#include "blackmagic/token.h" // STRINGIZE
4
5#define ARG_10(A, B, C, D, E, F, G, H, I, J, ...) J
6#define ARG_COUNT(...) ARG_10(__VA_ARGS__ __VA_OPT__(,) 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
7
8#define SPELL_0 "zero"
9#define SPELL_1 "one"
10#define SPELL_2 "two"
11#define SPELL_3 "three"
12#define SPELL_4 "four"
13#define SPELL_5 "five"
14
15#define SPELL_(N) SPELL_ ## N
16#define SPELL(N) SPELL_(N)
17
18#define FOO(...) printf("FOO was called with " SPELL(ARG_COUNT(__VA_ARGS__)) " (" STRINGIZE(ARG_COUNT(__VA_ARGS__)) ") arguments\n")
19
20int main()
21{
22 FOO();
23 FOO(1, 2);
24 FOO(1, 2, 3, 4, 5);
25}
int main()
{
printf("FOO was called with "
"zero"
" ("
"0"
") arguments\n");
printf("FOO was called with "
"two"
" ("
"2"
") arguments\n");
printf("FOO was called with "
"five"
" ("
"5"
") arguments\n");
}
FOO was called with zero (0) arguments
FOO was called with two (2) arguments
FOO was called with five (5) arguments