确保预处理器定义不改变值

4

当实现C标准的Annex K(Bounds-checking Interfaces)时,有以下要求:

通过将__STDC_WANT_LIB_EXT1__定义为1,可以“请求”声明此附录中指定的扩展,并通过将其定义为0来请求不声明。

然后有这段话:

在预处理翻译单元中,对于从子句K.3包含的任何头文件的所有包含,__STDC_WANT_LIB_EXT1_ _必须被定义为相同。 如果对于任何这样的包含__STDC_WANT_LIB_EXT1_ _被定义为不同,则实现应发出一条诊断,就像使用了预处理器错误指令一样。

我想知道如何实现这个要求。 我继续天真地编写了以下内容(以包含每个受影响的头文件):

#ifndef __STDC_WANT_LIB_EXT1__
  #ifdef __STDC_WANT_LIB_EXT1_PREVIOUS__
    #error __STDC_WANT_LIB_EXT1__ undefined when it was defined previously.
  #endif
#else
  #ifdef __STDC_WANT_LIB_EXT1_PREVIOUS__
    #if __STDC_WANT_LIB_EXT1__ != __STDC_WANT_LIB_EXT1_PREVIOUS__
      #error __STDC_WANT_LIB_EXT1__ defined to different value from previous include.
    #endif
  #else
    #define __STDC_WANT_LIB_EXT1_PREVIOUS__ __STDC_WANT_LIB_EXT1__
  #endif
#endif

当然,这种方法由于多种原因是不可行的:

  • 无法捕获第一个include未定义__STDC_WANT_LIB_EXT1__但第二个include定义了该值的情况(应该使用#error捕获)
  • #define没有考虑到__STDC_WANT_LIB_EXT1__的值(在前面加上#会将符号作为字符串,通过symbol2value(...)会将1作为字符串)。
  • ...

...但如果将其视为伪代码,则可以展示其背后的逻辑。

我对这种更为复杂的预处理器操作并不熟悉,因为通常会被告知避免使用宏魔法。 一定有一种实现引用要求的方法;只是我还没有“想到”。

有什么想法吗?


要完成[mcve],将上述代码放入header.h,并将以下内容放入testme.c

#define __STDC_WANT_LIB_EXT1__ 0
#include "header.h"
#define __STDC_WANT_LIB_EXT1__ 1
#include "header.h"

int main() {}

这个应该会触发“不同值”错误信息。

你不想通过编译器选项 -D__STDC_WANT_LIB_EXT1__=1 激活它,有什么原因吗? - user6556709
2
@user6556709:GCC 也会对 #undef 发出警告。我们能否专注于问题的主题,而不是讨论示例的代码风格? - DevSolar
我认为这并不是打算通过“宏魔法”来实现的,而是由编译器/预处理器本身来实现(可以保持全局定义的注册表等,并轻松注意到更改)。你是在问真正的编译器如何实现这个?还是在问如何用“宏魔法”来实现它? - Sander De Dycker
2
很确定这是一个逻辑问题,而且其他人会攻击它(我必须很快上床睡觉),所以我会快速离开这个部分...不要将您的“PREVIOUS”宏定义为当前值;将其定义为字面上的01或某个标记(我们将说为-1表示未定义)。宏不是变量;它们存储替换列表,而不是该替换列表将扩展到什么,特别是在这里,您希望您的存储确切地是您找到的内容(这样下次定义“EXT”时,您不会简单地认为您等于它)。 - H Walters
1
要明确的是,我指的是#define PREV CURRENT是一个陷阱;当定义为1时并不重要...一旦在第二个包含中将CURRENT定义为0并且此宏用作值时,它将扩展为0。你需要#if CURRENT==1/#define PREV 1;这样,如果CURRENT被重新定义为0并且与PREV进行比较,它将真正不相等。 - H Walters
显示剩余9条评论
1个回答

4

@HWalters确实帮我找到了正确的方向:

#ifndef __STDC_WANT_LIB_EXT1__
  #ifdef __STDC_WANT_LIB_EXT1_PREVIOUS__
    #if __STDC_WANT_LIB_EXT1_PREVIOUS__ != -1
      #error __STDC_WANT_LIB_EXT1__ undefined when it was defined earlier.
    #endif
  #else
    #define __STDC_WANT_LIB_EXT1_PREVIOUS__ -1
  #endif
#else
  #ifdef __STDC_WANT_LIB_EXT1_PREVIOUS__
    #if __STDC_WANT_LIB_EXT1__ != __STDC_WANT_LIB_EXT1_PREVIOUS__
      #error __STDC_WANT_LIB_EXT1__ redefined from previous value.
    #endif
  #else
    #if __STDC_WANT_LIB_EXT1__ == 0
      #define __STDC_WANT_LIB_EXT1_PREVIOUS__ 0
    #elif __STDC_WANT_LIB_EXT1__ == 1
      #define __STDC_WANT_LIB_EXT1_PREVIOUS__ 1
    #else
      /* Values other than 0,1 reserved for future use */
      #define __STDC_WANT_LIB_EXT1_PREVIOUS__ -2
    #endif
  #endif
#endif

“Thinko” 是指这行代码:
#define __STDC_WANT_LIB_EXT1_PREVIOUS__ __STDC_WANT_LIB_EXT1__

将"previous"定义为实际值而不是其他标记,可以使其正常工作。

然而,这个解决方案并不完美 -- 所有除0,1,undefined之外的"other"值都被合并到单个"previous"值(-2)中,而标准的字面意思是任何重新定义都应该发出诊断。


我将问题保持开放,以防“完美”的解决方案仍然“存在”。 - DevSolar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接