Je m'excuse si le titre n'est pas entièrement auto-explicatif. J'essaye de comprendre pourquoi mon modèle d'usine de singleton ne fonctionne pas correctement, et je me suis heurté à une différence bizarre en utilisant la bibliothèque contre la liaison des fichiers d'objets simples.Singleton à travers les unités de compilation: reliant la bibliothèque vs les objets de liaison
est ici une version simplifiée du 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"
Maintenant, je construis mon "projet" de deux façons:
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
Voici les sorties que je reçois:
$ make all
$ ./test
print: 2
print: 2
$ make lib
$ ./test
print: 0
Dans le premier cas l'appel à A::get().set(2)
dans baz.hpp a lieu, et la même instanciation de A est alors utilisée dans la fonction principale, qui imprime donc 2
. Dans le second cas, l'appel à A::get().set(2)
dans baz.hpp n'a jamais lieu, et dans la fonction principale, la valeur définie par le constructeur (c'est-à-dire 0
) est imprimée.
Donc finalement je peux poser ma question: pourquoi le comportement différent dans les deux cas? Je m'attendrais à ce que les deux impriment une fois 0 ou impriment deux fois. J'ai toujours supposé qu'une bibliothèque était juste un moyen compact d'expédier des fichiers objets, et que le comportement de la liaison de mylib.a devrait être le même que celui de relier baz.o directement. Pourquoi n'est-ce pas le cas?
Éditer: la raison, comme expliqué par Richard, est qu'aucun des symboles définis dans baz.cpp n'est requis dans main.cpp, donc baz.o n'est pas extrait de la bibliothèque et lié. Cela soulève une autre question: existe-t-il une solution de contournement pour s'assurer que l'instruction A::get().set(2)
est exécutée? Je voudrais éviter de faire du singleton un objet global, mais je ne suis pas sûr que ce soit possible. Je voudrais également éviter d'inclure baz.hpp dans le principal, car il peut y avoir beaucoup de bazxyz.hpp
et cela nécessiterait main.cpp d'en savoir à l'avance tous, défiant tout le but du processus d'enregistrement de type usine ...
rien dans main.cpp exige un symbole dans baz.cpp, de sorte que le baz archivé.o ne sera pas lié à l'exécutable –
La liaison du fichier objet force-t-elle le lien à la place? Cela a du sens, je suppose. – bartgol
oui, lier explicitement le fichier objet le force dans l'exécutable. –