![](/img/trans.png)
[英]How to force include static objects from a static library in C++ (MSVC 11)
[英]Is there a way to force c++ compiler to not optimize out specific static objects in a static library?
(如果找不到通用解決方案,則只需要為gcc 5.4工作)
我有一個通用工廠,用於根據某些鍵(如代表類名的字符串)構造對象。 工廠必須允許注冊在構造時可能未知的類(因此,我不能簡單地顯式注冊一個類列表)。
作為注冊這些鍵及其關聯的構造函數的一種方法,我還有另一個“ RegisterInFactory”(模板)類。 在每個類的源文件中,我在與該類相對應的匿名名稱空間中構造一個對象。 這樣,一旦構造了全局對象,每個類都會自動注冊到工廠。 這些對象永遠不會在執行此初始注冊任務之外使用或引用。
但是,當將代碼編譯到靜態庫中時,將該庫鏈接到可執行文件中時,這些靜態對象將永遠不會被構造,因此這些類不會注冊到工廠,並且工廠無法創建任何東西。
我知道-Wl,--whole-archive -lfoo
, -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類。 我真的無法說出它為什么行得通。
該問題與優化無關。 而是鏈接器如何鏈接靜態庫中的符號。
但是,當將代碼編譯到靜態庫中時,將該庫鏈接到可執行文件中時,這些靜態對象將永遠不會被構造,因此這些類不會注冊到工廠,並且工廠無法創建任何東西。
之所以會發生這種情況,是因為沒有其他內容引用該注冊變量。 因此,鏈接器不會從存檔中提取符號的定義。
要告訴Unix鏈接器即使沒有其他引用,也保留該注冊變量,請在鏈接到該靜態庫時使用-Wl,--undefined=<symbol>
編譯器開關:
-u符號
--undefined =符號
強制將符號作為未定義符號輸入到輸出文件中。 這樣做可能會例如觸發來自標准庫的其他模塊的鏈接。 可以使用不同的選項參數重復
-u
,以輸入其他未定義的符號。
如果該注冊變量具有“ C”鏈接,則<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!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.