簡體   English   中英

有沒有一種方法可以強制c ++編譯器不優化靜態庫中的特定靜態對象?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM