While learning about how macros work, I tried defining a macro that works with different datatypes without having to define it multiple types. I used _Generic
to make this task easier. However, after it didn't work, even after making changes multiple times, I asked deepseek to figure it out, which eventually came up with this:
#define FOO(var) _Generic((var), \
int: printf(#var " = %d\n", (int)(var)), \
double: printf(#var " = %lf\n", (double)(var)), \
long: printf(#var " = %ld\n", (long)(var)), \
char: printf(#var " = '%c'\n", (char)(var)), \
float: printf(#var " = %f\n", (float)(var)), \
default:printf(#var " = unknown type\n"))
int main(void) {
int a = 3;
FOO(a); // correctly prints "a = 3"
}
In other code examples in the cpp-reference and on SO I've never seen anyone having to typecast to make this work.
Removing the typecasts (... int: printf(#var " = %d\n",(var)), \
...) shows a warning "Format specifies type 'long' but argument has type 'int'" and gives compile warning (-Wall
enabled):
ue_05.c: In function ‘main’:
ue_05.c:47:14: warning: format ‘%lf’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
47 | FOO(a);
| ^
ue_05.c:27:25: note: in definition of macro ‘FOO’
27 | double: printf(#var " = %lf\n", (var)), \
| ^~~
ue_05.c:47:14: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 2 has type ‘int’ [-Wformat=]
47 | FOO(a);
| ^
ue_05.c:28:25: note: in definition of macro ‘FOO’
28 | long: printf(#var " = %ld\n", (var)), \
| ^~~
ue_05.c:47:14: warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
47 | FOO(a);
| ^
ue_05.c:30:25: note: in definition of macro ‘FOO’
30 | float: printf(#var " = %f\n", (var)), \
I have not been able to find out why the typecast is required here and deepseek also was not able to clear things up.
Why is the typecast necessary here and where is it documented?
While learning about how macros work, I tried defining a macro that works with different datatypes without having to define it multiple types. I used _Generic
to make this task easier. However, after it didn't work, even after making changes multiple times, I asked deepseek to figure it out, which eventually came up with this:
#define FOO(var) _Generic((var), \
int: printf(#var " = %d\n", (int)(var)), \
double: printf(#var " = %lf\n", (double)(var)), \
long: printf(#var " = %ld\n", (long)(var)), \
char: printf(#var " = '%c'\n", (char)(var)), \
float: printf(#var " = %f\n", (float)(var)), \
default:printf(#var " = unknown type\n"))
int main(void) {
int a = 3;
FOO(a); // correctly prints "a = 3"
}
In other code examples in the cpp-reference and on SO I've never seen anyone having to typecast to make this work.
Removing the typecasts (... int: printf(#var " = %d\n",(var)), \
...) shows a warning "Format specifies type 'long' but argument has type 'int'" and gives compile warning (-Wall
enabled):
ue_05.c: In function ‘main’:
ue_05.c:47:14: warning: format ‘%lf’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
47 | FOO(a);
| ^
ue_05.c:27:25: note: in definition of macro ‘FOO’
27 | double: printf(#var " = %lf\n", (var)), \
| ^~~
ue_05.c:47:14: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 2 has type ‘int’ [-Wformat=]
47 | FOO(a);
| ^
ue_05.c:28:25: note: in definition of macro ‘FOO’
28 | long: printf(#var " = %ld\n", (var)), \
| ^~~
ue_05.c:47:14: warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
47 | FOO(a);
| ^
ue_05.c:30:25: note: in definition of macro ‘FOO’
30 | float: printf(#var " = %f\n", (var)), \
I have not been able to find out why the typecast is required here and deepseek also was not able to clear things up.
Why is the typecast necessary here and where is it documented?
Share Improve this question edited Mar 8 at 20:51 LLL asked Mar 8 at 14:33 LLLLLL 771 silver badge6 bronze badges 9 | Show 4 more comments1 Answer
Reset to default 6the #define
simply edits the code nothing more.
when you write
#define FOO(var) _Generic((var), \
int: printf(#var " = %d\n", (var)), \
double: printf(#var " = %lf\n",(var)), \
long: printf(#var " = %ld\n",(var)), \
char: printf(#var " = '%c'\n",(var)), \
float: printf(#var " = %f\n", (var)), \
default:printf(#var " = unknown type\n"))
int main(void) {
int a = 3;
FOO(a);
}
After the preprocessing is done it becomes behind the scenes
int main(void) {
int a = 3;
_Generic((a),
int: printf("a" " = %d\n", (a)),
double: printf("a" " = %lf\n", (a)),
long: printf("a" " = %ld\n", (a)),
char: printf("a" " = '%c'\n",(a)),
float: printf("a" " = %f\n",(a)),
default:printf("a" " = unknown type\n"));
}
When a
is an int
the compiler still sees
printf("a" " = %lf\n", (a))
and the other statements as well. thus, gives you the warning complaining about the variable a
being int
while printf
expecting float
even though that statement will not be executed.
typecasting just makes the compiler consider a
as float
(in the float:
part of _Generic
and so on in the rest of it) thus not complaining about it.
by the way you probably have the -Wall
on.
Edit:
To get rid of the warning do this instead
#include <stdio.h>
#define FOO(var) printf( _Generic((var),\
int: #var " = %d\n",\
double: #var " = %lf\n",\
long: #var " = %ld\n",\
char: #var " = '%c'\n",\
float: #var " = %f\n",\
default: #var " = unknown type\n")\
, var )
int main() {
float a = 2.4f;
FOO(a);
}
_Generic
inside theprintf
, to select the format string, instead of having theprintf
inside the_Generic
. – Eric Postpischil Commented Mar 8 at 15:55_Generic
? Which compiler are you using ? What options are you using? Are you compiling for at least C11? It looks a bit as if the compiler doesn't understand_Generic
because the standard makes it clear "None of the expressions from any other generic association of the generic selection is evaluated." So, the compiler should not be complaining about the unused generic selections. But if you wrote that code in a compiler that didn't understand_Generic
, you might get such warnings. OTOH, the type names as labels might cause errors in a compiler. – Jonathan Leffler Commented Mar 8 at 17:03-Wall
: godbolt./z/aqfqYoEG8. The compiler certainly does understand_Generic
in general (or there would be fatal errors). It's just being overly aggressive by giving warnings about code which is not actually evaluated. Arguably this is a minor bug in GCC, or at least an enhancement that they should consider. – Nate Eldredge Commented Mar 8 at 20:07