最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

macros - _Generic in C needs typecasting? - Stack Overflow

programmeradmin2浏览0评论

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
  • 3 it is not a compiler error, but just warning and it is documented here, typecasting is not required here, because correct data type will be supplied anyway to actually chosen branch, so typecasting is just a way to suppress warning in unused branches – Iłya Bursov Commented Mar 8 at 15:14
  • 1 Put the _Generic inside the printf, to select the format string, instead of having the printf inside the _Generic. – Eric Postpischil Commented Mar 8 at 15:55
  • Does the compiler actually understand _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
  • 2 @JonathanLeffler: Reproducible with GCC 14 -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
  • 1 I just wrote a little Q&A about "X-macros" What are X macros and when to use them? which contains an example of how you can use X-macros in combination with _Generic to generate the appropriate conversion specifier. In the advanced example there, I outsourced the printing to a type-specific function, which would be another way to get rid of the warning. – Lundin Commented Mar 10 at 14:15
 |  Show 4 more comments

1 Answer 1

Reset to default 6

the #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);
}
发布评论

评论列表(0)

  1. 暂无评论