我正在用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_p
与__func__
一起使用,但由于__func__
不是编译时常数,因此无法使用。
我也尝试过使用__builtin_choose_expr
,但在全局范围内似乎不起作用。
文件里还有我遗漏的东西吗?看起来这应该是一件很容易做到的事情,然而,我似乎无法理解。
注意:我知道我可以简单地键入CREATE_STATIC_VAR_GLOBAL
或CREATE_STATIC_VAR_LOCAL
,而不是破坏宏连接,但这是我试图突破编译器的限制。我也意识到我可以使用C++并立即解决这个问题,但这不是我的目标
#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))
函数。。。或者这些方法的混合。我没有任何具体的想法,但如果你提供更多的信息,也许会有一些东西出现。
EDIT:我认为这对你也没有帮助,因为这不是预处理器的把戏,但这里有一个常量表达式,它在函数范围内的求值为0,在全局范围内的赋值为1
#define AT_GLOBAL_SCOPE __builtin_types_compatible_p(const char (*)[1], __typeof__(&__func__))
但是,请注意,我说的是"评估",而不是"扩展"。这些构造是编译时,而不是预处理时
灵感来自@Qxuuplusone答案。
建议的AT_GLOBAL_SCOPE
宏确实有效(在GCC中),但会引起编译器警告(我很确定它不能被Diagnostic Pragma静音,因为它是由pedwarn
创建的,这里有一个测试)。
除非你打开-w
,否则你总是会看到这些警告,并且在你的脑海里有一种可怕的感觉,你可能不应该做你正在做的事情。
幸运的是,有一个解决方案可以消除这些挥之不去的疑虑。在其他建筑部分,有一个__builtin_FUNCTION
,上面有这个非常有趣的描述(强调矿):
此函数相当于
__FUNCTION__
符号,返回一个地址常量,该常量指向调用内置函数的函数名,如果调用不在函数范围内,则返回空字符串。
事实证明,至少在GCC的8.3版本中,你可以这样做:
#define AT_GLOBAL_SCOPE (__builtin_FUNCTION()[0] == ' ')
这可能仍然不能回答最初的问题,但在GCC决定这也会引起警告之前(这似乎是故意设计的而不是),它让我可以继续使用宏做有问题的事情,而不需要任何东西来警告我这是个坏主意。