确保始终使用不同的参数调用接受枚举的 C 函数(编译时)

Make sure C function accepting enum is always called with a different argument (compile-time)

假设我想确保在编译时始终使用不同的参数值调用该函数。

理想情况下编译:

enum en {
   en_A,
   en_B
};
...
foo(en_A);

但这失败了:

enum en {
   en_A,
   en_B
};
...
foo(en_A);
...
foo(en_A);

所以,我们应该只能调用一些函数 foo(en_A) 一次(与 foo(en_B) 相同)。

定义 foo_en_A、foo_en_B 并确保它们中的每一个只被调用一次。

是否可以在 C 中执行类似编译时的操作?

我怀疑一般来说很难进行预处理,原因与停机问题困难的原因相同。实际上,如果您 运行 这个系统没有 NDEBUG,这将 assert 函数不会用相同的输入调用两次。

#include <stdio.h>
#include <limits.h>
#include <assert.h>

/* http://c-faq.com/misc/bitsets.html */
#define BITMASK(b) (1 << ((b) % CHAR_BIT))
#define BITSLOT(b) ((b) / CHAR_BIT)
#define BITSET(a, b) ((a)[BITSLOT(b)] |= BITMASK(b))
#define BITCLEAR(a, b) ((a)[BITSLOT(b)] &= ~BITMASK(b))
#define BITTEST(a, b) ((a)[BITSLOT(b)] & BITMASK(b))
#define BITNSLOTS(nb) ((nb + CHAR_BIT - 1) / CHAR_BIT)

enum en {
    en_A,
    en_B,
    en_NUM
};

static unsigned char debug_en[BITNSLOTS(en_NUM)];

static void foo(const enum en en) {
    assert(!BITTEST(debug_en, en)
        && (BITSET(debug_en, en), 1));
}

int main(void) {
    foo(en_A);
    foo(en_B);
    foo(en_A);
    return 0;
}

这给出了在第二个 foo(en_A) 上失败的断言中止。但是,它不是编译时测试。

Is it possible to do something like that compile-time in C?

不,这是不可能的。不仅C编程语言缺少reflection——无法检查自身,而且C编程语言一次编译一个“翻译单元”。虽然使用编译器扩展部分可能在一个 TU 中实现该检查,但您必须提供特殊的链接器或链接器插件才能跨多个 TU 实现该检查。

要在编译时做你想做的事,你必须使用 C 源代码本身之外的外部工具。这些工具可以处理生成的程序、检查程序集,或者可以处理 C 源代码本身。

(你问的是XY问题)

make sure that users of the logging library never make the call ambiguous (e.g. set the same error from two different places), so that place in the code which produced the error can be umambiguously traced

要做到这一点,只需记录 __FILE____LINE__ 并雇用理智的程序员,他们永远不会将非唯一的日志消息放在同一行,或使用相同的路径编译不同的文件。

无论如何,还有另一种解决问题的方法。不需要程序员在整个代码库中键入唯一的数字,只需生成数字即可。我曾经在一个裸机小型嵌入式系统中工作,该系统的通信能力非常低,以每小时字节数的数量级计算。用 shell 和 awk 编写的工具将扫描整个代码库以准确查找字符串 UNIQUE(),并且每个这样的调用都将被替换为一个唯一的数字 整个源代码库然后编译。因此,不是“要求程序员拥有唯一编号”,而是编号自行生成,这比检查某某编号是否已被所有同事使用更容易编程。