更新时间:2023-12-01 08:29:40
如果这里最重要的方面是你希望它编译失败 if a
and b
是不同的类型,您可以使用 C11 的 _Generic
以及 GCC 的 __typeof__
扩展来管理它.
If the most important aspect here is that you want it to fail to compile if a
and b
are different types, you can make use of C11's _Generic
along with GCC's __typeof__
extension to manage this.
一个通用的例子:
#include <stdio.h>
#define TYPE_ASSERT(X,Y) _Generic ((Y),
__typeof__(X): _Generic ((X),
__typeof__(Y): (void)NULL
)
)
int main(void)
{
int a = 1;
int b = 2;
TYPE_ASSERT(a,b);
printf("a = %d, b = %d
", a, b);
}
现在如果我们尝试编译这段代码,它会编译得很好,每个人都很高兴.
Now if we try to compile this code, it will compile fine and everybody is happy.
如果我们把b
的类型改成unsigned int
,但是编译会失败.
If we change the type of b
to unsigned int
, however, it will fail to compile.
之所以有效,是因为 _Generic
选择使用控制表达式的类型(在本例中为 (Y)
)来选择要遵循的规则并插入与该规则对应的代码.在这种情况下,我们只为 __typeof__(X)
提供了一个规则,因此如果 (X)
不是 (Y)
的兼容类型,没有合适的规则可供选择,因此无法编译.为了处理具有将衰减为指针的控制表达式的数组,我添加了另一个 _Generic
以另一种方式确保它们必须相互兼容,而不是接受单向兼容.而且因为——就我特别关心的——我们只想确保它在不匹配时编译失败,而不是在匹配时执行特定的操作,所以我给了相应的规则不做任何事情的任务:(void)NULL
This works because _Generic
selection uses the type of a controlling expression ((Y)
in this case) to select a rule to follow and insert code corresponding to the rule. In this case, we only provided a rule for __typeof__(X)
, thus if (X)
is not a compatible type for (Y)
, there is no suitable rule to select and therefore cannot compile. To handle arrays, which have a controlling expression that will decay to a pointer, I added another _Generic
that goes the other way ensuring they must both be compatible with one another rather than accepting one-way compatibility. And since--as far as I particularly cared--we only wanted to make sure it would fail to compile on a mismatch, rather than execute something particular upon a match, I gave the corresponding rule the task of doing nothing: (void)NULL
这种技术有一个问题:_Generic
不处理可变可修改类型,因为它是在编译时处理的.因此,如果您尝试使用可变长度数组执行此操作,它将无法编译.
There is a corner case where this technique stumbles: _Generic
does not handle Variably Modifiable types since it is handled at compile time. So if you attempt to do this with a Variable Length Array, it will fail to compile.
要处理固定宽度无符号类型的特定用例,我们可以修改嵌套的 _Generic
来处理它,而不是处理数组的特殊性:
To handle your specific use-case for fixed-width unsigned types, we can modify the nested _Generic
to handle that rather than handling the pecularities of an array:
#define TYPE_ASSERT(X,Y) _Generic ((Y),
__typeof__(X): _Generic ((Y),
uint8_t: (void)NULL,
uint16_t: (void)NULL,
uint32_t: (void)NULL,
uint64_t: (void)NULL
)
)
传递不兼容类型时的示例 GCC 错误:
Example GCC error when passing non-compatible types:
main.c: In function 'main':
main.c:7:34: error: '_Generic' selector of type 'signed char' is not compatible with any association
7 | __typeof__(X): _Generic ((Y),
| ^
值得一提的是,作为 GCC 扩展的 __typeof__
不会成为所有编译器都可移植的解决方案.不过,它似乎确实适用于 Clang,因此这是另一个支持它的主要编译器.
It is worth mentioning that __typeof__
, being a GCC extension, will not be a solution that is portable to all compilers. It does seem to work with Clang, though, so that's another major compiler supporting it.