是否可以使用 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
为了在 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