是否可以使用C11的_Generic实现GNU C的typeof(x)?

时间:2016-10-20 00:56:35

标签: c generics gcc gnu c11

要在C和C ++中编译一些代码,我会在以下几个地方使用它:

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

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

在C中,这会编译为char* a = (char *) malloc(4),其中演员阵容是完全不必要的,但在C ++中void *未被隐式提升为char *,如果演员阵容不存在则会发出错误。

当我可以在GCC或Clang上使用-std=gnu11进行编译时,这也是一样,但是当我想将我的代码编译为ISO C11时呢?我以为我可以使用C11的_Generic来实现typeof(x)来投射某些类型:

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

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

  return a;
}

但无论在gettype(x)的声明中给出a中定义的类型,

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说这条线扩展得很好:

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

是否有一些我缺少的语法,或者在C中根本不可能使用_Generic生成强制转换代码?

3 个答案:

答案 0 :(得分:3)

不,这是不可能的。 (现在看别人证明我错了!)

_Generic表达式中,每个泛型关联都是

type-name assignment-expression

default assignment-expression

它不能是类型名称或扩展为类型名称的东西。特别是,尽管在编译时解析了_Generic表达式,但不是宏。最后的结果总是表达。

而且我不相信还有其他方法可以在标准C中做你想做的事。

答案 1 :(得分:2)

问题是您不能在通用选择中包含部分表达式。可能的解决方法是在其中加入完整的表达式:

#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;
}

答案 2 :(得分:0)

我刚想通了..如果在C中使用Visual C ++,而不是C ++,如果在三元表达式中有两个不相关的非void指针结果类型,则三元表达式的类型是第一个。

这很有用。

所以,在一个角落,我被绘制到我有一堆C代码的地方,我需要在一个宏内部投射一个void *的类型,不应该双重评估...

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