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?

cat
  • 3,888
  • 5
  • 32
  • 61
  • 2
    Although `_Generic` is hardly usable outside a macro, it cannot be part of the preprocessor. Thus it cannot provide text replacement. – too honest for this site Oct 20 '16 at 01:43
  • It would be better to provide full C compatibility and no C++ compatibility, than lacking C and C++ compatibility. – user694733 Oct 20 '16 at 07: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 Oct 20 '16 at 10:21
  • Why is there a need to compile the same code using compilers for two different languages? Just being curious. – unwind Oct 21 '16 at 11:09
  • @unwind On Unix-likes, building as C is acceptable because on these platforms, it is trivial to get a ISO/IEC C99/C11 compiler and use it with `make` etc. However, if I wsnt to be able to build my code on Windows, it is a pain to install GCC, Clang or anothrr ISO C compiler, even with mingw/cygwin. For this platform, my project must compile as C++11 because MSVC, the only real option, only supports ISO C up to 90, which is way too old. I won't write it in C++ in the first place because that is unecessary complexity (er, I dislike C++). – cat Oct 21 '16 at 11:17
  • 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 Oct 21 '16 at 11:20
  • 1
    @cat You could give Codeblocks a try, the default installation is simple to install. [See this](http://stackoverflow.com/questions/34163283/how-to-use-c11-standard-in-codeblocks/34220462#34220462) for help how to make it understand C11. – Lundin Oct 21 '16 at 11:26
  • @Lundin Comments are not for extended offtopic discussion, and I'm ok with my intelligent text editor + teletypewriter :) – cat Oct 21 '16 at 14:15

3 Answers3

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • It is as I feared! Ah well, that's what I get for thinking C'd be ahead of 1979 :) – cat Oct 20 '16 at 01: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 Oct 21 '16 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 Oct 21 '16 at 11:12
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;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
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
Jay K
  • 129
  • 3