2012-04-09 54 views
7

我一直使用和測試自行註冊,抽象工廠根據這裏所描述的:鏈接自行註冊,抽象工廠

https://stackoverflow.com/a/582456

在我所有的測試案例,它的工作原理像一個魅力,並提供了我想要的功能和重用。

在我的項目中使用cmake鏈接這個工廠一直非常棘手(儘管它似乎更像是一個問題)。

我有相同的base.hpp,derivedb.hpp/cpp和等效的deriveda.hpp/cpp鏈接的例子。在main中,我只是實例化工廠並調用createInstance()兩次,每次都使用「DerivedA」和「DerivedB」。

由線創建可執行文件:

g++ -o testFactory main.cpp derivedb.o deriveda.o 

按預期工作。移動我的派生類到庫(使用cmake,但我已經與AR單獨以及測試這一點),然後鏈接失敗:

ar cr libbase.a deriveda.o derivedb.o 
g++ -o testFactory libbase.a main.cpp 

只要求第一個靜態實例(從derivedA.cpp),從不第二靜態實例,即

// deriveda.cpp (if listed first in the "ar" line, this gets called) 
DerivedRegister<DerivedA> DerivedA::reg("DerivedA"); 

// derivedb.cpp (if listed second in the "ar" line, this does not get called) 
DerivedRegister<DerivedB> DerivedB::reg("DerivedB"); 

注意,在AR線交換兩個只調用derivedb.cpp靜態實例,而不是deriveda.cpp實例。

我是否錯過了某些與ar或靜態庫,某種方式不能很好地用C++中的靜態變量播放?

回答

10

與直覺相反,包括鏈接命令中的存檔與包含存檔中的所有對象文件不同。只包含歸檔中解析未定義符號所需的那些對象文件。如果您認爲一旦沒有動態鏈接,並且其他任何庫(認爲C庫)都將被複制到每個可執行文件中,這是件好事。以下是ld(1)手冊頁(linux上的GNU ld)必須說的:

鏈接器將只在命令行中指定的位置搜索存檔。如果歸檔文件定義了一個符號,該符號在命令行上歸檔之前出現的某個對象中未定義,則鏈接器將從歸檔文件中包含相應的文件。但是,稍後在命令行中出現的對象中的未定義符號將不會導致鏈接器再次搜索存檔。

不幸的是,沒有一種標準的方法可以將鏈接的可執行文件中的每個檔案成員包含進來。在Linux上,您可以使用g++ -Wl,-whole-archive,在Mac OS X上,您可以使用g++ -all_load

因此,與GNU binutils的LD,該鏈接命令應該是

g++ -o testFactory -Wl,-whole-archive libbase.a -Wl,-no-whole-archive main.cpp 

-Wl,-no-whole-archive確保任何檔案由++將以正常方式被鏈接克生成的最終鏈接命令後面出現。

+0

非常感謝,這讓我走上了正確的道路。爲了將來的參考,這也有所幫助(感謝在整個檔案庫中改進了search-foo!) http://stackoverflow.com/a/842770/1322752 – 2012-04-09 22:25:14