有没有办法强制c ++编译器不优化静态库中的特定静态对象?

时间:2017-11-08 17:48:21

标签: c++ gcc

(如果无法找到通用解决方案,只需要为gcc 5.4工作)

我有一个通用工厂,我用它来构建基于某个键的对象(比如表示类名的字符串)。工厂必须允许注册在构造时可能不知道的类(因此我不能简单地明确注册类列表)。

作为注册这些键及其相关构造函数的一种方法,我有另一个'RegisterInFactory'(模板化)类。在每个类的源文件中,我在与该类对应的匿名命名空间中构造一个对象。这样,一旦构造了全局对象,每个类就会自动注册到工厂。在执行此初始注册任务之外,永远不会使用或引用这些对象。

但是,当代码被编译到静态库中时,当该库链接到可执行文件时,这些静态对象永远不会被构造,因此类不会注册到工厂,并且工厂不能创建任何东西。

我知道-Wl,--whole-archive -lfoo标志,它包含这些全局对象。但它也引入了很多“多重定义”错误。我知道还有另一个标志我可以关闭多重定义错误,但是如果没有这些错误我感到不舒服。我知道-u symbolName要从这些多个定义错误中关闭特定的符号名称(至少我认为这样做)。然而,这些冗余函数中有太多是实际的(主要来自protobuf类)。

有没有办法告诉编译器不要优化那些对象,而只是那些对象所以我可以避免多重定义问题?我可能能够遵循哪种模式符合约束条件吗? (特别是在编译时我不知道哪些类可以注册到工厂。)

简化示例代码: Factory.h:

template<Base>
class Factory{
  ...
  template<Derived>
  class RegisterInFactory{
    RegisterInFactory(){
      instance().regInFactory(derivedConstructorFunctional);
    }
  };
};

在Derived.cpp中:

namespace{ BaseFactory::RegisterInFactory<Derived> registerMe{"Derived"}; }

最后的注意事项:在某种程度上,如果没有链接器标记,我们仍然很幸运,它们仍然被包含在内,但似乎发生的唯一方法是Derived类是否“足够”复杂。或者也许是我直接在链接的可执行文件中使用Derived类。我无法确定它为什么会有效。

2 个答案:

答案 0 :(得分:3)

该问题与优化无关。而是链接器如何链接静态库中的符号。

  

但是,当代码被编译到静态库中时,当该库链接到可执行文件时,这些静态对象永远不会被构造,因此这些类不会注册到工厂,工厂可以&#39 ;创造任何东西。

发生这种情况是因为没有其他任何内容引用该注册变量。因此,链接器不会从存档中提取符号的定义。

要告诉Unix链接器保留该注册变量,即使没有其他内容引用它,在链接到该静态库时使用-Wl,--undefined=<symbol>编译器开关:

  

-u symbol

     

- 未定义=符号

     

强制符号作为未定义的符号输入到输出文件中。例如,这样做可以触发标准库中其他模块的链接。可以使用不同的选项参数重复-u以输入其他未定义的符号。

如果该注册变量有&#34; C&#34;链接,然后<symbol>是变量名。

对于C ++链接,您需要使用nm --defined-only <object-file>查找损坏的名称。您可能还需要将该变量放入命名的命名空间中,以便它具有外部链接。

示例:

[max@supernova:~/src/test] $ cat mylib.cc
#include <cstdio>

namespace mylib {

struct Register
{
    Register() { std::printf("%s\n", __PRETTY_FUNCTION__); }
};

Register register_me;

}

[max@supernova:~/src/test] $ cat test.cc
#include <iostream>

int main() {
    std::cout << "Hello, world!\n";
}

[max@supernova:~/src/test] $ make
mkdir /home/max/src/test/debug
g++ -c -o /home/max/src/test/debug/test.o -MD -MP -std=gnu++14 -march=native -pthread -W{all,extra,error,inline} -ggdb -fmessage-length=0 -Og test.cc
g++ -c -o /home/max/src/test/debug/mylib.o -MD -MP -std=gnu++14 -march=native -pthread -W{all,extra,error,inline} -ggdb -fmessage-length=0 -Og mylib.cc
ar rcsT /home/max/src/test/debug/libmylib.a /home/max/src/test/debug/mylib.o
g++ -o /home/max/src/test/debug/test -ggdb -pthread /home/max/src/test/debug/test.o /home/max/src/test/debug/libmylib.a

[max@supernova:~/src/test] $ ./debug/test 
Hello, world! <-------- Missing output from mylib::register_me.

[max@supernova:~/src/test] $ nm --defined-only -C debug/mylib.o
0000000000000044 t _GLOBAL__sub_I__ZN5mylib11register_meE
0000000000000000 t __static_initialization_and_destruction_0(int, int)
0000000000000000 B mylib::register_me                        <-------- Need a mangled name for this.
0000000000000000 r mylib::Register::Register()::__PRETTY_FUNCTION__

[max@supernova:~/src/test] $ nm --defined-only debug/mylib.o
0000000000000044 t _GLOBAL__sub_I__ZN5mylib11register_meE
0000000000000000 t _Z41__static_initialization_and_destruction_0ii
0000000000000000 B _ZN5mylib11register_meE                   <-------- The mangled name for that.
0000000000000000 r _ZZN5mylib8RegisterC4EvE19__PRETTY_FUNCTION__

# Added -Wl,--undefined=_ZN5mylib11register_meE to Makefile.
[max@supernova:~/src/test] $ make 
g++ -o /home/max/src/test/debug/test -ggdb -pthread -Wl,--undefined=_ZN5mylib11register_meE /home/max/src/test/debug/test.o /home/max/src/test/debug/libmylib.a

[max@supernova:~/src/test] $ ./debug/test 
mylib::Register::Register() <-------- Output from mylib::register_me as expected.
Hello, world!

答案 1 :(得分:-1)

简单(和语言一致)解决方案是声明它volatile static... 使用volatile关键字告诉编译器不对其应用任何优化,包括不优化它