dlsym rückgabewert casten
-
Hallo,
ich habe ein shared object erstellt, und mittels dlopen geladen. Danach habe ich mit dlsym versucht eine funktion zu laden, die mir einen zeiger auf eine klasse zurückgibt, welche ich mit static_cast zu casten versucht habe.
Wenn ich jetzt versuche, eine methode aus dieser gecasteten classe zu laden, bekomme ich die fehlermeldung "Speicherzugriffsfehler".
Kann mir jemand sagen, wo ich einen fehler gemacht habe?
Danke!
Hier der source der klasse, welche das plugin lädt:
#include <iostream> #include <dlfcn.h> #include "testplugin/testplugin.h" int main() { void *handle = dlopen("libtestplugin.so", RTLD_NOW); if (!handle) return 0; void *pluginInstance = dlsym(handle, "createClassInstance"); if (!pluginInstance) { dlclose(handle); return 0; } TestPlugin *tp = static_cast<TestPlugin *>(pluginInstance); if (tp) tp->printTestMessage(); dlclose(handle); return 0; }Header des plugins:
#ifndef TESTPLUGIN_H_ #define TESTPLUGIN_H_ class TestPlugin { public: TestPlugin(); virtual ~TestPlugin(); void printTestMessage(); }; extern "C" TestPlugin *createClassInstance(); #endif /* TESTPLUGIN_H_ */Source des Plugins:
#include "testplugin.h" #include <iostream> TestPlugin::TestPlugin() { } void TestPlugin::printTestMessage() { std::cout << "Plugin geladen!" << std::endl; } TestPlugin::~TestPlugin() { } extern "C" TestPlugin *createClassInstance() { return new TestPlugin; }
-
C++-Objekte über eine Shared Library (=reines C-Interface) zu schieben ist immer eine gute Idee - zumindest wenn man lustige Crashs und undefiniertes Verhalten haben möchte.
-
Wie soll ich sonst ein plugin-system realisieren, wenn ich nur dlopen/dlsym habe?
-
Das Problem liegt nicht bei dlopen() und dlsym(), die sind vollkommen in Ordnung genau so wie das Konzept der Shared Libraries. Das Problem liegt darin, dass diese nur für den Export von C-Funktionen gedacht sind, du aber C++-Objekte verwendest.
Das KANN gut gehen, muss aber nicht. D.h. unter Umständen funktioniert es, unter Umständen reicht aber auch schon irgend eine Compileroption, die bei Plug-In und Hauptprogramm anders gesetzt ist, um dir die seltsamsten Effekte zu bescheren. Von unterschiedlichen Compilerversionen ganz zu schweigen...
-
Donggger schrieb:
Das Problem liegt darin, dass diese nur für den Export von C-Funktionen gedacht sind, du aber C++-Objekte verwendest.
Was wäre eine alternative für dlopen, damit das mit c++ objekten funktioniert?
-
Steini79, hör nicht auf Donggger.
Dein Problem hat eine simple Ursache:
dlsym ruft deine Funktion nicht auf, sondern gibt lediglich einen Funktionszeiger zurück.
Deine "plugInstance" ist also kein Zeiger auf ein Objekt deiner Klasse sondern eben ein Funktionszeiger.So wäre es also fast richtig:
typedef TestPlugin * (*createClassInstancePtr)(); // ... createClassInstancePtr createClassInstance = (createClassInstancePtr)dlsym(handle, "createClassInstance"); TestPlugin * instance = createClassInstance();fast, weil der Standard nicht garantiert, dass ein Funktionszeiger genauso groß ist wie ein Objektzeiger (z.B. void*, int*, TestPlugin* ...).
dlsym stellt eine Inkompatibilität zwischen POSIX und ANSI dar und wurde auch schon mehrfach im Netz diskutiert.Korrekt wäre also, in dein Shared Object sowas zu packen:
// für plugin und programm sichtbar // z.B. in einem Header struct exported_functions { TestPlugin * (*createClassInstance)(); }; // nur im Plugin // global! exported_functions public_interface = { &createClassInstance // Zeiger auf deine Funktion }; // und das kommt in das Programm: exported_functions * functions = static_cast<exported_functions*>(dlsym(handle, "public_interface")); TestPlugin * instance = functions->createClassInstance();Nur so gehst du dem Problem der Zeigergröße aus dem Weg.
Eventuell musst du die globale Instanz der exported_functions Struktur noch in einen extern "C"-Block stecken, da bin ich mir gerade nicht sicher :p
Einfach ausprobieren, wie es functioniert
-
DrakoXP schrieb:
Steini79, hör nicht auf Donggger.
Dein Problem hat eine simple Ursache:
dlsym ruft deine Funktion nicht auf, sondern gibt lediglich einen Funktionszeiger zurück.Richtig, einen Funktionszeiger, kein Objekt. Weil Bibliotheken nun mal nicht für Objekte vorgesehen sind - deswegen hat MS ja das unselige COM/DCOM erfunden.
Und warum genau soll er jetzt nicht auf mich hören? Willst du ihn lieber weiter in die falsche Richtung laufen lassen, bis ihm seine Objekte über die C-Schnittstelle irgendwann viel, viel später um die Ohren fliegen?
-
Danke für deine hilfe, ich werde mir das genauer ansehen.
@Donggger
Ich nehme mir beide argumentationen zu herzen, gibt es dann eine alternative zu dlopen/dlsym?
-
Steini79 schrieb:
Ich nehme mir beide argumentationen zu herzen, gibt es dann eine alternative zu dlopen/dlsym?
dlopen/dlsym passt schon. Du musst nur dein Interface aendern.
Statt
foo->bar()
schreibst du ab jetzt
bar(foo);Sprich: du machst dein Interface einfach Kompatibel zur verwendeten technology. Das bedeutet lustigerweise naemlich auch gleich, dass ich mit so ziemlich jeder Sprache dein shared object nutzen kann, nicht nur mit C++.
Und das ganze ist sowieso nur Syntax um die es geht - die Funktionalitaet bleibt eh gleich.
Das Shared Object exportiert eine create Funktion die einen void* returned. Intern macht sie natuerlich new Foo(), liefert das Foo Objekt aber nur als void* raus. Oft versteckt man den void* hinter einem typedef, damits besser aussieht.
Wenn du nun foo->bar() aufrufen willst, dann exportiert das shared object eine Funktion bar die einen void* nimmt (als this Zeiger) und so implementiert ist:
static_cast<Foo*>(foo)->bar();so hast du auch weiter die komplette polymorphie erhalten die du in deinem C++ Code hast.
-
Donggger schrieb:
Das KANN gut gehen, muss aber nicht. D.h. unter Umständen funktioniert es, unter Umständen reicht aber auch schon irgend eine Compileroption, die bei Plug-In und Hauptprogramm anders gesetzt ist, um dir die seltsamsten Effekte zu bescheren. Von unterschiedlichen Compilerversionen ganz zu schweigen...
Und warum genau soll er jetzt nicht auf mich hören? Willst du ihn lieber weiter in die falsche Richtung laufen lassen, bis ihm seine Objekte über die C-Schnittstelle irgendwann viel, viel später um die Ohren fliegen?
Die Berichte von auf Grund von unterschiedlichen Compiler-Switches explodierten Programmen sind stark übertrieben.
Es gibt andere Dinge die schon weniger egal sind, wie z.B. die Sache mit unterschiedlichen CRT Versionen, Heap etc.
Lässt sich aber auch alles regeln.Es gibt Fälle wo C-Interfaces angesagt sind, und es gibt Fälle wo C++ Interfaces angesagt bzw. zumindest vollkommen OK sind. Die Grundsätzliche "C++ Interfaces sind pöse" Panikmache kann ich nicht ganz nachvollziehen.
-
hustbaer schrieb:
Die Grundsätzliche "C++ Interfaces sind pöse" Panikmache kann ich nicht ganz nachvollziehen.
Na dann schreib' mal eine DLL mit Visual Studio die Objekte nutzt. Und viel Spaß dann mit dem Support für deine Kunden, die eine Applikation mit MingW compilieren und denen deine DLL bei jedem Zugriff um die Ohren fliegt.
Oder auch viel Spaß mit denen, die zwar Visual Studio verwenden, aber einen Debug-Build verwenden wollen. Erkläre denen mal, dass du keinen Debug-Build deiner DLL auslieferst und sie deswegen ihre Applikation auch nur als Release-Build debuggen können.
-
Komm lass gut sein und troll hier nicht rum.
Wenn du diskutieren willst dann zitier ordentlichEs gibt Fälle wo C-Interfaces angesagt sind, und es gibt Fälle wo C++ Interfaces angesagt bzw. zumindest vollkommen OK sind. Die Grundsätzliche "C++ Interfaces sind pöse" Panikmache kann ich nicht ganz nachvollziehen.
-
Danke an alle für die infos! Das hilft mir weiter.
@Donggger
Der vollständigkeit halber würde ich auch gerne deine argumentation verstehen, dazu müsste ich aber wissen, wie du die sache mit anstatt mit dlopen/dlsym lösen würdest?
-
Steini79 schrieb:
dazu müsste ich aber wissen, wie du die sache mit anstatt mit dlopen/dlsym lösen würdest?
Den Ersatz für dlopen()/dlsym() den du suchst, gibt es nicht. Wenn es um Shared Libraries geht, kannst du nur diese Funktionen verwenden und du kannst dir nur C-Funktionspointer mit C-Datentypen als Parameter zurückgeben lassen.
Der springende Punkt ist: Shared Libraries sind genau dafür gedacht, für Objekte waren sie nie vorgesehen. Wenn du wirklich Objekte über ausgelagerte Programmkomponenten ziehen willst und eine saubere Lösung suchst, darfst du generell keine Shared Library verwenden, sondern müsstest dich beim COM-Interface (nicht zu verwechseln mit der seriellen Schnittstelle) bedienen. Und das gibt es nur bei Windows.
-
Donggger schrieb:
Wenn du wirklich Objekte über ausgelagerte Programmkomponenten ziehen willst und eine saubere Lösung suchst, darfst du generell keine Shared Library verwenden, sondern müsstest dich beim COM-Interface (nicht zu verwechseln mit der seriellen Schnittstelle) bedienen. Und das gibt es nur bei Windows.
Schwachsinn. Das geht mit dem Posix-Interface wunderbar und wohldefiniert.
-
@Donggger
Du verwechselst "sauber" mit "so flexibel wie möglich".
Es ist nichts unsauberes daran einen Compiler (plus evtl. nötige Einstellungen) vorzuschreiben.
COM funktioniert ja auch nur weil alle (relevanten) Windows-Compiler sich an das "COM kompatible" VTable Layout von MSVC halten.
MS verwendet sogar im Kernel-Mode diverse "virtuelle" Interfaces.Das selbe kann man auch schön mit GCC unter Linux/BSD/... machen. Wird auch gemacht. Funktioniert auch gut.
Es ist natürlich nicht so flexibel wie ein reines C-Interface, weil es eben gewisse Restrictions bezüglich verwendbarer Compiler etc. bedeutet, aber wie schon gesagt: es ist nichts "unsauberes" daran.
-
314159265358979 schrieb:
Donggger schrieb:
Wenn du wirklich Objekte über ausgelagerte Programmkomponenten ziehen willst und eine saubere Lösung suchst, darfst du generell keine Shared Library verwenden, sondern müsstest dich beim COM-Interface (nicht zu verwechseln mit der seriellen Schnittstelle) bedienen. Und das gibt es nur bei Windows.
Schwachsinn. Das geht mit dem Posix-Interface wunderbar und wohldefiniert.
Welches Posix-Interface meinst du

