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

时间:2015-12-18 16:25:39

标签: c++ idioms static-block

我有一个带有main()功能的翻译单元和另一个没有主要功能的TU。假设我只控制第二个而不能触及第一个。

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

这样做会有什么优雅,或者我们说,不是非常丑陋的方式?更清楚的是,我正在寻找能够多次使用此功能的东西,而不仅仅是让它能够工作一次。我希望它尽可能接近:

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

PS:这个问题与this question about initializing static class members有关,但不一样。这也是出于明确反驳this claim it can't be done in C++的愿望。

注意:是的,我知道静态初始化命令惨败,不需要提醒我...而且我不是要求绕过它的东西。显然,静态运行代码需要谨慎。

2 个答案:

答案 0 :(得分:4)

请欣赏static initialization order fiasco

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

static int _ignore = f(/*...*/);

请注意,有时可能不会调用代码,如果没有在其他地方使用(别名"优化了")。其中一种情况是将TU编译成静态库(然后未使用的变量和代码可能不会被拉入可执行文件)。 (E. Maskovsky的注释)。

答案 1 :(得分:2)

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

用法

如果你写:

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

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

注意:

  • 必须用大括号包围你的静态块代码(不需要一个尾随的分号;感谢@KlitosKyriacou的建议)。
  • 如果你不使用花括号,那么就会发现难以弄清楚的错误信息。
  • 执行静态代码的相对顺序为not guaranteed in 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))是另一个编译器扩展,虽然属性已经进入该语言;例如,请参阅this discussion。如果放弃它,你会收到警告。
  • 代码是C ++ 98(忽略编译器扩展),即您不需要任何现代C ++构造。不幸的是,它不符合C(初始化器必须是常量)。

最初的灵感来自Andrei Alexandrescu的SCOPE_EXIT trick