7

I am trying to generate overloaded functions using _Generic macro in C11, and I have stopped on zero arguments function support, e.g:

#define msg(_1) _Generic((_1), char*: msg_string, default: msg_none)(_1)

char* msg_none(void){
    return moo_string("Have a nice day!");
}

char* msg_string(char* message){
    int msglen = strlen(message);
    char* result = malloc(msglen + 3);
    sprintf(result, "<%s>\n", message);
    return result;
}

For now compiling and running:

printf("%s",msg("hello!"));

goes without any problem, but:

printf("%s",msg());

throws error:

main.c:7:17: error: expected expression
printf("%s",msg());

I am using:

 clang --version
 clang version 3.5.0 (tags/RELEASE_350/final)
 Target: x86_64-pc-linux-gnu
 Thread model: posix

GCC throws:

main.c:7:5: warning: implicit declaration of function ‘_Generic’

so I understand _Generic is not supported this version of gcc:

gcc --version
gcc (Gentoo 4.8.3 p1.1, pie-0.5.9) 4.8.3

Is my problem even solvable or I just overestimate capabilities of _Generic, or I just need to upgrade my compilers to use this options properly ?

mucka
  • 1,286
  • 3
  • 20
  • 36
  • 1) `"hello!"` might match `char[7]` 2) `msg_none(_1)` but `msg_none(void)` 3) `char* result = malloc(msglen + 3);` : `+3` should be `+4` for NUL. – BLUEPIXY Apr 22 '15 at 15:12
  • 1
    `_Generic` GCC 4.9 [C11Status](https://gcc.gnu.org/wiki/C11Status) – BLUEPIXY Apr 22 '15 at 15:21
  • @BLUEPIXY, today they shipped gcc 5.1, which has C11 (+gnu extensions) as a default. – Jens Gustedt Apr 22 '15 at 22:12
  • Sory, I actually used `printf("%s",msg((char*)"hello!"));` so it worked anyway, but `+0` solution from below solves even this problem. – mucka Apr 23 '15 at 06:56

3 Answers3

3

C has variadic macros that may receive zero or more arguments

#define msg(...) _Generic((__VA_ARGS__+0), char*: msg_string, default: msg_none)(__VA_ARGS__)

here the +0 in addition ensures that an array to pointer conversion is performed for your string argument, which you seem to assume.

The later is important since gcc and clang currently differ in their behavior if the selection expression is an array.

Edit: You probably also would want your macro to work if someone passes in a char const* so you should add that case, too.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 1
    Huh, I didn't know you could do that with _Generic. `(__VA_ARGS__+0)` this doesn't really work with more than one argument though. – 2501 Apr 22 '15 at 15:52
  • But you did answer the question so...+1 – 2501 Apr 22 '15 at 15:56
  • Thank you very much ! This is exacly what I was lookign for, with `+0` the `printf("%s",msg((char*)"hello!"));` is reduced to `printf("%s",msg("hello!"));`, zero argument case works and even `#define msg(_1) _Generic((_1+0), char*: msg_string, default: msg_none)(_1)` this particular syntax works. – mucka Apr 23 '15 at 07:02
2

Your problem is not directly related to _Generics. You simply defined you macro #define msg(_1) with an argument, therefore you have to pass an argument.

If you don't rely on compiler extensions you cannot pass zero or more arguments to a _Generic macro. You will have to choose between zero or one arguments as shown here or, 1 or more.

This is my solution for any macro combination, but it involves a dummy argument. You can define you own type that will serve as an indicator of an emtpy macro

typedef struct
{
    int unused ;
} emtpy ;

const empty msg_empty = { 0 } ;

char* msg_none(empty e)
{
    ( void )e ;
    return moo_string("Have a nice day!");
}

#define msg(_1) _Generic((_1), char*: msg_string, empty : msg_none)(_1)

And then call it with:

msg( msg_empty ) ;

Which will call the msg_none function.

Community
  • 1
  • 1
2501
  • 25,460
  • 4
  • 47
  • 87
1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define dummy void
#define ARGS_AUX(_0, VAR, ...) VAR
#define ARGS(...) ARGS_AUX(dummy, ##__VA_ARGS__, NULL) //gnu extensions
#define MSG(var) (_Generic(var, char*: msg_string(var), default: msg_none()))
#define msg(...) MSG(ARGS(__VA_ARGS__)) //MSG(NULL) when no argument

char *moo_string(const char *s){
    return (char*)s;
}

char* msg_none(void){
    return moo_string("Have a nice day!");
}

char* msg_string(char* message){
    int msglen = strlen(message);
    char* result = malloc(msglen + 4);
    sprintf(result, "<%s>\n", message);
    return result;
}

int main(void){
    printf("%s\n", msg());//Have a nice day!
    printf("%s\n", msg((char*)"hello!"));//<hello!>, type is char[7] when no cast
    return 0;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70