10

To make some code compile in C and C++ I use this in a few places:

#ifdef __cplusplus 
    #define typeof(x) decltype(x) // works ok in most cases, except C++ reference types
#endif 

char* a = (typeof(a)) malloc(4);

In C, this compiles to char* a = (char *) malloc(4) where the cast is totally unecessary, but in C++ void * is not implicitly promoted to char * and an error is issued if a cast is not present.

This is just as well when I can compile with -std=gnu11 on GCC or Clang, but what when I want to make my code compile as ISO C11? I thought I could use C11's _Generic to implement typeof(x) to cast some types:

#define gettype(x) _Generic((x), \
  short:       (short ), \
  char:        (char  ), \
  char*:       (char *), \
  default:     (void *)  )

int main (void) {
  short a = (gettype(a)) 1;

  return a;
}

But no matter what type defined in gettype(x) is given in a's declaration,

typeof.h: In function ‘main’:
typeof.h:2:24: error: expected expression before ‘,’ token
   short:       (short ), \
                        ^
typeof.h:8:13: note: in expansion of macro ‘gettype’
   char a = (gettype(a)) 1;
             ^~~~~~~
typeof.h:8:25: error: expected ‘,’ or ‘;’ before numeric constant
   char a = (gettype(a)) 1;

gcc -E says that line expands just fine:

short a = (_Generic((a), short: (short ), char: (char ), char*: (char *), default: (void *) )) 1;                             ^

Is there some syntax I am missing, or is it simply not possible in C to generate cast code using _Generic?

8
  • 2
    Although _Generic is hardly usable outside a macro, it cannot be part of the preprocessor. Thus it cannot provide text replacement. Commented Oct 20, 2016 at 1:43
  • It would be better to provide full C compatibility and no C++ compatibility, than lacking C and C++ compatibility.
    – user694733
    Commented Oct 20, 2016 at 7:31
  • @user694733 thanks for your critique but I am confident this code ll only be compiled as C with GCC or Clang -- the C++ compat is for MSVC which barely supports C90.
    – cat
    Commented Oct 20, 2016 at 10:21
  • 1
    @cat Fine. I have all sorts of arguments about what to do in situations like these, but that's off-topic. I was just wondering what drove you in this direction. Thanks.
    – unwind
    Commented Oct 21, 2016 at 11:20
  • 1
    @cat You could give Codeblocks a try, the default installation is simple to install. See this for help how to make it understand C11.
    – Lundin
    Commented Oct 21, 2016 at 11:26

3 Answers 3

4

The problem is that you can't have a partial expression inside the generic selection. A possible work-around could be to put a full expression inside it:

#define cast(from, to) _Generic((from), \
  short:       (short) (to),            \
  char:        (char)  (to),            \
  char*:       (char*) (to),            \
  default:     (void*) (to))

int main (void) {
  short a = cast(a, 1);

  return 0;
}
4

No, it's not possible. (Now watch someone prove me wrong!)

In a _Generic expression, each generic-association is either

type-name : assignment-expression

or

default : assignment-expression

It can't be a type name or something that expands to a type name. In particular, though a _Generic expression is resolved at compile time, it is not a macro. The final result is always an expression.

And I don't believe there's any other way to do what you want in standard C.

3
  • It is as I feared! Ah well, that's what I get for thinking C'd be ahead of 1979 :)
    – cat
    Commented Oct 20, 2016 at 1:11
  • @cat 1979 doesn't have much to do with it. No language old or new allows partial expressions. This doesn't work for the same reason as int x = ; doesn't work - it is not a complete expression.
    – Lundin
    Commented Oct 21, 2016 at 11:07
  • @Lundin I mostly use homoiconic languages (eg LISP) for "real" applications so it's always kind of shocking to me that in C like languages, syntax elements that feel just like other expressions aren't.
    – cat
    Commented Oct 21, 2016 at 11:12
1

I just figured out..if Visual C++, in C, not C++, if you have two unrelated non-void pointer result types in a ternary expression, the type of the ternary expression is the first.

This can be useful.

So, in a corner I am painted into where I have a bunch of C code, I need to cast a void* to the type of something else, within a macro, that should not double evaluate...

typedef struct DesiredType { ... } DesiredType;
typedef struct ArbitraryType { ... } ArbitraryType;
ArbitraryType/*void*/* function_to_void_double_eval (void* a)
{
    ...
}


#if  defined(_MSC_VER) && !defined(__cplusplus)

#define MACRO(x) (0 ? (DesiredType*)0 : function_to_avoid_double_eval(x))

#else // assume gcc 

use typeof and temporaries in a macro

#endif
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.