6

我正在用 C / Objective-C 开发一个应用程序(请不要使用 C++,我已经有一个解决方案),我遇到了一个有趣的用例。

因为clang不支持嵌套函数,所以我原来的做法是行不通的:

#define CREATE_STATIC_VAR(Type, Name, Dflt) static Type Name; __attribute__((constructor)) void static_ ## Type ## _ ## Name ## _init_var(void) { /* loading code here */ }

这段代码可以用 GCC 很好地编译,但是因为 clang 不支持嵌套函数,我得到一个编译错误:

预期的 ';' 在声明结束时。

所以,我找到了一个适用于函数内部变量的 Clang 解决方案:

#define CREATE_STATIC_VAR_LOCAL(Type, Name, Dflt) static Type Name; ^{ /* loading code here */ }(); // anonymous block usage

但是,我想知道是否有一种方法可以利用宏连接来选择适合这种情况的连接,例如:

#define CREATE_STATIC_VAR_GLOBAL(Type, Name, Dflt) static Type Name; __attribute__((constructor)) void static_ ## Type ## _ ## Name ## _init_var(void) { /* loading code here */ }
#define CREATE_STATIC_VAR_LOCAL(Type, Name, Dflt) static Type Name; ^{ /* loading code here */ }(); // anonymous block usage

#define SCOPE_CHOOSER LOCAL || GLOBAL
#define CREATE_STATIC_VAR(Type, Name, DFLT) CREATE_STATIC_VAR_ ## SCOPE_CHOOSER(Type, Name, Dflt)

显然,最终的实现不必完全如此,但类似的东西就足够了。

我曾尝试使用__builtin_constant_pwith __func__,但因为__func__它不是编译时常量,所以它不起作用。

我也尝试过使用__builtin_choose_expr,但这似乎不适用于全局范围。

我在文档中还缺少什么吗?似乎这应该是相当容易做到的事情,但是,我似乎无法弄清楚。

注意:我知道我可以简单地键入CREATE_STATIC_VAR_GLOBALCREATE_STATIC_VAR_LOCAL代替宏连接,但这是我试图突破编译器的限制。我也知道我可以使用 C++ 并立即解决这个问题,但这不是我的目标。

4

2 回答 2

3
#define SCOPE_CHOOSER LOCAL || GLOBAL
#define CREATE_STATIC_VAR(Type, Name, DFLT) CREATE_STATIC_VAR_ ## SCOPE_CHOOSER(Type, Name, Dflt)

这里最大的困难是 C 预处理器通过文本替换工作,所以即使你知道如何SCOPE_CHOOSER去做你想做的事,你最终会得到一个看起来像这样的宏扩展

CREATE_STATIC_VAR_LOCAL || GLOBAL(Type, Name, Dflt);

在替换过程中没有办法让预处理器“恒定折叠”宏扩展;事物“折叠”的唯一时间是它们出现在#if表达式中时。所以你唯一的希望(模数轻微的手摇)是找到一个可以在函数内部和外部工作的单一结构。

你能在这里解释更多关于最终目标的信息吗?我不认为你可以用 加载变量的初始值__attribute__((constructor)),但也许有一种方法可以在第一次输入函数体时加载初始值......或者在编译时将这些变量的所有地址注册到全局列表中 -时间并有一个__attribute__((constructor))​​遍历该列表的函数......或者这些方法的一些混搭。我没有任何具体的想法,但如果你提供更多信息,也许会出现一些东西。

编辑: 我认为这对您也没有帮助,因为它不是预处理器技巧,但这里有一个常量表达式,在函数范围内将评估为 0,在全局范围内评估为 1。

#define AT_GLOBAL_SCOPE __builtin_types_compatible_p(const char (*)[1], __typeof__(&__func__))

但是,请注意我说的是“评估”而不是“扩展”。这些构造是编译时的,而不是预处理时的。

于 2012-08-30T18:04:01.203 回答
0

@Qxuuplusone 答案的启发。

建议的宏AT_GLOBAL_SCOPE确实有效(在 GCC 中),但会导致编译器警告(我很确定它不能被Diagnostic Pragma静音,因为它是由这里pedwarn的测试创建的)。

除非您打开电源,否则您-w将始终看到这些警告,并且在您的脑海中产生一种可怕的感觉,即您可能不应该做您正在做的任何事情。

幸运的是,有一个解决方案可以消除这些挥之不去的疑虑。在Other Builtins部分,有__builtin_FUNCTION一个非常有趣的描述(强调我的):

此函数等效于__FUNCTION__符号,并返回一个地址常量,该地址常量指向调用内置函数的函数的名称,如果调用不在函数范围内,则返回空字符串

事实证明,至少在 GCC 8.3 版中,您可以这样做:

#define AT_GLOBAL_SCOPE (__builtin_FUNCTION()[0] == '\0')

这仍然可能无法回答最初的问题,但是在 GCC 决定这也会引起警告之前(它似乎是故意设计的这样做),它让我继续使用宏做有问题的事情而没有任何警告我这是个坏主意。

于 2021-10-13T09:09:23.963 回答