Whilst reviewing some code here, I saw a macro where the argument was required to be a string literal.
And I discovered this macro (from Jenn's Gustedt Modern C) which claimed to fulfill the following constraints:
- Is a character pointer
- Must be non-null
- Must be immutable
- Must be 0-terminated
If I have:
#define SV(str) (sizeof(str) - 1)
int main(void)
{
static const char *const s = "hello";
char word[] = "hello";
double a = 0.0f;
double *d = &a;
printf("%zu\n", SV(NULL));
printf("%zu\n", SV(word));
printf("%zu\n", SV(d));
printf("%zu\n", SV(s));
return 0;
}
The code compiles correctly and issues no errors or warnings, and the output is:
7
5
7
7
But that changes if I use empty string literals in its expansion "" str "":
#define SV(str) (sizeof("" str "") - 1)
int main(void)
{
static const char *const s = "hello";
char word[] = "hello";
double a = 0.0f;
double *d = &a;
printf("%zu\n", SV(NULL));
printf("%zu\n", SV(word));
printf("%zu\n", SV(d));
printf("%zu\n", SV(s));
return 0;
}
Now if I compile this, I get:
macro_str.c: In function ‘main’:
macro_str.c:4:33: error: called object is not a function or function pointer
4 | #define SV(str) (sizeof("" str "") - 1)
| ^~
macro_str.c:19:21: note: in expansion of macro ‘SV’
19 | printf("%zu\n", SV(NULL));
| ^~
macro_str.c:4:40: error: expected ‘)’ before string constant
4 | #define SV(str) (sizeof("" str "") - 1)
| ~ ^~
macro_str.c:19:21: note: in expansion of macro ‘SV’
19 | printf("%zu\n", SV(NULL));
| ^~
macro_str.c:20:24: error: expected ‘)’ before ‘word’
20 | printf("%zu\n", SV(word));
| ^~~~
macro_str.c:4:36: note: in definition of macro ‘SV’
4 | #define SV(str) (sizeof("" str "") - 1)
| ^~~
macro_str.c:4:32: note: to match this ‘(’
4 | #define SV(str) (sizeof("" str "") - 1)
| ^
macro_str.c:20:21: note: in expansion of macro ‘SV’
20 | printf("%zu\n", SV(word));
| ^~
macro_str.c:21:24: error: expected ‘)’ before ‘d’
21 | printf("%zu\n", SV(d));
| ^
macro_str.c:4:36: note: in definition of macro ‘SV’
4 | #define SV(str) (sizeof("" str "") - 1)
| ^~~
macro_str.c:4:32: note: to match this ‘(’
4 | #define SV(str) (sizeof("" str "") - 1)
| ^
macro_str.c:21:21: note: in expansion of macro ‘SV’
21 | printf("%zu\n", SV(d));
| ^~
macro_str.c:22:24: error: expected ‘)’ before ‘s’
22 | printf("%zu\n", SV(s));
| ^
macro_str.c:4:36: note: in definition of macro ‘SV’
4 | #define SV(str) (sizeof("" str "") - 1)
| ^~~
macro_str.c:4:32: note: to match this ‘(’
4 | #define SV(str) (sizeof("" str "") - 1)
| ^
macro_str.c:22:21: note: in expansion of macro ‘SV’
22 | printf("%zu\n", SV(s));
| ^~
make: *** [<builtin>: macro_str] Error 1
My question is: Are there any edge-cases or loopholes where this would fail? If so, how can I modify it to avoid them?
These compile without complaint: