Question

I've been working with and testing a self-registering, abstract factory based upon the one described here:

https://stackoverflow.com/a/582456

In all my test cases, it works like a charm, and provides the features and reuse I wanted.

Linking in this factory in my project using cmake has been quite tricky (though it seems to be more of an ar problem).

I have the identical base.hpp, derivedb.hpp/cpp, and an equivalent deriveda.hpp/cpp to the example linked. In main, I simply instantiate the factory and call createInstance() twice, once each with "DerivedA" and "DerivedB".

The executable created by the line:

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

works as expected. Moving my derived classes into a library (using cmake, but I have tested this with ar alone as well) and then linking fails:

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

only calls the first static instantiation (from derivedA.cpp) and never the second static instantiation, i.e.

// 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");

Note that swapping the two in the ar line calls only the derivedb.cpp static instantiation, and not the deriveda.cpp instantiation.

Am I missing something with ar or static libraries that somehow do not play nice with static variables in C++?

Was it helpful?

Solution

Contrary to intuition, including an archive in a link command is not the same as including all of the objects files that are in the archive. Only those object files within the archive necessary to resolve undefined symbols are included. This is a good thing if you consider that once there was no dynamic linking and otherwise the entirety of any libraries (think the C library) would be duplicated into each executable. Here's what the ld(1) manpage (GNU ld on linux) has to say:

The linker will search an archive only once, at the location where it is specified on the command line. If the archive defines a symbol which was undefined in some object which appeared before the archive on the command line, the linker will include the appropriate file(s) from the archive. However, an undefined symbol in an object appearing later on the command line will not cause the linker to search the archive again.

Unfortunately there's no standard way to include every member of an archive in the linked executable. On linux you can use g++ -Wl,-whole-archive and on Mac OS X you can use g++ -all_load.

So with GNU binutils ld, the link command should be

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

the -Wl,-no-whole-archive ensures that any archive appearing later in the final link command generated by g++ will be linked in the normal way.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top