in C ++, come chiamare una routine 'init' come viene caricata una classe?

2

Ho un programma che carica un sacco di "addon" (diversi misuratori che misurano e visualizzano valori di sistema).

Gli "addons" sono "cresciuti" nel tempo e il codice di quali addon "attivare", e quanti di ciascuno (se c'è più di uno dei "dispositivi" nel sistema (cpu, interfacce di rete, ecc.) è codificato nel master. Ug (e ick).

Quello che voglio è che ogni addon-meter inserisca il suo nome e init-routine-addr in un array mentre vengono caricati. In questo momento, quali metri sono stati caricati sono fissi nel master in fase di compilazione. Quello che voglio è per la possibilità di caricamento dinamico, ma ancora più importante, per "estensioni di script" esterne (che potrebbe misurare "quasi tutto" e contare sul master da visualizzare i dati raccolti (come uno script che legge la temperatura esterna tramite alcuni dispositivi di terze parti). In altre parole, molto tempo dopo che il maestro è stato compilato, voglio fornire la possibilità di aggiungere altri misuratori di addon purché forniscano una specifica API callable.

Quindi, con l'aggiunta di una nuova estensione, ho bisogno di richiamare qualche routine init come carica che posizionerà il nuovo nome-contatore e la routine da chiamare in un class singleton che ha una matrice di nomi di estensione e i loro indirizzi di routine init (come in un'estensione caricata dinamicamente).

Quindi quando inizia l'esecuzione, ogni estensione avrà già "registrato" stesso con una lista centrale che il "maestro" può vedere e sapere cosa chiamare.

In perl, ho usato qualcosa in un "BEGIN {...} block" in estensioni caricate per chiamare un Plug_Common->register("plugin name", \%Plugin_Description_Data); , quindi, quando la routine principale è iniziata, nel registro del plugin dovrebbe apparire ciò che era disponibile, dinamicamente, in fase di runtime.

La domanda è, come faccio a farlo in C ++?

Ho impostato la routine di registrazione, ma non ho idea di come avere le estensioni ottenere il proprio "nome e indirizzo" registrato prima dell'esecuzione del master principale. Teoricamente, poiché alcune estensioni potrebbero essere aggiunte tramite script molto tempo dopo è stato scritto il master principale - non sarebbe stato in grado di conoscerne i nomi in anticipo.

In cima alla mia testa, posso solo pensare a un file di testo statico "modificato dall'utente", che potrebbe essere in grado di avere il master, "in qualche modo (?!)" chiama ogni init init e la routine 'get_config' . Quindi posso sbarazzarmi di tutta la conoscenza "estensione / metro-specifica" dal maestro. Non mi piace l'idea del file di testo poiché è troppo soggetta a errori utente - avendo qualcosa che è in grado di chiamare automaticamente una funzione di registro mentre viene caricata, sembra molto meno probabile che abbia problemi come il file di configurazione statica fuori sincrono con quali estensioni possono essere utilizzate sul sistema.

Qualche idea? (Preferirei piuttosto non dover creare una copia di qualcosa come perl per gestire la "caricabilità" dinamica dei moduli ... per me sarebbe "brutto" ...

Sembra un po 'inaffidabile per il programma principale cercare di esaminarne alcuni tabella dei simboli in fase di esecuzione, in particolare con la strong digitazione di C ++. Continuo a venire con difetti praticamente in ogni metodo che penso. Triste. Qualcuno conosce un modo migliore? ;-) ...

Grazie mille!

Aggiornamento: Ho avuto un pensiero (beh più di uno, ma questo è un po 'non convenzionale, ma potrebbe funzionare) E se ognuno dei plug-in dichiarasse un puntatore condiviso a un elenco globale nel master?) Se i puntatori condivisi sono stati utilizzati a livello di modulo , quindi il 'conteggio degli usi' condiviso non dovrebbe essere incrementato quando i moduli sono stati caricati? ... hmmm ... Non so ancora come ottenere gli indirizzi dei moduli nell'array principale (senza che questi ottengano alcun tempo di esecuzione ), ma se riesco a capire un modo per un'istruzione di inizializzazione usando il parametro shared_ptr, poi dal momento che lo sto caricando come non-multithread (almeno a questo punto) se quel puntatore potrebbe essere sfruttato per incrementare e memorizzare l'indirizzo di una funzione init, in un array globale, potrebbe darmi la mia configurazione dinamica? In questo momento, l'unica cosa che so per certo è che i puntatori condivisi hanno un 'use_count' accessibile - il che mi aiuterebbe solo a determinare quanti plug-in sono stati registrati. Forse non c'è modo di farlo funzionare, ma solo un pensiero (alle 5:45 del mattino dopo che sono stato sveglio tutta la notte ... quindi, potrebbe essere completamente imperfetto e io proprio non lo vedo ancora ...; )) ... Comunque, ho pensato di aggiungere i miei ultimi pensieri; Per gli script, sto pensando di suddividere quel tipo di plugin in un gestore separato che dovrebbe leggere alcune configurazioni ... Non mi piace molto, ma isola quelle cose che non caricheranno l'ora di inizio del programma @ ... Qualcuno pensa che sia possibile sfruttare il puntatore condiviso per farlo?

    
posta Astara 11.03.2015 - 22:57
fonte

3 risposte

5

Nella maggior parte dei sistemi di plugin in C e C ++, il caricamento dei plugin funziona così:

L'applicazione principale specifica un percorso in cui posizionare i plug-in (o consente di configurare tale percorso). All'avvio, l'applicazione cerca librerie caricabili dinamicamente (DLL su Windows, .so su Linux, in caso di plugin compilati) o script (in caso di plugin interpretati / script) nel percorso dell'estensione.
Per ciascun plug-in individuato, l'applicazione tenta di richiamare una funzione init / registration che consente al plug-in di registrarsi con l'applicazione. Per i plugin interpretati / script, questo potrebbe essere il corpo principale dello script.

Per supportare plugin interpretati / script, l'applicazione deve incorporare un motore di scripting che comprenda la lingua in cui è scritto il plugin.

Quando si scrive un plug-in compilato, una delle regole è che la si costruisce come una libreria caricabile dinamicamente e che si fornisce la funzione di inizializzazione con il nome esatto e la firma che l'applicazione principale si aspetta.
Inoltre, qualsiasi classe fornita dal plugin deve essere derivata da un'interfaccia specificata dall'applicazione principale per i plug-in che forniscono un determinato servizio.

Per un plug-in script, il requisito è che la funzione di inizializzazione esegua le inizializzazioni e le registrazioni previste dall'applicazione principale.

    
risposta data 12.03.2015 - 08:40
fonte
2

The question is, how do I do this in C++?

Non sarai in grado di mappare l'inizializzazione di Perl in C ++ senza renderla esplicita.

Componenti per un sistema di plugin in C ++:

  • definisce l'interfaccia del plugin come file di intestazione (probabilmente conterrà la dichiarazione di una funzione / classe di inizializzazione del plugin, un'interfaccia di classe di base di configurazione e un factory di classe, un'interfaccia di base di base plugin e una factory di classe plugin (o simile) )). Questo file di intestazione verrà utilizzato da entrambi i plugin e dall'applicazione principale, per capirsi.

  • crea un plug-in vuoto che implementa completamente questa interfaccia e conservalo come riferimento [valido per manutenzione, sviluppo di plug-in, prova di concetto e esempio di documentazione].

  • scrivi una libreria client di alto livello per l'applicazione principale, per lavorare con i plugin. Verifica che carichi il tuo plugin vuoto [utile per semplificare il lavoro con i plugin nell'applicazione master].

  • descrive i plugin in un semplice file di testo / json / xml. Mantieni il formato il più semplice possibile. Ciò ti consentirà di aggiungere diversi plugin alla tua applicazione, in fase di esecuzione (perché è molto più semplice modificare una riga in un file di configurazione e aggiungere un commento, piuttosto che ricordare dove devi spostare le librerie dinamiche sul tuo disco rigido.

Istruzioni per lo sviluppo di un nuovo plug-in basato su quello vuoto e aggiornamento dei passaggi nei documenti.

Modifica (commenti indirizzati):

My prototype code already has the interface being pure-virtual, so I can use dynamic_casting at run time to get at the needed data

Non farlo (la cosa dynamic_cast). Invece, definire un protocollo più ricco tra l'applicazione e i plugin. Definisci (ad esempio) un protocollo basato su eventi nell'interfaccia virtuale e chiama attraverso questo:

struct your_plugin_class
{
virtual void on_init() = 0; // called from the application, on initialization
};

My main problem right now, is how to have plugins "register" themselves (in so much as to putting their init-function in a call-array, that the master can use to dynamically adapt to whatever list of plugins have been inserted on a given run -- without recompilation.

Crea un gestore di plugin che apra file in una determinata posizione, con un nome di corrispondenza del modello (ad esempio, "fai- e-questa.prima-app.plugin.so" - qualcosa che si adatta bene a un normale espressione). All'interno di questa libreria, chiama semplicemente un factory di classe personalizzato.

Potresti persino avere il gestore plugin come un oggetto attivo e mantenere un elenco in memoria dei plugin caricati. Periodicamente, iterate i file del plugin e, se ci sono differenze, caricateli sul thread dell'oggetto attivo. La tua applicazione sceglierebbe i plug-in in fase di esecuzione.

    
risposta data 12.03.2015 - 10:58
fonte
1

Su Linux, se si compila codice con GCC, il costruttore di dati statici viene eseguito su dlopen (3 ) tempo. Vedi anche l'attributo visibilità e lo specifico GCC __attribute__((constructor)) per le funzioni.

Tuttavia, dovresti definire una convenzione plugin : quali nomi (per le funzioni extern "C" C ++ all'interno dei plugin) vengono utilizzati, con quali firme, in quale ordine, ecc. .

Ad esempio, potresti decidere che ogni plug-in dovrebbe avere una funzione extern "C" void my_plugin_init(void); che verrà eseguita dal tuo programma (dopo averne dlsym -ed) durante il caricamento di un plug-in, ecc ... Quella my_plugin_init potrebbe registrare alcuni altre funzioni (magari aggiungendole ad alcune variabili globali std::map<std::string,std::function<BaseClass*(int)>> , ecc ....)

Il framework Qt Plugin potrebbe ispirarti.

    
risposta data 12.03.2015 - 14:04
fonte

Leggi altre domande sui tag