25

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.

Michael Rawson
  • 1,905
  • 1
  • 15
  • 25
  • 5
    Note that the [N2176 draft for the upcoming C17](http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf) requires array-to-pointer decay of the controlling expression of generic selections in 6.5.1.1, so string literals would be treated as char*: _"The type of the controlling expression is the type of the expression as if it had undergone an lvalue conversion (footnote: lvalue conversion drops type qualifiers), array to pointer conversion, or function to pointer conversion."_ – dpi Jul 14 '18 at 16:23

3 Answers3

21

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 Gustedt, 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)

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • Thank you, not a bad suggestion, if annoying. About `char` versus `const char`: yup, but `_Generic` adds `const` fuzzily, so I thought I'd take advantage of that. I'll edit the example. Cheers! – Michael Rawson Sep 17 '13 at 18:40
  • 3
    @MichaelRawson 6.5.1.1 continues: “If a generic selection has a generic association with a type name that is **compatible** with the type of the controlling expression, then the result expression of the generic selection is the expression in that generic association.” and `const char *` is not compatible with `char *`. I would not expect any const-fuzziness. My clang does not allow me any. – Pascal Cuoq Sep 17 '13 at 18:45
  • my apologies, I must have missed an asterisk somewhere or something. `clang` behaves as you (and the standard) describe. – Michael Rawson Sep 17 '13 at 18:50
15

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;
}
Community
  • 1
  • 1
2501
  • 25,460
  • 4
  • 47
  • 87
  • 5
    `&*` is another “do nothing” context that, like `0,` forces pointer decay, but it won't work if `x` is not a pointer (say if you want to use `_Generic` for both floats and string literals). I am quite impressed by your use of `sizeof` though. – Pascal Cuoq Nov 10 '14 at 22:39
  • @PascalCuoq You're right, (0,x) is the only way. I only did all of this because the compiler gives me a warning: *Expression with no effect removed.* if I use (0,x). – 2501 Nov 10 '14 at 22:41
  • 1
    Well if the statement that precedes the `_Generic` happens to be an expression, you can move it into the `_Generic`, transforming, say, `printf("hello world\n"); _Generic(x, …)` into `_Generic((printf("hello world\n"), x), …)`. You might also want to try whether `(((void)0), x)` makes the intent clear to the compiler. – Pascal Cuoq Nov 10 '14 at 22:59
  • @PascalCuoq Yup `( void )0` did it. I also tried your way on clang online http://gcc.godbolt.org/ and it gave an error, but changing the expression a little: `_Generic( ( (0,x)+0 )` solved the problem. – 2501 Nov 10 '14 at 23:02
  • Wouldn't *& work correctly for all types, also non pointer? – psprint Dec 30 '20 at 15:43
6

The behaviour of Clang was incorrect (C11 Defect report 481) until 3.7.1. It was fixed in Clang 3.8.0, released on March 8, 2016.

The Committee response to the DR 481 says the following:

This paper elicited a long and productive discussion. The committee agrees with the author of the _Generic proposal that the intent was that selecting on qualified types was explicitly to be avoided as was selecting on arrays by size. The intent of _Generic was to give C a mechanism to somewhat express the notion of “overloaded function” found in C++, and in particular a possible mechanism for implementors to use to implement the atomic type generic functions from section 7.17.7.

  • Thanks for the reference. Sad they changed it. Matching with sizeof like @2501 suggested would've been awesome. as it would've allowed the passing of the size of the array, effectively preventing pointer decay and all the potential bugs associated. – Born2Smile Jun 08 '23 at 00:04