C11_Generic 将 true 和 false 推导为整数
C11_Generic deduces true and false as integers
在 C11 中,有一个 _Generic
宏可以实现很酷的泛型函数。然而,在正常情况下使用 true
和 false
会导致不正确的推导:
#include <stdio.h>
#include <stdbool.h>
#define TypeName(x) \
_Generic((x), \
bool: "bool", \
int: "int", \
default: "unknown")
#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && (__bool_true_false_are_defined)
# undef true
# define true ((bool)(1))
# undef false
# define false ((bool)(0))
#endif
int main(void)
{
printf("1: %s\n", TypeName(1));
printf("true: %s\n", TypeName(true));
printf("false: %s\n", TypeName(false));
}
这会打印:
1: int
true: bool
false: bool
但是没有重新定义true
和false
的中间位:
1: int
true: int
false: int
这意味着您不能执行 _Generic
功能,例如:
struct Variant * const int32 = variant_create(1);
struct Variant * const boolean = variant_create(true);
所以我的问题是:
- 重定义片段安全吗?
- 这是 C11 标准中的疏忽还是 GCC 和 Clang 中的错误?
那是因为stdbool.h
中的true
和false
只是整数1
和0
,它们的类型确实是int
,不是bool
.
C11 §7.18 Boolean type and values <stdbool.h>
The remaining three macros are suitable for use in #if
preprocessing directives. They
are
true
which expands to the integer constant 1
,
false
which expands to the integer constant 0
, and
__bool_true_false_are_defined
which expands to the integer constant 1
.
两种类型确实都是宏:
7.18 Boolean type and values
-
The remaining three macros are suitable for use in #if preprocessing directives.
They are:
true which expands to the integer constant 1,
false which expands to the integer constant 0,
and
__bool_true_false_are_defined which expands to the integer constant 1.
最后一条规则说你可以重新定义宏:
Notwithstanding the provisions of 7.1.3, a program may undefine and perhaps then
redefine the macros bool, true, and false. 259)
259) See ‘‘future library directions’’ (7.31.9)
尽管引用了规则:
7.1.3 Reserved identifiers
-
If the program removes (with #undef) any macro definition of an identifier in the first
group listed above, the behavior is undefined.
规则 7.31.9 说重新定义可能不是一个好主意:
7.31.9 Boolean type and values
-
The ability to undefine and perhaps then redefine the macros bool, true, and false is
an obsolescent feature.
所以我建议您创建自己的 my_true 和 my_false 宏,这些宏被转换为 _Bool。
你的问题 "Is this an oversight in the C11 standard or a bug in GCC and Clang?" 的答案是 "neither"。
其他人已经解释了为什么它不是 GCC 和 Clang 中的错误。
这也不是标准中的疏忽,标准定义了 true 和 false 以实现向后兼容性。如果 true 被定义为 (bool)1 并且 false 被定义为 (bool) 0,更容易遇到不兼容的宏定义。最明显的效果是警告。如果宏的重新定义(不干预#undef)没有改变定义,编译器通常不会发出警告;因此,最简单的定义最不可能在实际中产生问题。
C 没有比 int
更窄的整数类型的文字,正如其他人所说,宏 false
和 true
因此被定义为 0
和 1
。因此,您为 _Bool
描述的问题对于所有其他窄数据类型都是相同的:很难在类型泛型宏中触发例如 short
变体,并使用 [=18 调用这样的宏=] 也不会触发 char
变体。
您的替换宏不好用,因为它们在预处理器 #if
表达式中不可用。如果您之后包含的某些代码使用它们,那么编译将失败。
你可以使用
# define true ((_Bool)+1)
# define false ((_Bool)+0)
相反。小加号 +
神奇地使预处理器将它们解释为表达式中的 ((0)+1)
和 ((0)+0)
,因此它们仍然会按照您的预期进行。
在 C11 中,有一个 _Generic
宏可以实现很酷的泛型函数。然而,在正常情况下使用 true
和 false
会导致不正确的推导:
#include <stdio.h>
#include <stdbool.h>
#define TypeName(x) \
_Generic((x), \
bool: "bool", \
int: "int", \
default: "unknown")
#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && (__bool_true_false_are_defined)
# undef true
# define true ((bool)(1))
# undef false
# define false ((bool)(0))
#endif
int main(void)
{
printf("1: %s\n", TypeName(1));
printf("true: %s\n", TypeName(true));
printf("false: %s\n", TypeName(false));
}
这会打印:
1: int
true: bool
false: bool
但是没有重新定义true
和false
的中间位:
1: int
true: int
false: int
这意味着您不能执行 _Generic
功能,例如:
struct Variant * const int32 = variant_create(1);
struct Variant * const boolean = variant_create(true);
所以我的问题是:
- 重定义片段安全吗?
- 这是 C11 标准中的疏忽还是 GCC 和 Clang 中的错误?
那是因为stdbool.h
中的true
和false
只是整数1
和0
,它们的类型确实是int
,不是bool
.
C11 §7.18 Boolean type and values
<stdbool.h>
The remaining three macros are suitable for use in
#if
preprocessing directives. They aretrue
which expands to the integer constant
1
,false
which expands to the integer constant
0
, and__bool_true_false_are_defined
which expands to the integer constant
1
.
两种类型确实都是宏:
7.18 Boolean type and values
- The remaining three macros are suitable for use in #if preprocessing directives.
They are:
true which expands to the integer constant 1,
false which expands to the integer constant 0,
and
__bool_true_false_are_defined which expands to the integer constant 1.
最后一条规则说你可以重新定义宏:
Notwithstanding the provisions of 7.1.3, a program may undefine and perhaps then redefine the macros bool, true, and false. 259)
259) See ‘‘future library directions’’ (7.31.9)
尽管引用了规则:
7.1.3 Reserved identifiers
- If the program removes (with #undef) any macro definition of an identifier in the first group listed above, the behavior is undefined.
规则 7.31.9 说重新定义可能不是一个好主意:
7.31.9 Boolean type and values
- The ability to undefine and perhaps then redefine the macros bool, true, and false is an obsolescent feature.
所以我建议您创建自己的 my_true 和 my_false 宏,这些宏被转换为 _Bool。
你的问题 "Is this an oversight in the C11 standard or a bug in GCC and Clang?" 的答案是 "neither"。
其他人已经解释了为什么它不是 GCC 和 Clang 中的错误。
这也不是标准中的疏忽,标准定义了 true 和 false 以实现向后兼容性。如果 true 被定义为 (bool)1 并且 false 被定义为 (bool) 0,更容易遇到不兼容的宏定义。最明显的效果是警告。如果宏的重新定义(不干预#undef)没有改变定义,编译器通常不会发出警告;因此,最简单的定义最不可能在实际中产生问题。
C 没有比 int
更窄的整数类型的文字,正如其他人所说,宏 false
和 true
因此被定义为 0
和 1
。因此,您为 _Bool
描述的问题对于所有其他窄数据类型都是相同的:很难在类型泛型宏中触发例如 short
变体,并使用 [=18 调用这样的宏=] 也不会触发 char
变体。
您的替换宏不好用,因为它们在预处理器 #if
表达式中不可用。如果您之后包含的某些代码使用它们,那么编译将失败。
你可以使用
# define true ((_Bool)+1)
# define false ((_Bool)+0)
相反。小加号 +
神奇地使预处理器将它们解释为表达式中的 ((0)+1)
和 ((0)+0)
,因此它们仍然会按照您的预期进行。