9

我一直在使用和测试一个基于此处描述的自注册抽象工厂:

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++ 中不能很好地处理静态变量?

4

1 回答 1

11

与直觉相反,在链接命令中包含存档与包含存档中的所有对象文件不同。仅包含存档中解析未定义符号所需的那些目标文件。如果您考虑到一旦没有动态链接,那么这是一件好事,否则所有库(想想 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

确保稍后出现在 g++ 生成的最终链接命令中的-Wl,-no-whole-archive任何存档都将以正常方式链接。

于 2012-04-09T22:13:50.187 回答