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

Is it possible to implement GNU C's typeof(x) with C11's _Generic?

为了在 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 生成转换代码?

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

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

类型名称 : 赋值表达式

default : 赋值表达式

它不能是类型名称或扩展为类型名称的内容。特别是,虽然 _Generic 表达式在编译时解析,但它 不是 宏。最终结果总是一个表达式。

而且我不相信有任何其他方法可以在标准 C 中执行您想要的操作。

问题是您不能在泛型选择中包含部分表达式。一种可能的解决方法是在其中放入一个完整的表达式:

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

我刚刚弄明白..如果是 Visual C++,在 C 中,而不是 C++ 中,如果三元表达式中有两个不相关的非空指针结果类型,则三元表达式的类型是第一个。

这很有用。

所以,在一个角落里,我被画成一堆 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