Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incompatible pointer types passing in _Generic macro

Tags:

The following code generates 2 warnings which are described in the question's title.

#include <stdio.h>

static void _print_f(float *f){printf("float : %f\n", *f);}
static void _print_i(int *i)  {printf("int   : %d\n", *i);}

#define print(num) _Generic((num), \
    int*   : _print_i(num),        \
    float* : _print_f(num))


int main(void)
{
    print((&(int){10}));
    print((&(float){10.f}));

    return 0;
}

OUTPUT:

int   : 10
float : 10.000000

I know, this macro could be written like the following:

#define print(num) _Generic((num), \
    int*   : _print_i,             \
    float* : _print_f)(num)

and in that case, there won't be any warnings, however my example is a dummy snippet which I wrote to demonstrate the problem. In my real code base I chose the former solution, because some other "default" but type specific arguments needs to be passed to the selected function.

So the question is: Even if the macro is working as it should, and the output is exactly what I expect, why are the warnings generated?


Flags and Environment:

/* Mac OS X 10.9.4
   Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn) */
cc -Wall -v -g -std=c11 -fmacro-backtrace-limit=0 -I/usr/local/include
   -c -o build/tmp/main.o main.c

Update1:

I forgot to paste the full traceback! Here is the first one:

main.c:39:11: warning: incompatible pointer types passing 'int *'
to parameter of type 'float *' [-Wincompatible-pointer-types]
    print((&(int){10}));
          ^~~~~~~~~~~~
main.c:31:23: note: expanded from macro 'print'
    float* : _print_f(num))
                      ^
main.c:26:29: note: passing argument to parameter 'f' here
static void _print_f(float *f){printf("float : %f\n", *f);}
                            ^

And here is the second one:

main.c:40:11: warning: incompatible pointer types passing 'float *'
to parameter of type 'int *' [-Wincompatible-pointer-types]
    print((&(float){10.f}));
          ^~~~~~~~~~~~~~~~
main.c:30:23: note: expanded from macro 'print'
    int*   : _print_i(num),        \
                      ^
main.c:27:27: note: passing argument to parameter 'i' here
static void _print_i(int *i)  {printf("int   : %d\n", *i);}
                          ^

Update2:

Until the developers of clang fix this bug, here is an ugly piece of workaround to mute the warnings, which will work if all keys in the assoc-list are types, OR all are pointers to types; and will fail if types AND pointers to types are in the keys too:

/* HACK: re-casting pointers to mute warnings */
#define print(num) _Generic((num), \
    int*   : _print_i((int*)num),  \
    float* : _print_f((float*)num))
like image 242
Peter Varo Avatar asked Jul 14 '14 18:07

Peter Varo


1 Answers

This is not a bug in clang, but unfortunately what the C11 standard requires. All branches of a _Generic primary expression must be a valid expressions, and thus valid under all circumstances. The fact that only one of the branches will ever be evaluated, is not related to this.

Your alternative version is what C11 foresees for situations as this: chose the function (and not the evaluated call) as a result of the type generic expression, and apply that function to the arguments.

like image 69
Jens Gustedt Avatar answered Nov 01 '22 20:11

Jens Gustedt