Domanda

ho trascorso giorni leggendo e rileggendo ogni tutorial che ho trovato su questo argomento, e ha trascorso ore (e anche giorni) che stanno questioni connesse qui a SO, ma ancora non può ottenere la seguente al lavoro. Accettare le mie scuse se questo è un duplicato: è probabile che ho visto e riletto molte volte le domande duplicati, ma non riuscivo a capire la rilevanza delle risposte al mio problema. Con quella di mezzo ...

Sto cercando di implementare un'architettura a plugin per la mia applicazione. I plugin sono compilati e installati come librerie. In fase di esecuzione, l'applicazione utilizza quindi dlopen () / dlsym () per il carico e il collegamento alle funzioni del plugin.
L'idea è che i plugin (biblioteche) attueranno una serie di funzioni per restituire i dati per l'applicazione principale, o manipolare i dati passati dall'applicazione.

Al fine di testare questa idea, ho cercato di implementare una funzione (all'interno del plugin) che sarebbe tornato il (leggibile) il nome del plugin stesso (come uno std :: string). Ho pensato che sarebbe stato qualcosa di semplice per cominciare ....: - /

Ecco quello che ho ottenuto finora:

// Plugin.cpp
extern "C" void plugin_name(std::string *name) {
        name = new std::string("Example plugin name");
}

// Application.cpp
void* handle = dlopen("libplugin.so", RTLD_LAZY);
typedef void (*plugin_t)(std::string*);
dlerror(); // Reset errors.
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ... Some error handling code.
std::string my_plugin_name;
call_plugin_name(&my_plugin_name);
dlclose(handle);
// More code that displays my_plugin_name.

Ho provato molte combinazioni differenti, tra cui uno che sembrava più straigtforward (ma non ha funzionato meglio) in cui viene restituito il nome del plugin:

// Plugin.cpp
extern "C" std::string plugin_name(void) {
        return std::string("Example plugin name");
}

Lo so che sono vicino: i compila codice e l'applicazione ha smesso di schiantarsi;)
Tuttavia, ho uno spazio vuoto dove mi aspetto di vedere il nome del plugin effettivo.

Tutti i tutorial che ho letto finora andare molto rapidamente sopra il meccanismo con cui i dati vengono passati in entrambe le direzioni: plug <=> applicazione. Quello che sto cercando di fare con un "semplice" std :: string, desidero fare in seguito con molto più complessi oggetti (vale a dire una funzione plugin avrebbe preso un oggetto per riferimento e cambiare alcune delle sue proprietà). I tutorial più o meno tutti sosta al punto di creare un puntatore con dlsym () e non dare molto esempi su come utilizzare questo puntatore.

Quindi, come fare tutto questo?

Un'altra domanda pertinente: devo usare un colpo di testa comune che userei sia con l'applicazione e con il plugin e dove mi piacerebbe definire la funzione chiama firma? Come faccio a fare questo e come sarebbe che aiutano?

È stato utile?

Soluzione

La firma di una funzione è generato dal suo nome e argomenti tipi (tipo di valore di ritorno non ha importanza). Quando si dichiara funzione con extern "C", C schema di denominazione simbolo viene utilizzato, che a quanto pare non in grado di gestire C ++ tipi come std :: string. Questo è il motivo che passano std :: string come argomenti non funziona.

Non riesco a spiegare il motivo per il ritorno std :: string non funziona. Forse sono utilizzati differenti convenzioni di chiamata.

In ogni caso il modo corretto di importare codice C ++ da una libreria condivisa è quello di tornare puntatori ai tipi C ++ da punti di ingresso. E questo punti di ingresso devono avere argomenti con i tipi disponibili in C. (punto di ingresso è una funzione documentata esportata da una libreria condivisa)

Qui è un buon articolo su aspetti fondamentali del carico C ++ classi di librerie condivise. In questo articolo vi rispondere alla tua domanda accuratamente.

Si prega di notare che ci sono insidie ??quando si utilizza eccezioni generate da una libreria condivisa per le applicazioni principali. E con dynamic_cast di oggetti creati all'interno di una libreria. Ho citato questi temi in modo che si potrebbe essere un po 'preparati quando si affrontano questi problemi.

[modifica]

Per rendere la mia risposta più chiaro aggiungerò un paio di esempi.

Per ottenere il nome del plugin è possibile utilizzare:

extern "C" const char * plugin_name() {
    return "Example plugin name";
}

// main.cc:
void* handle = dlopen("libplugin.so", RTLD_LAZY);
// ...
typedef const char * (*plugin_t)();
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ...
std::string my_plugin_name(call_plugin_name());
// use it

Per davvero usare la funzionalità plug-in si dovrebbe dichiarare una classe base in un colpo di testa:

// plugin.h
class Plugin {
    public:
        virtual void doStuff() = 0;
        virtual ~Plugin() = 0;
};

// plugin.cc
Plugin::~Plugin() {
}

// myplugin.cc
class MyPlugin : public Plugin {
    virtual void doStuff() {
        std::cout << "Hello from plugin" << std::endl;
    }
};

extern "C" Plugin *createMyPluginInstance() {
    return new MyPlugin;
}

Altri suggerimenti

Prova:

 extern "C" void plugin_name(std::string **name) {
     *name = new std::string("Example plugin name");
 }

 ...

 std::string *my_plugin_name;
 call_plugin_name(&my_plugin_name);

Come si sta assegnando una copia del puntatore si passa come argomento, non quello che si voleva assegnare.

Modifica Qui si va: File main.cpp

#include <iostream>
#include <dlfcn.h>
#include <string>

// Application.cpp
int main() {
    void* handle = dlopen("libplugin.so", RTLD_LAZY);
    typedef void (*plugin_t)(std::string**);
    dlerror(); // Reset errors.
    plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
    // ... Some error handling code.
    std::string *my_plugin_name;
    call_plugin_name(&my_plugin_name);
    dlclose(handle);
    // More code that displays my_plugin_name.
    std::cout << "Plugin name is " << *my_plugin_name << std::endl;
    delete my_plugin_name;
    return 0;
}

File plugin.cpp

#include <string>

extern "C" void plugin_name(std::string **name) {
    *name = new std::string("example plugin name");
}

Solo una parola di avviso . Anche se questo viene compilato ed eseguito, passando tipi C ++ in tutto il boundry DLL è rischioso e il codice di cui sopra è solo il codice fisso abbastanza per compilare ed eseguire, non è sicuro e ha la gestione della memoria molto esplicito. Si consiglia di attaccare il problema in modo diverso.

Si prega di avere una lettura di questa domanda e le sue risposte. Ci sono molte opportunità per incompatibilità attraverso i confini lib condivisi in C ++.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top