2012-06-24 10 views
5

Sto provando a scrivere un'applicazione che carica le sue estensioni in modo dinamico durante il runtime. Ho usato la libreria Boost Preprocessor per scrivere una funzione di preprocessore che, dato un elenco di nomi, dichiara una classe per ogni nome (e crea tutte sottoclassi di alcune classi di AbstractPlugin) e quindi dichiara una sequenza di MPL Boost contenente tali classi. Poi ho scritto una classe che prova un puntatore a AbstractPlugin se può essere lanciato su uno qualsiasi dei tipi in quella sequenza MPL. Il problema qui è che la mia funzione di preprocessore ha bisogno di un elenco completo di tutte le estensioni che voglio creare e caricare. C'è qualche tecnica che mi consente di registrare ciascuna estensione in un file separato?Registrare una classe C++ in modo che successivamente una funzione possa iterare su tutte le classi registrate

Aggiornamento:

credo, la mia spiegazione della situazione era troppo vago, così ho deciso di renderla più specifica.

Vorrei definire una raccolta di tipi di interno. Per ogni tipo di estensione potrebbe esserci un numero qualsiasi di estensioni. Durante il runtime il programma carica la libreria esterna, risolve la funzione del punto di ingresso, la chiama e, come risultato, ottiene un puntatore. Quindi cerca di trasmettere quel puntatore a tutti i tipi di estensione registrati (usando dynamic_cast, quindi le classi per i tipi di estensione ereditano tutte da una classe di base polimorfa). Se un cast per un tipo di estensione ha esito positivo, il puntatore cast viene utilizzato in una chiamata al gestore speciale per quel tipo di estensione.

Il numero di tipi di estensione è noto al momento della compilazione (mentre, ovviamente, il numero di estensioni è infinito). Usando il mio aproach la classe loader usa questa conoscenza per verificare se esiste un gestore per ogni tipo di estensione (in caso contrario, il programma non viene compilato). Inoltre, il mio aproach non obbliga le classi per i tipi di estensione a sapere nulla del caricatore (quindi è facile modificare il loader). Ma sarebbe più comodo se ogni tipo di estensione fosse registrato da solo.

+0

Sta generando un'intestazione una soluzione accettabile? – Arpegius

risposta

0

A quanto pare, quello che voglio è impossibile. La ragione per cui "registro" in questo contesto significa "metti un tipo nella sequenza del tipo" e le sequenze di tipo sono immutabili perché sono i tipi stessi. Quindi si dovrebbe creare manualmente questa sequenza di tipi, o come qualcuno suggerisce di spostare la "registrazione" in runtime.

0

Non è difficile implementare una simile struttura di estensione utilizzando il modello di fabbrica astratto.

http://en.wikipedia.org/wiki/Abstract_factory_pattern

È possibile registrare quelle astratte funzioni factory/oggetti in un elenco globale, e fare tutto ciò che vuoi fare di base su di esso.

8

È possibile rendere tutte le classi registrate automaticamente in una sorta di raccolta. Ecco un approccio scheletro:

Base.hpp:

#include <memory> 
#include <unordered_map> 
#include <string> 

struct Base 
{ 
    virtual ~Base() = default; 

    using create_f = std::unique_ptr<Base>(); 

    static void registrate(std::string const & name, create_f * fp) 
    { 
     registry()[name] = fp; 
    } 

    static std::unique_ptr<Base> instantiate(std::string const & name) 
    { 
     auto it = registry().find(name); 
     return it == registry().end() ? nullptr : (it->second)(); 
    } 

    template <typename D> 
    struct Registrar 
    { 
     explicit Registrar(std::string const & name) 
     { 
      Base::registrate(name, &D::create); 
     } 
     // make non-copyable, etc. 
    }; 

private: 
    static std::unordered_map<std::string, create_f *> & registry(); 
}; 

Base.cpp:

#include "Base.hpp" 

std::unordered_map<std::string, Base::create_f *> & Base::registry() 
{ 
    static std::unordered_map<std::string, Base::create_f *> impl; 
    return impl; 
} 

Ora per utilizzare questo in un client:

derivata. hpp:

#include "Base.hpp" 

struct Derived : Base 
{ 
    static std::unique_ptr<Base> create() { return std::make_unique<Derived>(); } 
    // ... 
}; 

Derived.cpp:

#include "Derived.hpp" 

namespace 
{ 
    Base::Registrar<Derived> registrar("MyClass"); 
} 

Il costruttore del Base::Registrar<Derived> si occupa di registrare la classe Derived sotto il nome "MyClass".È possibile creare istanze di Derived dinamicamente tramite:

std::unique_ptr<Base> p = Base::instantiate("MyClass"); 

Il codice potrebbe/dovrebbe essere migliorata rilevando ripetute registrazioni, la stampa di un elenco di classi disponibili, ecc Nota come abbiamo evitare qualsiasi problema di inizializzazione di ordinazione statici mia rendendo l'attuale registro mappa oggetto un oggetto block-static, che è garantito per essere inizializzato prima del suo primo utilizzo, e quindi distrutto solo dopo il suo ultimo utilizzo.

+0

Ho considerato qualcosa di simile, ma sono più interessato ad un aggiornamento in fase di compilazione. Non sono sicuro che esista, ma la terra dei modelli C++ è piena di miracoli. – grisumbras

+0

Non ce n'è uno statico. Non è possibile fare in modo che le classi si registrino da sé, poiché ogni file di implementazione ha la propria unità di compilazione e ciascuna può essere collocata in una libreria diversa. La potenza di questa soluzione è che quando carichi una libreria dinamica, la classe di estensione diventa automaticamente disponibile. È un modello ben noto e molto utile. – Arpegius

+0

Questa è un'implementazione molto bella. L'unica cosa che non capisco è il = 0 sulla funzione statica create(). Per quanto ne so, questo è C++ invalido dal momento che le funzioni statiche non possono mai essere virtuali. Come dovrebbe funzionare quella parte? – Shenjoku

Problemi correlati