C++习语,用于在任意翻译单元中的main之前执行任意代码

C++ Idiom for executing arbitrary code before main in arbitrary translation unit

本文关键字:任意 main 代码 执行 单元 习语 用于 翻译 C++      更新时间:2023-10-16

我有一个带有main()函数的翻译单元和另一个没有main的TU。甚至假设我只控制第二个,而不能触摸第一个。

现在,由于我不想深入讨论的原因,我希望能够在main()运行之前运行一些代码。我知道这可以通过用函数调用初始化全局变量来完成,但我想隐藏这一点——尽可能少地使用宏(我敢说不使用宏吗?可能不可能,C++中没有合适的静态块)

什么是一种优雅的,或者我们可以说,不是很丑陋的方式来做这件事?更明确地说,我正在寻找一种可以多次使用该功能的东西,而不仅仅是一次工作的东西。我希望它接近:

// ... at global scope ...
static {
    // my code here
}

PS:这个问题与初始化静态类成员的问题有关,但不相同。它的动机也是希望清楚地反驳这一说法——这在C++中是做不到的。

注意:是的,我知道静态初始化顺序的失败,没有必要提醒我……我并不是在要求绕过它的东西。显然,静态运行代码需要一些谨慎。

请享受静态初始化顺序的惨败:

int f(/* whatever args you want*/)
{
    // code to be ran before main()
    return 42;
}
static int _ignore = f(/*...*/);

请注意,如果不在其他地方使用,有时可能不会调用代码(别名"optimized-out")。其中一种情况是将TU编译到静态库中(然后可能不会将未使用的变量和代码拉入可执行文件中)。(E.马斯科夫斯基的说明)。

这是迄今为止我能想到的最好的。它是有效的,但实现有点模糊。

用法

如果你写:

STATIC_BLOCK {
    std::cout << "Hello static block world!" << std::endl;
}

此代码将在main()之前运行。然而,请注意,在main()开始之前写入std::cout实际上并不是一个好主意。

注意:

  • 必须用大括号将静态块代码括起来(不需要尾随分号;感谢@KlitosKyriacou的建议)
  • 如果不使用大括号,则会出现难以理解的错误消息
  • 在C++中不能保证静态代码的相对执行顺序

实施

静态块实现涉及一个伪变量。为了确保我们不会与其他一些伪变量发生冲突(例如,来自另一个静态块或其他任何地方),我们需要一点宏机制。

#define CONCATENATE(s1, s2) s1##s2
#define EXPAND_THEN_CONCATENATE(s1, s2) CONCATENATE(s1, s2)
#define STATIC_BLOCK_IMPL2(function_name,var_name) 
static void function_name(); 
static int var_name __attribute((unused)) = (function_name(), 0) ; 
static void function_name()
#define STATIC_BLOCK_IMPL1(prefix)   
    STATIC_BLOCK_IMPL2(CONCATENATE_FOR_STATIC_BLOCK(prefix,_fn),CONCATENATE_FOR_STATIC_BLOCK(prefix,_var))
#define STATIC_BLOCK STATIC_BLOCK_IMPL1(EXPAND_THEN_CONCATENATE(static_block_,__COUNTER__))

注意:

  • 如果您的编译器不支持__COUNTER__(因为它是标准的扩展,而不是标准的一部分),您可以使用__LINE__,它也可以工作。GCC和Clang支持__COUNTER__
  • __attribute__((unused))是另一个编译器扩展,尽管属性已经进入该语言;例如,请参阅本讨论。如果你把它扔了,你会得到警告
  • 代码是C++98(忽略编译器扩展),即不需要任何支持的现代C++构造。不幸的是,它不符合C的条件(其中初始化项必须是常量)

最初的灵感来自安德烈·亚历山德雷斯库的SCOPE_EXIT技巧