2016-06-22 2 views
0

J'utilise une approche plutôt impliquée de l'allocation d'objets en utilisant une approche «catalogue». Le code génère un objet de catalogue (carte statique globale) qui est rempli par des pointeurs de base, qui pointent vers des objets dérivés. Tout cela est blasphématoire, mais pas le point ici. Lors de l'exécution du programme, le catalogue est rempli par tous les objets dérivés, et nous pouvons allouer tout ce qui a été enregistré avec le catalogue, sans avoir à maintenir une liste de classes dérivées candidates. Génial. Toutefois, lorsque l'une des classes dérivées est exécutée à l'aide de l'archiveur (ar) et que l'archive est utilisée à la place du fichier objet pour l'éditeur de liens, le code échoue. Quelqu'un peut-il me dire pourquoi cela se passe? Exemple de code:Comment forcer archiver (ar) à produire une archive qui se comporte de manière identique au fichier objet?

//Base.h 
#include<unordered_map> 
#include<string> 
#include<map> 
#include<iostream> 
#include<memory> 

class Base 
{ 
public: 
    Base(std::string name):m_name(name){} 
    virtual ~Base() {} 
    std::string m_name; 
}; 

class ObjectCatalogueEntryBase 
{ 
public: 
    typedef std::map< std::string, ObjectCatalogueEntryBase* > CatalogueType; 

    ObjectCatalogueEntryBase(){} 
    virtual std::unique_ptr<Base> Allocate(std::string const &) = 0; 
    virtual ~ObjectCatalogueEntryBase(){} 

    static CatalogueType& GetCatalogue() 
    { 
    static CatalogueType catalogue; 
    return catalogue; 
    } 

    static std::unique_ptr<Base> Factory(const std::string& objectTypeName, std::string const & name) 
    { 
    std::cout<<"Creating solver of type: "<<objectTypeName<<" name "<<name<<std::endl; 
    ObjectCatalogueEntryBase* const entry = GetCatalogue().at(objectTypeName); 
    return entry->Allocate(name); 
    } 

}; 


template< typename TYPE > 
class ObjectCatalogueEntry : public ObjectCatalogueEntryBase 
{ 
public: 
    ObjectCatalogueEntry(): 
    ObjectCatalogueEntryBase() 
    { 
    std::string name = TYPE::CatalogueName(); 
    (ObjectCatalogueEntryBase::GetCatalogue())[name] = this; 
    std::cout<<"Registered Solver: "<<name<<std::endl; 
    } 

    ~ObjectCatalogueEntry() final{} 

    virtual std::unique_ptr<Base> Allocate(std::string const & name) final 
    { 
    return std::unique_ptr<Base>(new TYPE(name)); 
    } 
}; 

/// Compiler directive to simplify autoregistration 
#define REGISTER_FACTORY(ClassName) namespace{ ObjectCatalogueEntry<ClassName> reg_; } 

fichier suivant:

// Derived.h 
#include "Base.h" 
class Derived : public Base 
{ 
public: 
    Derived(std::string name); 
    ~Derived(); 
    static std::string CatalogueName() {return "Derived";} 
}; 

fichier suivant:

// Derived.cpp 
#include "Derived.h" 

Derived::Derived(std::string name):Base(name) 
{} 

Derived::~Derived() 
{} 

REGISTER_FACTORY(Derived) 

fichier suivant:

// main.cpp 
#include "Derived.h" 

int main() 
{ 
    std::string newName("Foo"); 
    auto solver = ObjectCatalogueEntryBase::Factory(Derived::CatalogueName(),newName); 
    return 0; 
} 

Et le Makefile:

CPP=g++-mp-6 

test: main.o Derived.o 
    ${CPP} -std=c++14 -o test main.o Derived.o 

testlib: main.o Derived.a 
    ${CPP} -std=c++14 -o testlib main.o Derived.a 

main.o: main.cpp Base.h Derived.h 
    ${CPP} -std=c++14 -c main.cpp 

Derived.o: Derived.cpp Derived.h 
    ${CPP} -std=c++14 -c Derived.cpp 

Derived.a: 
    ar qcsv Derived.a Derived.o 

clean: 
    rm *.o *.a test testlib 

all: test testily 

Deux exécutables sont donc liés. Le premier (test) est lié avec les fichiers d'objet et produit le résultat « correct »:

$ ./test 
Registered Solver: Derived 
Creating solver of type: Derived name Foo 

Le second (TestLib) est lié avec le fichier Derived.o remplacé par Derived.a, qui a été créé avec "ar" en utilisant seulement Derived.o. Le résultat est:

./testlib 
Creating solver of type: Derived name Foo 
terminate called after throwing an instance of 'std::out_of_range' 
    what(): map::at 
Abort trap: 6 

De toute évidence, l'enregistrement ne s'est pas produit ici, et la carte est vide. Même résultat avec gcc6, et apple clang7. Je soupçonne que cela a quelque chose à voir avec la carte statique globale, mais je ne comprends pas assez "ar" pour savoir ce que l'on retire du fichier objet. Donc, il y a deux questions:

  1. pourquoi la version de l'archive échoue?
  2. Comment puis-je générer un fichier d'archive qui apparaîtra comme un fichier objet du point de vue des liens?

Répondre

1

Le problème n'est pas l'archiveur, c'est l'éditeur de liens. L'éditeur de liens prend chaque fichier objet, plus ce qui est nécessaire à partir des archives. Les membres de votre archive ne résolvent pas les références non résolues et ne sont donc pas nécessaires.

Le lieur gnu comprend --whole-archive, ce qui est ce que vous vouliez faire ici.

+0

Dans darwin (mac osx), l'éditeur de liens du système n'a pas "--whole-archive". Au lieu de cela, il y a une commande "-all_load" qui semble fonctionner. Merci! – doc07b5