C11 _Generic: how to deal with string literals?

2019-01-18 02:39发布

问题:

Using the _Generic feature in C11, how do you deal with string literals?

For instance:

#include <stdio.h>
#define foo(x) _Generic((x), char *: puts(x))

int main()
{
    foo("Hello, world!");
    return 0;
}

gives this error on clang:

controlling expression type 'char [14]' not compatible with any generic association type

Replacing char * with char[] gives me

error: type 'char []' in generic association incomplete

The only ways (to my knowledge) of getting this to compile are:

  1. Cast the string literal to an appropriate type. This is ugly and (in my view) defeats the point of _Generic in the first place.
  2. Use char[14] as the type specifier. You have got to be kidding me...

My assumption was that arrays would decay to pointers when passed to _Generic, but evidently not. So, how do I use _Generic with string literals? Are those the only two options?

I'm using clang 3.2 on Debian. Unfortunately, it's the only compiler I have access to that supports this feature, so I can't tell if it's a compiler bug or not.

回答1:

Here is a solution:

#include <stdio.h>
#define foo(x) _Generic((0,x), char*: puts(x))

int main()
{
    foo("Hello, world!");
    return 0;
}

This compiles and produces:

$ clang t.c && ./a.out 
Hello, world!

It is somewhat lame, but I did not find any better way to make x decay to a pointer to char nor to match its type in the fuzzy fashion that you require, with Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn).

According to this blog post by Jens Gusted, GCC's behavior is different (in GCC, strings automatically decay to pointer in a _Generic context, apparently).

By the way, in C, the type of a string literal is array of char, not of const char. Rejecting char [] as type-name in a generic-association is not a compiler bug:

A generic selection shall have no more than one default generic association. The type name in a generic association shall specify a complete object type other than a variably modified type. (6.5.1.1:2 with my emphasis)



回答2:

I have figured out a way to avoid using the clever (0,x) trick.

If you use a string literal the type is char[s] , where s is the size of the string literal.

How do you get that size?, use sizeof operator:

#include <stdio.h>

#define Test( x )   _Generic( ( x ) ,   char*: puts ,                   \
                                        const char*: puts ,             \
                                        const char[sizeof( x )]: puts , \
                                        char[sizeof( x )]: puts )( x )

int main(void) 
{

    char str[] = "This" ;
    Test( str ) ;

    Test( "works" ) ;

    char str2[10] = "!!!" ;
    Test( str2 ) ;

return 0;
}

I tried compiling it with clang and Pelles and it worked.

The only problem you still have to cast variable length arrays.

After trying some more I found another analogue way of doing what Pascal Cuoq did, use &* operators:

#include <stdio.h>
#define foo(x) _Generic( ( &*(x) ), char*: puts , const char*: puts )( x )

int main()
{
    foo("Hello, world!");
    return 0;
}


标签: c clang c11