a2i(): Implement default arguments with macro magic#1410
a2i(): Implement default arguments with macro magic#1410alejandro-colomar wants to merge 3 commits intoshadow-maint:masterfrom
Conversation
|
For the curious, here's a small example of how this preprocessor magic works: #include <a2i.h>
#define VA_IFNOT_(...) __VA_ARGS__
#define VA_IFNOT_1(...)
#define VA_IFNOT(cond, ...) VA_IFNOT_ ## cond (__VA_ARGS__)
#define DEFAULT_(def, ...) \
( \
/* default: */ VA_IFNOT(__VA_OPT__(1), def) \
/* override: */ __VA_OPT__(__VA_ARGS__) \
)
#define DEFAULT(def, ovr) DEFAULT_(def, ovr)
#define foo(a, b) bar(DEFAULT(42, a), DEFAULT(43, b))
int bar(int, int);
int
main(void)
{
long l;
l = foo(7, 111);
l = foo(77,);
l = foo(,11);
l = foo(,);
l = foo(); // Too few arguments
l = foo(,,); // Too many arguments
}alx@devuan:~/tmp$ gcc -Wall -Wextra -E def.c | grepc main
def.c:30:17: error: macro ‘foo’ requires 2 arguments, but only 1 given
30 | l = foo(); // Too few arguments
| ^
def.c:15:9: note: macro ‘foo’ defined here
15 | #define foo(a, b) bar(DEFAULT(42, a), DEFAULT(43, b))
| ^~~
def.c:31:19: error: macro ‘foo’ passed 3 arguments, but takes just 2
31 | l = foo(,,); // Too many arguments
| ^
def.c:15:9: note: macro ‘foo’ defined here
15 | #define foo(a, b) bar(DEFAULT(42, a), DEFAULT(43, b))
| ^~~
(standard input):int
main(void)
{
long l;
l = bar(( 7 ), ( 111 ));
l = bar(( 77 ), ( 43 ));
l = bar(( 42 ), ( 11 ));
l = bar(( 42 ), ( 43 ));
l = foo;
l = foo;
} |
5e3800f to
6316a83
Compare
6316a83 to
7efe763
Compare
This macro will allow writing APIs that have default values. Signed-off-by: Alejandro Colomar <[email protected]>
And use them to implement str2i(). Signed-off-by: Alejandro Colomar <[email protected]>
This removes chances of typos. It would be possible to accidentally pass for example ULONG_MAX or INT_MAX where LONG_MAX would be appropriate. Because these macros are able to automate the right value, don't specify it manually. Thus, when we specify it manually, it will be in those places where we use a non-default value, which is less common. That will make it easier to review those few places. Signed-off-by: Alejandro Colomar <[email protected]>
7efe763 to
6c779df
Compare
|
FWIW, I find the empty "," arguments in the callers very distressing to read. I'm fine with wild preprocessor macro syntax, but it should leave the resulting usage looking like normal C. |
The alternative would be allowing the arguments to not appear at all. This is what the kernel does with strscpy(), which accepts 2 or 3 arguments. However, I think that's more error-prone, as one may accidentally omit an argument, and that could introduce a bug (and the compiler couldn't diagnose anything in the case of this API, unlike in strscpy()). The empty |
This needs some ISO C23 feature (
__VA_OPT__), but we're already using that in stprintf_a(), which we have since 4.15.0, so it should be fine.I've tested this in liba2i, and it works like a charm.
Revisions:
v1b
v1c
v1d