我一直在使用并测试一个基于这里描述的自注册抽象工厂:
https://stackoverflow.com/a/582456
在我所有的测试用例中,它就像一个魅力,并提供我想要的功能和重用。
在我的项目中使用 cmake 在这个工厂中链接非常棘手(尽管它似乎更像是一个 ar 问题)。
我有相同的base.hpp,derivedb.hpp/cpp,以及链接到示例的等效deriveda.hpp/cpp。 总的来说,我只是实例化工厂并调用 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++中的静态变量配合使用?
与直觉相反,在链接命令中包含存档与包含存档中的所有对象文件不同。仅包括归档中解析未定义符号所需的那些对象文件。如果您认为一旦没有动态链接,否则任何库(想想 C 库)的整个库都会复制到每个可执行文件中,这是一件好事。以下是 ld(1) 手册页 (GNU ld on linux) 是怎么说的:
链接器只会在命令行上指定存档的位置搜索存档一次。 如果存档在命令行上定义的某个对象中未定义的符号出现在存档之前,则链接器将包含存档中的相应文件。 但是,稍后在命令行上出现的对象中的未定义符号不会导致链接器再次搜索存档。
不幸的是,没有标准方法可以在链接的可执行文件中包含存档的每个成员。在 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
可确保稍后出现在 G++ 生成的最终链接命令中的任何存档都将以正常方式链接。