2017-09-13 2 views
0

J'ai un programme qui utilise un singleton. Ce programme charge une bibliothèque d'objets partagés lors de l'exécution. Cette bibliothèque utilise également le même singleton. Le problème est que lorsque vous accédez au singleton à partir de la bibliothèque, une nouvelle instance du singleton est créée.Chargement dynamique et singlesons

Le programme est lié à -rdynamic, j'utilise -fPIC pour les deux et le chargement se passe comme ceci:

std::shared_ptr<Module> createModuleObject(const std::string& filename) 
{ 
    if (!fs::exists(filename)) 
     throw std::runtime_error("Library not found: " + std::string(filename)); 

    struct export_vtable* imports; 
    void *handle = dlopen(filename.c_str(), RTLD_LAZY | RTLD_GLOBAL); 

    if (handle) { 
     imports = static_cast<export_vtable*>(dlsym(handle, "exports")); 
     if (imports) 
      return std::shared_ptr<Module>(imports->make()); 
     else 
      throw std::runtime_error("Error trying to find exported function in library!"); 
    } else 
     throw std::runtime_error("Error trying to load library: " + std::string(filename)); 
} 

La bibliothèque exporte une classe comme ceci:

Module* make_instance() 
{ 
    return new HelloWorld(); 
} 
struct export_vtable 
{ 
    Module* (*make)(void); 
}; 
struct export_vtable exports = { make_instance }; 

et cette classe fait un usage du singleton.

Voici comment le singleton est créé (Configuration.cpp):

std::unique_ptr<Configuration> Configuration::instance_(nullptr); 
std::once_flag Configuration::onlyOnceFlag_; 

Configuration& Configuration::instance() 
{ 
    if (instance_ == nullptr) 
    { 
     std::cout << "INSTANCE IS NULL, CREATING NEW ONE" << std::endl; 
     std::call_once(Configuration::onlyOnceFlag_, 
        [] { 
          Configuration::instance_.reset(new Configuration()); 
         }); 
    } 

    return *Configuration::instance_; 
}  

Le programme et le lien de la bibliothèque contre la Configuration.cpp. Si je l'omets de la bibliothèque, j'obtiens une erreur de symbole indéfinie lorsque j'essaie d'accéder au singleton.

Quelqu'un a eu une idée pour résoudre ce problème? Merci beaucoup!

+1

C'est l'une des raisons pour lesquelles les singletons sont une mauvaise idée. Ils ne fonctionnent pas comme prévu avec les bibliothèques liées dynamiquement. De même, tenez-vous plutôt à [Singleton Pattern de Scott Meyer] (https://stackoverflow.com/questions/1008019/c-singleton-design-pattern). – user0042

+0

Il n'y a rien de mal avec les singletons. Il y a deux problèmes avec cette implémentation particulière: 1) Vous avez abandonné le contrôle sur la durée de vie singleton. Créer singleton au premier appel de la méthode d'obtention d'instance n'est jamais une bonne idée. 2) Votre bibliothèque crée son propre singleton au lieu de demander singleton créé par l'application parente. Pour corriger cette 'instance()' méthode devrait être importé de l'application principale. P.S. Singleton Pattern de Scott Meyer devrait s'appeler Antipattern. – VTT

+0

Il devrait être possible de réutiliser le même singleton à l'intérieur de la bibliothèque, n'est-ce pas? Le problème semble être le singleton à l'intérieur de la bibliothèque qui observe l'autre. Avoir une méthode 'setConfiguration()' ou quelque chose dans la bibliothèque n'est pas non plus une option pour moi, car cela va un peu à l'encontre du but. ** EDIT: ** qu'entendez-vous par "importé de l'application principale"? – Pfaeff

Répondre

0

Voilà comment je l'ai résolu pour https://github.com/kvahed/codeare/blob/master/src/core/ReconStrategy.hpp Après avoir chargé l'objet partagé j'attribue l'instance du singleton global Workspace à la classe chargée dans la dll. Toutes les classes de https://github.com/kvahed/codeare/tree/master/src/modules sont dérivées de ReconStrategy et d'objets partagés. La bonne chose est que ce code est portable.

Lors de la construction d'un tel ReconStrategy cela se produit:

ReconContext::ReconContext (const char* name) { 
    m_dlib = LoadModule ((char*)name); 
    if (m_dlib) { 
    create_t* create = (create_t*) GetFunction (m_dlib, (char*)"create"); 
    m_strategy = create(); 
    m_strategy->Name (name); 
    m_strategy->WSpace (&Workspace::Instance()); 
    } else { 
     m_strategy = 0; 
    } 
    } 
} 

La ligne clé ici est m_strategy->WSpace (&Workspace::Instance());

+0

Cela s'arrête de fonctionner dès que vous avez un second singleton qui tente d'accéder au premier (par exemple un singleton de journalisation). Ensuite, vous devez avoir une méthode d'affectation pour cela aussi. Il défait un peu le but. – Pfaeff

+0

Pourriez-vous s'il vous plaît quelqu'un prendre une seconde pour attendre une réponse avant de voter pour un message qui est destiné à aider. Je ne comprendrai jamais d'où vient cette motivation. Je ne fais que voter contre l'intention malveillante et la grande négligence. Il s'agit d'un code valgrind testé et fonctionne toute la journée sur les machines IRM sans fuites de mémoire et autres. Ce que je propose ci-dessus permet à chaque objet partagé d'avoir son espace de travail local pour les matrices privées et un global pour le service de reconstruction de le transmettre aux étapes de reconstruction de la chaîne. Sérieusement, je ne vais pas avoir de gens. –

+0

Ainsi, vous regrouperiez tous les singletons dans un seul objet d'espace de travail, puis l'affecteriez à votre bibliothèque? Et si les singletons ont besoin d'accéder les uns aux autres? Supposons qu'un singleton de journalisation tente de déterminer le niveau de journalisation à partir d'un singleton de configuration. – Pfaeff