2011-01-17 10 views
3

Ich habe Tage damit verbracht, alle Tutorials zu lesen und zu lesen, die ich zu diesem Thema gefunden habe, und verbrachte Stunden (und sogar Tage) damit, verwandte Fragen hier in SO zu durchstöbern, aber ich kann immer noch nicht alles zum Laufen bringen. Akzeptieren Sie meine Entschuldigung, wenn es sich um ein Duplikat handelt: Die Chancen stehen gut, dass ich die Duplikatsfragen oft gesehen und wieder gelesen habe, aber die Relevanz der Antworten auf mein Problem nicht verstehen konnte. Mit diesem aus dem Weg ...Wie manipuliere/gebe ich Daten mit dynamisch geladenen Funktionen zurück (dlopen)?

Ich versuche, eine Plugin-Architektur für meine Anwendung zu implementieren. Plugins werden kompiliert und als Bibliotheken installiert. Zur Laufzeit verwendet die Anwendung dann dlopen()/dlsym(), um die Funktionen des Plugins zu laden und zu verlinken.
Die Idee ist, dass Plugins (Bibliotheken) eine Reihe von Funktionen implementieren, um Daten an die Hauptanwendung zurückzugeben, oder manipulieren Daten von der Anwendung übergeben.

Um diese Idee zu testen, habe ich versucht, eine Funktion (innerhalb des Plugins) zu implementieren, die den (lesbaren) Namen des Plugins selbst (als std :: string) zurückgeben würde. Ich dachte, dass wäre etwas einfach, mit zu beginnen ....: -/

Hier ist, was ich bisher habe:

// 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. 

ich viele verschiedene Kombinationen ausprobiert habe, darunter eine, die mehr straigtforward schien (aber nicht funktionierte besser), wo der Plugin-Name zurückgegeben wird:

ich weiß, ich bin in der Nähe: der Code kompiliert und die Anwendung gestoppt Absturz;)
aber ich habe eine leere Platz, wo ich erwarten würde, das tatsächliche plugin nam zu sehen e.

Alle Tutorials, die ich bisher gelesen habe, gehen sehr schnell über den Mechanismus, mit dem Daten in beide Richtungen übergeben werden: Plugin < => Anwendung. Was ich mit einer "einfachen" std :: string versuche, möchte ich später mit viel komplexeren Objekten machen (d. H. Eine Plugin-Funktion würde ein Objekt als Referenz nehmen und einige seiner Eigenschaften ändern). Die Tutorials halten mehr oder weniger an dem Punkt an, an dem mit dlsym() ein Zeiger erzeugt wird, und geben nicht viele Beispiele zur Verwendung dieses Zeigers.

Also, wie all das zu tun?

Eine weitere relevante Frage: verwende ich eine gemeinsame Kopfzeile, die ich sowohl mit der Anwendung und mit dem Plugin verwenden würde und wo würde ich die Signatur der Funktion Anrufe definieren? Wie würde ich das machen und wie würde das helfen?

+0

Können Sie es mit einem normalen 'char *' arbeiten lassen? – genpfault

+0

@genpfault: lass mich versuchen und ich komme zurück zu dir;) Ich habe mich gefragt, ob die Verwendung einer std :: string (eine C++ - Klasse) Probleme verursacht ... – augustin

+0

Ich habe ein paar Kommentare zu meiner Antwort – ssmir

Antwort

5

Die Signatur einer Funktion wird aus ihren Namen und Argumenttypen generiert (der Rückgabewert spielt keine Rolle). Wenn Sie eine Funktion mit externem "C" deklarieren, wird das Namensschema des C-Symbols verwendet, das offensichtlich nicht mit C++ - Typen wie std :: string umgehen kann. Aus diesem Grund funktioniert das Übergeben von std :: string als Argument nicht.

Ich kann nicht erklären, warum die Rückkehr von std :: string nicht funktioniert. Vielleicht werden andere Aufrufkonventionen verwendet.

Wie auch immer die korrekte Methode zum Importieren von C++ - Code aus einer gemeinsam genutzten Bibliothek besteht darin, Zeiger von Anfangspunkten an C++ - Typen zurückzugeben. Und diese Einstiegspunkte müssen Argumente mit in C verfügbaren Typen haben. (Einstiegspunkt ist eine dokumentierte Funktion, die aus einer gemeinsam genutzten Bibliothek exportiert wurde)

Here ist ein guter Artikel über grundlegende Aspekte des Ladens von C++ - Klassen aus gemeinsam genutzten Bibliotheken. Dieser Artikel beantwortet Ihre Frage gründlich.

Bitte beachten Sie, dass bei der Verwendung von Ausnahmen, die von einer gemeinsam genutzten Bibliothek an die Hauptanwendungen gesendet werden, Fehler auftreten. Und mit dynamic_cast von Objekten, die in einer Bibliothek erstellt wurden. Ich habe diese Themen erwähnt, damit Sie etwas vorbereitet sein können, wenn Sie mit diesen Problemen konfrontiert werden.

[Bearbeiten]

Um meine Antwort mehr deutlich zu machen, ich ein paar Beispiele hinzufügen würde.

Um den Namen des Plugins erhalten Sie verwenden können:

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 

wirklich die Plugin-Funktionalität nutzen zu können, eine Basisklasse in einem Kopf erklären sollte:

// 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; 
} 
2

Versuchen:

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); 

Wie Sie eine Kopie des Zeigers zuweisen Sie als Argument übergeben, nicht die, die Sie zuweisen soll.

EDIT Hier gehen Sie: Datei 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; 
} 

Datei plugin.cpp

#include <string> 

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

Nur ein Wort der warnen.Obwohl dies kompiliert und ausgeführt wird, ist das Übergeben von C++ - Typen über die DLL-Grenze riskant und der obige Code ist nur dein Code, der zum Kompilieren und Ausführen fest genug ist, er ist nicht sicher und hat eine sehr explizite Speicherbehandlung. Vielleicht möchten Sie das Problem auf andere Weise angehen.

+0

Fehler: kann nicht 'Std :: String **' in 'Std :: String *' in Argument übergeben – augustin

+0

theoretisch verstehe ich Ihren Punkt. Wenn ich versuche, was Sie vorschlagen, bekomme ich den Kompilierungsfehler, der oben im Kommentar erwähnt wird. Wenn ich andere Sachen in den gleichen Zeilen versuche, habe ich einen segfault zur Laufzeit ...: - / – augustin

1

haben Sie eine Lese von this question und seine Antworten. In C++ gibt es viele Möglichkeiten für Inkompatibilitäten zwischen den gemeinsam genutzten Libgrenzen.