-
hustbaer schrieb:
Welches Posix-Interface meinst du

Na das, von dem wir (fast) die ganze Zeit reden: dlopen, dlssym, ...
-
314159265358979 schrieb:
hustbaer schrieb:
Welches Posix-Interface meinst du

Na das, von dem wir (fast) die ganze Zeit reden: dlopen, dlssym, ...
Heißt das, unter Windows gibt es keine sichere/saubere/... Lösung?
-
Nö, hab ich das irgendwo geschrieben? -.-
-
314159265358979 schrieb:
Donggger schrieb:
Wenn du wirklich Objekte über ausgelagerte Programmkomponenten ziehen willst und eine saubere Lösung suchst, darfst du generell keine Shared Library verwenden, sondern müsstest dich beim COM-Interface (nicht zu verwechseln mit der seriellen Schnittstelle) bedienen. Und das gibt es nur bei Windows.
Schwachsinn. Das geht mit dem Posix-Interface wunderbar und wohldefiniert.
Nope. Geht leider nicht so einfach. Weil wenn du zB den gcc verwendest und ich den llvm dann tut das ganz dicke aua machen wenn wir versuchen C++ Objekte zu sharen

Aber insofern hast du recht, es ist wohldefiniert was passiert: naemlich ein PENG BUMM BAENG.
C++ Objekte ueber Modulgrenzen hinweg sharen ist deshalb doof, weil C++ keine definierte ABI hat. Da gibt es viele Fallstricke deshalb ratet man davon ab.
Wo es Sinn machen kann ist innerhalb eines Projektes - wenn man das Buildenvironment kontrolliert.