连接一个自我注册的抽象工厂

时间:2012-04-09 21:52:11

标签: c++ g++ cmake ar

我一直在根据这里描述的那个工作并测试一个自我注册的抽象工厂:

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 ++中的静态变量不起作用?

1 个答案:

答案 0 :(得分: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确保g ++生成的最终链接命令中稍后出现的任何存档都将以正常方式链接。