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_10macro takes any number of arguments, and expands to the tenth argument.The
ARG_COUNTmacro 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