C 定义任何非零值
The C define with any value for non zero
在 C 中我有这样的定义:
#define VALUE 5
但用户也可以设置一个功能:
#define VALUE get_value()
由于历史原因
#define VALUE 0
表示“未设置值,使用默认值”
问题是如何编写 #if
来决定 VALUE
是否为 0
。
#if VALUE == 0
...
#else
...
#endif
GCC 出现 "missing binary operator before token "(""
错误。
编辑:
为了使用例更清楚:
#if VALUE == 0
set_default_value();
#else
set_value(VALUE)
#endif
所以我不需要 VALUE
在 #if
中进行评估,只需看看它是否真的是“0”。
预处理器无法计算 C 函数,例如 get_value()
。它只能处理静态数据,因为它在编译之前被代码替换,所以你的语句不会在运行时执行。
if (VALUE == 0)
在你的 C 代码中会在编译之前被替换为 if (get_value() == 0)
。预处理器无法评估 get_value()
的 return 值(即使它总是 return 0)。
A #define VALUE 5
行使 C 预处理器将字符串 VALUE
替换为字符串 5
,无论它在哪里(即“字符串”之外)。生成的程序甚至不再包含 VALUE
,这就是编译器本身所看到的(1).
做个实验:获取你的程序(将其缩减为几行),然后 运行:
cc -E proggie.c > proggie.i
在 Unixy 系统下,您将获得一个包含预处理 C 的文件 proggie.i
,这是编译器本身所看到的。看不到 VALUE
,尝试在源代码中设置 VALUE
最终会尝试设置 0
,这显然行不通。
(1) 从历史上看,C 编译器 运行 源代码上的程序链(然后是以 KiB 而非 GiB 衡量的内存),第一个 --preprocessor-- 通常被称为 cpp
;今天的编译器通常不再有单独的预处理器。但从概念上讲,第一步仍然是预处理代码(处理 #include
、#if
和 #define
d 宏)。
您可以使用预处理模式匹配。
#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(A,B,...) B
#define GLUE(A,B) GLUE_I(A, B)
#define GLUE_I(A,B) A##B
#define ZERO_TEST(X_) SECOND(GLUE(ZERO_TEST_AGAINST_,X_),0)
#define ZERO_TEST_AGAINST_0 ,1
这里的关键构造是 SECOND
宏,它间接扩展到它的第二个参数。对于模式匹配,您可以通过仔细构造第一个参数来使用它;因为 SECOND
通常扩展到它的第二个参数,所以无论你构造什么,通常都会被忽略。但是由于 SECOND
间接 扩展到它的第二个参数 ,你可以通过在特定情况下用逗号扩展第一个参数来挑选特定的模式,这将推入一个新的第二个参数。
在这种情况下,我们在 ZERO_TEST_AGAINST_
的末尾有一个间接粘贴,我们正在寻找其结果为 ZERO_TEST_AGAINST_0
。
要使用这个:
#if ZERO_TEST(VALUE)
set_default_value();
#else
set_value(VALUE)
#endif
演示
http://coliru.stacked-crooked.com/a/2a6afc189637cfd3
警告
这完全符合您给定的规格;如果您有括号定义,则间接粘贴不适用于此表单:
#define VALUE (5)
...或:
#define VALUE (get_value() << 2) | 1
...因为 ZERO_TEST_AGAINST_
和 (
不加入以生成有效令牌。
一个通用的解决方案可能是不可能的,但你可以破解一些东西:
#define CAT(x, ...) CAT_(x, __VA_ARGS__)
#define CAT_(x, ...) x##__VA_ARGS__
#define CHECK_VALUE_CHECK_0 )(
#define CHECK_VALUE_FALSE(...) CHECK_VALUE_TRUE
#define CHECK_VALUE_TRUE() 1
#define CHECK_VALUE CHECK_VALUE_(CAT(CHECK_VALUE_CHECK_, VALUE))
#define CHECK_VALUE_(...) CHECK_VALUE_FALSE(__VA_ARGS__)
#if CHECK_VALUE
#error Value is 0.
#else
#error Value is not 0.
#endif
现在,如果 VALUE
定义为 0
,宏 CHECK_VALUE
将扩展为 1
。否则它将扩展为 CHECK_VALUE_TRUE
,这是一个未知的标识符,被 #if
.
认为是错误的
这个解决方案是 hacky:
- 如果
VALUE
以 0,
开头,则会导致硬错误。
- 如果
VALUE
以字母或数字或 _
以外的其他内容开头(例如 (
),则会导致硬错误。
- ...
我认为你误解了历史 'the value is not set' 的工作原理。
在 C 预处理器中,只要你在表达式中有一个 #if
指令 any 标识符(并且不是特殊函数 defined
) 将在计算 #if
之前被常量 0
替换。所以以你的例子
#define VALUE get_value()
#if VALUE == 0
扩展了 VALUE
宏,然后,由于没有定义 get_value
宏,它被替换为 0
,留下
#if 0() == 0
这会给出您看到的语法错误。
在 C 中我有这样的定义:
#define VALUE 5
但用户也可以设置一个功能:
#define VALUE get_value()
由于历史原因
#define VALUE 0
表示“未设置值,使用默认值”
问题是如何编写 #if
来决定 VALUE
是否为 0
。
#if VALUE == 0
...
#else
...
#endif
GCC 出现 "missing binary operator before token "(""
错误。
编辑: 为了使用例更清楚:
#if VALUE == 0
set_default_value();
#else
set_value(VALUE)
#endif
所以我不需要 VALUE
在 #if
中进行评估,只需看看它是否真的是“0”。
预处理器无法计算 C 函数,例如 get_value()
。它只能处理静态数据,因为它在编译之前被代码替换,所以你的语句不会在运行时执行。
if (VALUE == 0)
在你的 C 代码中会在编译之前被替换为 if (get_value() == 0)
。预处理器无法评估 get_value()
的 return 值(即使它总是 return 0)。
A #define VALUE 5
行使 C 预处理器将字符串 VALUE
替换为字符串 5
,无论它在哪里(即“字符串”之外)。生成的程序甚至不再包含 VALUE
,这就是编译器本身所看到的(1).
做个实验:获取你的程序(将其缩减为几行),然后 运行:
cc -E proggie.c > proggie.i
在 Unixy 系统下,您将获得一个包含预处理 C 的文件 proggie.i
,这是编译器本身所看到的。看不到 VALUE
,尝试在源代码中设置 VALUE
最终会尝试设置 0
,这显然行不通。
(1) 从历史上看,C 编译器 运行 源代码上的程序链(然后是以 KiB 而非 GiB 衡量的内存),第一个 --preprocessor-- 通常被称为 cpp
;今天的编译器通常不再有单独的预处理器。但从概念上讲,第一步仍然是预处理代码(处理 #include
、#if
和 #define
d 宏)。
您可以使用预处理模式匹配。
#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(A,B,...) B
#define GLUE(A,B) GLUE_I(A, B)
#define GLUE_I(A,B) A##B
#define ZERO_TEST(X_) SECOND(GLUE(ZERO_TEST_AGAINST_,X_),0)
#define ZERO_TEST_AGAINST_0 ,1
这里的关键构造是 SECOND
宏,它间接扩展到它的第二个参数。对于模式匹配,您可以通过仔细构造第一个参数来使用它;因为 SECOND
通常扩展到它的第二个参数,所以无论你构造什么,通常都会被忽略。但是由于 SECOND
间接 扩展到它的第二个参数 ,你可以通过在特定情况下用逗号扩展第一个参数来挑选特定的模式,这将推入一个新的第二个参数。
在这种情况下,我们在 ZERO_TEST_AGAINST_
的末尾有一个间接粘贴,我们正在寻找其结果为 ZERO_TEST_AGAINST_0
。
要使用这个:
#if ZERO_TEST(VALUE)
set_default_value();
#else
set_value(VALUE)
#endif
演示
http://coliru.stacked-crooked.com/a/2a6afc189637cfd3
警告
这完全符合您给定的规格;如果您有括号定义,则间接粘贴不适用于此表单:
#define VALUE (5)
...或:
#define VALUE (get_value() << 2) | 1
...因为 ZERO_TEST_AGAINST_
和 (
不加入以生成有效令牌。
一个通用的解决方案可能是不可能的,但你可以破解一些东西:
#define CAT(x, ...) CAT_(x, __VA_ARGS__)
#define CAT_(x, ...) x##__VA_ARGS__
#define CHECK_VALUE_CHECK_0 )(
#define CHECK_VALUE_FALSE(...) CHECK_VALUE_TRUE
#define CHECK_VALUE_TRUE() 1
#define CHECK_VALUE CHECK_VALUE_(CAT(CHECK_VALUE_CHECK_, VALUE))
#define CHECK_VALUE_(...) CHECK_VALUE_FALSE(__VA_ARGS__)
#if CHECK_VALUE
#error Value is 0.
#else
#error Value is not 0.
#endif
现在,如果 VALUE
定义为 0
,宏 CHECK_VALUE
将扩展为 1
。否则它将扩展为 CHECK_VALUE_TRUE
,这是一个未知的标识符,被 #if
.
这个解决方案是 hacky:
- 如果
VALUE
以0,
开头,则会导致硬错误。 - 如果
VALUE
以字母或数字或_
以外的其他内容开头(例如(
),则会导致硬错误。 - ...
我认为你误解了历史 'the value is not set' 的工作原理。
在 C 预处理器中,只要你在表达式中有一个 #if
指令 any 标识符(并且不是特殊函数 defined
) 将在计算 #if
之前被常量 0
替换。所以以你的例子
#define VALUE get_value()
#if VALUE == 0
扩展了 VALUE
宏,然后,由于没有定义 get_value
宏,它被替换为 0
,留下
#if 0() == 0
这会给出您看到的语法错误。