有没有办法强制c ++编译器不优化静态库中的特定静态对象? [英] Is there a way to force c++ compiler to not optimize out specific static objects in a static library?

查看:172
本文介绍了有没有办法强制c ++编译器不优化静态库中的特定静态对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我有一个通用的工厂,用于基于一些关键字(如表示类名称的字符串)。工厂必须允许在建造时不知道的类进行注册(所以我不能简单地注册一个类的列表)。

作为注册这些键及其相关构造函数的方法,我有另一个'RegisterInFactory'(模板)类。在每个类的源文件中,我在与该类相对应的匿名名称空间中构造一个对象。这样,一旦构建全局对象,每个类都会自动注册到工厂。在执行此初始注册任务之外,这些对象永远不会被使用或引用。然而,当代码被编译到一个静态库中时,当这个库链接到一个可执行文件中时,这些静态对象永远不会被构造,所以这些类不会被注册到工厂,工厂不能创造任何东西。

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

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

简单示例代码:
Factory.h:

 模板< Base> 
类工厂{
...
模板<派生>
class RegisterInFactory {
RegisterInFactory(){
instance()。regInFactory(derivedConstructorFunctional);
}
};
};

在Derived.cpp中:

  namespace {BaseFactory :: RegisterInFactory< Derived> registerMe { 派生}; } 

最后说明:在没有链接器标志的情况下,包括在内,但似乎发生的唯一方法是如果派生类是足够复杂的。或者,也许是我直接在链接的可执行文件中使用Derived类。我无法确定为什么当它有效时。

解决方案

这个问题与优化无关。而是链接器如何链接来自静态库的符号。


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


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



要告诉Unix链接器保留该注册变量,即使没有其他引用变量指向它,使用 -Wl, - undefined =<符号> 编译器切换链接到该静态库时:


-u符号

- undefined =符号

输出文件作为未定义的符号。例如,这可能会触发从标准库中链接附加模块。可以用不同的选项参数重复 -u 来输入附加的未定义符号。

如果该注册变量具有C链接,则< symbol> 是变量名。



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






示例: p>

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

namespace mylib {

struct注册
{
Register(){std :: printf(%s\,__PRETTY_FUNCTION__); }
};

注册register_me;

}

[最大@超新星:〜/ src / test] $ cat test.cc
#include< iostream>

int main(){
std :: cout<< 你好,世界!\ 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 -g 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

[最大@超新星:〜/ src / test] $ ./debug/test
你好,世界! < --------缺少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< --------为此需要一个重名的名称。
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< --------对此的重名。
0000000000000000 r _ZZN5mylib8RegisterC4EvE19__PRETTY_FUNCTION__

#向Makefile添加-Wl, - undefined = _ZN5mylib11register_meE。
[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/测试
mylib :: Register :: Register()<--------从预期的mylib :: register_me输出。
你好,世界!


(Only needs to work for gcc 5.4, if a general solution can't be found)

I have a generic factory that I use to construct objects based on some key (like a string representing a class name). The factory must allow classes to register that may not be known at construction time (so I can't simply register a list of classes explicitly).

As a means of registering these keys and their associated constructors, I have another 'RegisterInFactory' (templated) class. In each class's source file, I construct an object in an anonymous namespace corresponding to that class. This way, each class is automatically registered to the factory once the global objects are constructed. These objects never get used or referenced outside of doing this initial registration task.

However, when the code is compiled into a static library, when that library is linked into an executable, these static objects never get constructed, so the classes don't register to the factory, and the factory can't create anything.

I'm aware of the -Wl,--whole-archive -lfoo flag, which does include these global objects. But it also introduces a lot of 'multiple definition' errors. I'm aware that there's another flag that I can turn off the multiple definition errors, but I don't feel comfortable going without those errors. I'm aware of -u symbolName to turn off specific symbol names from these multiple definition errors (at least that's what I think it does). However, there are just too many of these redundant functions for that to be realistic (mostly from protobuf classes).

Is there any way to tell the compiler not to optimize those objects out, but only those objects so I can avoid the multiple definition issue? Is there another pattern I might be able to follow that fits within the constraints? (Particularly that I do not know at compile time what classes may be registered to the factory.)

Simplified Example code: Factory.h:

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

In Derived.cpp:

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

Final note: I've gotten lucky to some degree where without the linker flags, they still get included, but the only way that seems to happen is if the Derived class is 'sufficiently' complicated. Or maybe it's if I use the Derived class directly within the linked executable. I can't really tell why it's worked when it has.

解决方案

The issue is not related to optimizations. Rather how linkers link symbols from static libraries.

However, when the code is compiled into a static library, when that library is linked into an executable, these static objects never get constructed, so the classes don't register to the factory, and the factory can't create anything.

That happens because nothing else refers to that registration variable. Hence, the linker is not pulling in the definition of the symbol from the archive.

To tell a Unix linker to keep that registration variable even if nothing else refers to it, use -Wl,--undefined=<symbol> compiler switch when linking to that static library:

-u symbol

--undefined=symbol

Force symbol to be entered in the output file as an undefined symbol. Doing this may, for example, trigger linking of additional modules from standard libraries. -u may be repeated with different option arguments to enter additional undefined symbols.

If that registration variable has "C" linkage, then <symbol> is the variable name.

For C++ linkage you will need to lookup the mangled name using nm --defined-only <object-file>. You may also need to put that variable into a named namespace, so that it has external linkage.


Example:

[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!

这篇关于有没有办法强制c ++编译器不优化静态库中的特定静态对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