Singleton across compilation units: linking library vs linking objects

731 views Asked by At

I apologize if the title is not fully self-explanatory. I'm trying to understand why my singleton factory pattern is not working properly, and I ran into a bizarre difference when using library vs linking single objects files.

Here's a simplified version of the code:

main.cpp

#include <iostream>
#include "bar.hpp"

int main (int /*argc*/, char** /*argv*/)
{
  A::get().print();
  return 0;
}

bar.hpp

#ifndef BAR_HPP
#define BAR_HPP

#include <iostream>

class A
{
public:
  static A& get ()
  {
    static A a;
    return a;
  }
  bool set(const int i)
  {
    m_i = i;
    print();
    return true;
  }
  void print ()
  {
    std::cout << "print: " << m_i << "\n";
  }
private:

  int m_i;
  A () : m_i(0) {}
};
#endif // BAR_HPP

baz.hpp

#ifndef BAZ_HPP
#define BAZ_HPP

#include "bar.hpp"

namespace
{
static bool check = A::get().set(2);
}

#endif // BAZ_HPP

baz.cpp

#include "baz.hpp"

Now, I build my "project" in two ways:

Makefile:

all:
  g++ -std=c++11 -c baz.cpp
  g++ -std=c++11 -o test main.cpp baz.o
lib:
  g++ -std=c++11 -c baz.cpp
  ar rvs mylib.a baz.o
  g++ -std=c++11 -o test main.cpp mylib.a

Here are the outputs I get:

$ make all
$ ./test
print: 2
print: 2

$ make lib
$ ./test
print: 0

In the first case the call to A::get().set(2) in baz.hpp takes place, and the same instantiation of A is then used in the main function, which therefore prints 2. In the second case, the call to A::get().set(2) in baz.hpp never takes place, and in the main function the value set by the constructor (that is, 0) is printed.

So finally I can ask my question: why is the behavior different in the two cases? I would expect that either both print 0 once or print 2 twice. I always assumed that a library was just a compact way to ship object files, and that the behavior of linking mylib.a should be the same as that of linking baz.o directly. Why isn't that the case?

Edit: the reason, as explained by Richard, is that no symbols defined in baz.cpp are required in main.cpp, so baz.o is not extracted from the library and linked. This raises another question: is there a workaround to ensure that the instruction A::get().set(2) is executed? I would like to avoid making the singleton a global object, but I'm not sure it's possible. I would also like to avoid to include baz.hpp in the main, since there may be many bazxyz.hpp and that would require main.cpp to know in advance all of them, defying the whole purpose of the factory-like registration process...

1

There are 1 answers

3
Richard Hodges On BEST ANSWER

If this is to be a static library, then some module somewhere is going to have to address something in each implementation file of the objects that are going to register themselves with the factory.

A reasonable place for this would be in bar.cpp (which is a file you don't yet have). It would contain some or all of the implementation of A plus some means of calling the registration functions the widgets you're going to create.

Self-discovery only works if the object files are linked into the executable. This gives the c++ startup sequence a chance to know about and construct all objects with global linkage.