c语言 - c11的"通用"关键字可以在gcc中使用吗_Static_assert



我理解使用 C11 的"泛型"做什么,我想在静态断言中使用它来保证两个用户定义的类型(typedefs(是相同的原始类型。

我制作了一个宏,将每个基元类型映射到枚举值,并验证它是否按预期工作。但是,当我尝试在静态断言中比较来自两种类型的两个结果宏的相等性时,我得到一个编译器错误。注释掉静态断言时,代码将按预期工作。

在评估泛型扩展之前,编译器似乎正在评估静态断言。是这样吗?我在哪里可以验证此行为?

示例代码

#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>

typedef enum
{
UTIL_TYPE_ENUM_BOOL,
UTIL_TYPE_ENUM_CHAR,
UTIL_TYPE_ENUM_SCHAR,
UTIL_TYPE_ENUM_UCHAR,
UTIL_TYPE_ENUM_SHORT,
UTIL_TYPE_ENUM_USHORT,
UTIL_TYPE_ENUM_INT,
UTIL_TYPE_ENUM_UINT,
UTIL_TYPE_ENUM_LONG,
UTIL_TYPE_ENUM_ULONG,
UTIL_TYPE_ENUM_LONG_LONG,
UTIL_TYPE_ENUM_ULONG_LONG,
UTIL_TYPE_ENUM_FLOAT,
UTIL_TYPE_ENUM_DOUBLE,
UTIL_TYPE_ENUM_LONG_DOUBLE,
UTIL_TYPE_ENUM_OTHER,
} UtilTypeEnum_t;

// returns the enumerated value representing a primitive type
#define UTIL_TYPE_GET_TYPE_ENUM(x) _Generic((x), 
_Bool: UTIL_TYPE_ENUM_BOOL, 
char: UTIL_TYPE_ENUM_CHAR, 
signed char: UTIL_TYPE_ENUM_SCHAR, 
unsigned char: UTIL_TYPE_ENUM_UCHAR, 
short int: UTIL_TYPE_ENUM_SHORT, 
unsigned short int: UTIL_TYPE_ENUM_USHORT, 
int: UTIL_TYPE_ENUM_INT, 
unsigned int: UTIL_TYPE_ENUM_UINT, 
long int: UTIL_TYPE_ENUM_LONG, 
unsigned long int: UTIL_TYPE_ENUM_ULONG, 
long long int: UTIL_TYPE_ENUM_LONG_LONG, 
unsigned long long int: UTIL_TYPE_ENUM_ULONG_LONG, 
float: UTIL_TYPE_ENUM_FLOAT, 
double: UTIL_TYPE_ENUM_DOUBLE, 
long double: UTIL_TYPE_ENUM_LONG_DOUBLE, 
default: UTIL_TYPE_ENUM_OTHER)

typedef int32_t foo_t;
typedef float bar_t;
// IF YOU COMMENT OUT THE STATIC ASSERT, THE CODE WILL COMPILE AND WORKS AS EXPECTED
_Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");
int main(void)
{
foo_t foo;
bar_t bar;
printf("foo's type = %dn", UTIL_TYPE_GET_TYPE_ENUM(foo));    
printf("bar's type = %dn", UTIL_TYPE_GET_TYPE_ENUM(bar));
if (UTIL_TYPE_GET_TYPE_ENUM(foo) != UTIL_TYPE_GET_TYPE_ENUM(bar))
{
printf("Not the same type!n");
}
else
{
printf("Same type!n");
}
return 0;
}
#endif 

编译器错误:

$ gcc foo.c
foo.c:35:49: error: expected expression before ‘,’ token
#define UTIL_TYPE_GET_TYPE_ENUM(x) _Generic((x), 
^
foo.c:77:17: note: in expansion of macro ‘UTIL_TYPE_GET_TYPE_ENUM’
_Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");
^~~~~~~~~~~~~~~~~~~~~~~
foo.c:35:49: error: expected expression before ‘,’ token
#define UTIL_TYPE_GET_TYPE_ENUM(x) (_Generic((x), 
^
foo.c:77:49: note: in expansion of macro ‘UTIL_TYPE_GET_TYPE_ENUM’
_Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");
^~~~~~~~~~~~~~~~~~~~~~~
foo.c:77:16: error: expression in static assertion is not an integer
_Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");

_Generic选择的参数必须是有效的 C 表达式,然后检查其类型。您提供一个类型名称,该名称根本不是表达式。

若要获取所需类型的表达式,可以使用复合文本:

_Static_assert((UTIL_TYPE_GET_TYPE_ENUM((foo_t){0})==UTIL_TYPE_GET_TYPE_ENUM((bar_t){0})),"ERROR");

(foo_t){0}(bar_t){0}现在是要比较的类型的表达式,因此可以在泛型选择中使用。

你可以实现你想要的,或者至少是接近的东西,用

#define SAME_TYPE(t1,t2) _Generic((t1){0}, t2: 1, default: 0)
_Static_assert(SAME_TYPE(foo_t, bar_t));

这不涉及假设一组有限类型(不支持结构类型等(的枚举,也不依赖于"GNU C"typeof扩展(它不是C语言的一部分(。

这适用于类型,而不是表达式。它可以很容易地扩展到一个参数是类型而另一个参数是表达式的情况。如果你需要断言两个表达式具有相同的类型,那么纯粹在 C 中至少要做到一些困难,并且可能是不可能的。如果您处于它们是变量的特殊情况下,则表达式

1 ? &var1 : &var2

如果var1var2具有不同的类型,则违反约束,但遗憾的是,GCC 默认将其视为警告而不是错误。我不知道有什么方法可以在没有完整-Werror的情况下将其变成错误,因为它似乎不在自己的警告组中,只是匿名默认警告......

正如在另一个答案中已经指出的,您不能将类型作为第一个参数传递给_Generic。您需要提供一个表达式,编译器将推断表达式的类型是否与列表匹配。

可以简化代码。不需要使用这些枚举。GCC 支持typeof扩展,它允许您执行以下操作:

#define EXPR_HAVE_SAME_TYPE(E1, E2) 
_Generic((E1), typeof(E2): 1, default: 0)
_Static_assert(EXPR_HAVE_SAME_TYPE(foo, foo), "...");
_Static_assert(!EXPR_HAVE_SAME_TYPE(foo, bar), "...");

如果创建表达式,则可以将其用于类型:

#define SAME_TYPE(T1, T2) 
EXPR_HAVE_SAME_TYPE(*(T1 *)0, *(T2 *)0)
_Static_assert(SAME_TYPE(foo_t, foo_t), "...");
_Static_assert(!SAME_TYPE(foo_t, bar_t), "...");

R.的评论和回答中也提到了类似的想法。

最新更新