Eigene Programmbibliothek zur Laufzeit laden

Schnelle objektorientierte, kompilierende Programmiersprache.
Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Eigene Programmbibliothek zur Laufzeit laden

Beitrag von Glocke » So Okt 28, 2012 11:04 pm

Hiho, ich möchte ein paar Klassen zu einer Programmbibliothek (.so bzw .dll) zusammenfassen und sie zur Laufzeit laden. Nehmen wir mal folgendes Beispiel:

Demo.hpp

Code: Alles auswählen

#ifndef DEMO_HPP
#define DEMO_HPP

class Demo {

    public:
        Demo();
        ~Demo();
        void foo();
        int bar(double value);

};

#endif
Demo.cpp

Code: Alles auswählen

#include <iostream>

#include "Demo.hpp"

Demo::Demo() {
    std::cout << "Demo instance created\n";
}

Demo::~Demo() {
    std::cout << "Demo instance destroyed\n";
}

void Demo::foo() {
    std::cout << "Demo did foo\n";
}

int Demo::bar(double value) {
    std::cout << "Demo did bar\n";
    return (int)value;
}
Das ganze habe ich via

Code: Alles auswählen

g++ -fPIC -c Demo.cpp
g++ -shared -o libDemo.so Demo.o
zur libDemo.so kompiliert. Nun weiß ich nicht so recht, wie ich meine Klasse 'Demo' da zur Laufzeit "rausbekomme". Statisch binden möchte ich die nicht - im Verzeichnis der fertigen Anwendung soll sich dann die Bibliothek als eigene Datei befinden. Nach etwas googeln hab ich dlopen und dlsym gefunden. Allerdings fehlt mir da noch die zündende Idee, wie man am elegantesten eine Klasse lädt. Für Funktionen habe ich ein paar Bruchstücke gefunden.

Und dann gleich die nächste Frage: Wie würde das aussehen, wenn ich die Anwendung für Windows kompiliere und ausführen will?

LG Glocke

/EDIT: Ich hab noch was gefunden aber das scheint auch nicht so richtig die Lösung zu sein:

test.cpp

Code: Alles auswählen

#include <dlfcn.h>

#include "Demo.hpp"

int main() {
    void* handle = dlopen("libDemo.so", RTLD_LAZY);
    
    Demo* (*create)(); // constructor
    void (*destroy)(Demo*); // destructor
    
    create = (Demo* (*)())dlsym(handle, "create_demo");
    destroy = (void (*)(Demo*))dlsym(handle, "destroy_demo");
    
    Demo* myDemo = (Demo*)create();
    //myDemo->foo();
    //myDemo->bar(15.3);
    destroy(myDemo);
}
kompiliert mit g++ -o test test.cpp -ldl.

Erstmal meldet es meinen Lieblingsfehler: Speicherzugriffsfehler (Speicherabzug geschrieben), entferne ich die Kommentare (vor den beiden Funktionsaufrufen), kompiliert er nicht einmal :D
/tmp/cc0XrMTf.o: In function `main':
test.cpp:(.text+0x63): undefined reference to `Demo::foo()'
test.cpp:(.text+0x79): undefined reference to `Demo::bar(double)'
collect2: ld gab 1 als Ende-Status zurück
Und ich hab keinen richtigen Ansatzpunkt dafür :oops: In C# könnte man ja Reflection nutzen ... :?

Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Re: Eigene Programmbibliothek zur Laufzeit laden

Beitrag von Glocke » So Okt 28, 2012 11:53 pm

Ich hab es jetzt erstmal statisch probiert, d.h. ich linke meine lib beim kompilieren. Die lib heißt libDemo.so. Laut div. Suchergebnisse muss ich sie somit via -lDemo linken.

statisch.cpp

Code: Alles auswählen

#include "Demo.hpp"

int main() {
    Demo* d = new Demo();
    d->foo();
    d->bar(14.3);
    delete d;
}
Dann habe ich es mit g++ -o statisch statisch.cpp -L/home/christian/Dropbox/Projekte/libtest -lDemo kompiliert (wegen Pfadproblemen habe ich den absoluten Pfad mal angegeben).

Nur blöderweise geht das auch nicht so richtig:
christian@glocke:~/Dropbox/Projekte/libtest$ ./statisch
./statisch: error while loading shared libraries: libDemo.so: cannot open shared object file: No such file or directory
Hat jemand eine Idee was ich falsch mache?

LG Glocke

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: Eigene Programmbibliothek zur Laufzeit laden

Beitrag von Kerli » Mo Okt 29, 2012 12:56 am

Statische Bibliotheken haben normalerweise die Endung .a.

Eine Klasse wirst du übrigens nicht direkt dynamisch laden können, da es in C++ nicht möglich ist zur Laufzeit neue Typen zu generieren. Du kannst allerdings eine Funktion aus einer dynamischen Bibliothek laden, die dir eine Instanz einer Klasse zurückliefert, wobei dies allerdings immer entweder ein bestimmtes Interface haben muss (= von einer bekannten Basisklasse ableitet) oder etwas unschön die Funktion einen Zeiger auf eine Instanz einer unbekannten Klasse zurückgibt und man mit weiteren Funktionen abfragen welche Methoden die Klasse anbietet (Liste von Funktionszeigern). Letzteres ist mir allerdings noch nicht untergekommen und wird vermutlich auch leicht fehleranfällig werden.
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Re: Eigene Programmbibliothek zur Laufzeit laden

Beitrag von Glocke » Mo Okt 29, 2012 8:42 am

Kerli hat geschrieben:Statische Bibliotheken haben normalerweise die Endung .a.
Nachdem ich es auf .a geändert habe, geht es O_o
Kerli hat geschrieben:Eine Klasse wirst du übrigens nicht direkt dynamisch laden können, da es in C++ nicht möglich ist zur Laufzeit neue Typen zu generieren. Du kannst allerdings eine Funktion aus einer dynamischen Bibliothek laden, die dir eine Instanz einer Klasse zurückliefert, wobei dies allerdings immer entweder ein bestimmtes Interface haben muss (= von einer bekannten Basisklasse ableitet
Ich habe jetzt mal folgendes probiert:

Demo.hpp

Code: Alles auswählen

#ifndef DEMO_HPP
#define DEMO_HPP

class IDemo {

    public:
        virtual void foo() {}
        virtual int bar(double value) {}

};

class Demo: public IDemo {

    public:
        Demo();
        ~Demo();
        void foo();
        int bar(double value);

};

#endif
Demo.cpp

Code: Alles auswählen

#include <iostream>

#include "Demo.hpp"

extern "C" Demo* create_demo() {
    return new Demo;
}

extern "C" void destroy_demo(Demo* obj) {
    delete obj;
}

Demo::Demo() {
    std::cout << "Demo instance created\n";
}

Demo::~Demo() {
    std::cout << "Demo instance destroyed\n";
}

void Demo::foo() {
    std::cout << "Demo did foo\n";
}

int Demo::bar(double value) {
    std::cout << "Demo did bar\n";
    return (int)value;
}
Terminal:
g++ -fPIC -c Demo.cpp
g++ -shared -o libDemo.so Demo.o
test.cpp

Code: Alles auswählen

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

#include "Demo.hpp"

int main() {
    void* handle = dlopen("./libDemo.so", RTLD_LAZY);

    if (handle == NULL) {
        std::cout << dlerror() << "\n";
        return 1;
    }

    Demo* (*create)(); // constructor
    void (*destroy)(Demo*); // destructor

    destroy = (void (*)(Demo*))dlsym(handle, "destroy_demo");
    create = (Demo* (*)())dlsym(handle, "create_demo");

    Demo* myDemo = (Demo*)create();
    myDemo->foo();
    destroy(myDemo);
}
Terminal:
g++ -o test test.cpp -ldl
Ausgabe:

Demo instance created
Demo did foo
Demo instance destroyed
Du meintest das sicher so?

LG Glocke

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: Eigene Programmbibliothek zur Laufzeit laden

Beitrag von Kerli » Mo Okt 29, 2012 10:50 am

Du könntest auch noch die Deklaration der Klasse Demo in die Bibliothek auslagern. Es reicht wenn das Hauptprogramm das Interface kennt. Die Implementierung muss ihm nicht bekannt sein, da diese ja aus der Bibliothek geladen werden soll.
Ansonsten wäre das ja eigentlich schon ein schönes Beispiel fürs Wiki ;)
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Eigene Programmbibliothek zur Laufzeit laden

Beitrag von Xin » Di Jan 08, 2013 11:14 am

Ich habe das jetzt mit Windows und Linux nachvollzogen. Ein Artikel wäre also durchaus drin. Müsste nur noch rausfinden, wie das unter MacOS geht und die Sache wäre rund.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Re: Eigene Programmbibliothek zur Laufzeit laden

Beitrag von Glocke » Di Jan 08, 2013 11:20 am

Xin hat geschrieben:Müsste nur noch rausfinden, wie das unter MacOS geht und die Sache wäre rund
Ich frag mal nen Kumpel, der MacOS verwendet und mit C++ umgehen kann.

/EDIT: ich hab mal noch was drumrumgebastelt, um die dlopen-Aufrufe usw. bissel zu überdecken: http://pastebin.com/8A56vRbZ

Das lästige ist nur, dass ich für jede Methode einen Wrapper brauche (der die eigentliche Methode callt) und jeden Zugriff auf Membervariablen über Getter und Setter realisieren müsste. Statt den Typ T im LibraryWrapper als protected data member zu implementieren, könnte man ja LibraryWrapper<T> direkt von T ableiten (und damit die Member behalten). Allerdings müsste ich dann den Konstruktor (der zu ladenden Klasse T) direkt callen ... da fehlt es mir noch am Feintuning.

Was haltet ihr ansonsten davon?

LG Glocke

Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Re: Eigene Programmbibliothek zur Laufzeit laden

Beitrag von Glocke » Di Jan 08, 2013 12:29 pm

So, einen Fehler beim Laden von Konstruktor & Destruktor habe ich noch ausgemerzt, und das Beispiel noch etwas angepasst:

Interfaces.hpp: http://pastebin.com/JyHxbvrx
Demo.hpp: http://pastebin.com/Cbyi2b7M
Demo.cpp: http://pastebin.com/bJMeqHhm

LibraryWrapper.hpp: http://pastebin.com/R5ZDEhGx
main.cpp: http://pastebin.com/8A56vRbZ

LibraryWrapper wird nun direkt von T (IDemo) abgeleitet, erhält damit dessen Member und überschreibt Konstruktor und Destruktur.. Das war einfacher als Gedacht O_o
/EDIT: naja so richtig war das nicht die Lösung .. an die Membervariablen komm ich nicht ran (hab ja jetzt einmal alle im "this" und einmal in "this->data" jeweils die daten -.- das war quatsch xDD)

Der nächste Punkt auf meiner Liste sind Konstruktor-Parameter. Ich glaube das wird ekelhaft :? Hat jemand eine Idee parat? :D

LG

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: Eigene Programmbibliothek zur Laufzeit laden

Beitrag von fat-lobyte » Di Jan 08, 2013 6:50 pm

Ähem. Bitte nicht Pastebinnen.

Pastebin hat das Problem, dass die Daten wahrscheinlich nur Kurzzeitig zur verfügung stehen.

Wenn du Code veröffentlichen willst, bitte nutze eine der folgenden Möglichkeiten:

1) Poste den Code in [ code][/code] Tags. Nicht meine Lieblingslösung, aber dann ist der Code mal da.
2) Packe den Code in ein Zip-Archiv, und lade ihn hier im Forum hoch. @Xin: Ist es irgendwie gewährleistet dass alle hochgeladenen Dateien auch nach Backups/Restores/Aktualisierungen erhalten bleiben? Bitte nicht .RAR verwenden!!
3) Lade es auf GitHub hoch.
4) Wenn es ein größeres Projekt ist, kannst du Xin nach Hosting dafür fragen, und zwar im "Projekte"-Forum
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Eigene Programmbibliothek zur Laufzeit laden

Beitrag von Xin » Di Jan 08, 2013 8:24 pm

Entscheidene Stellen in Code-Tags, den Rest möglichst gut kompilierbar in ein ZIP Archiv anhängen, aber bitte keine Exe-Dateien oder sonstiges Zeug, dass zum kompilieren nicht erforderlich ist.
fat-lobyte hat geschrieben:2) Packe den Code in ein Zip-Archiv, und lade ihn hier im Forum hoch. @Xin: Ist es irgendwie gewährleistet dass alle hochgeladenen Dateien auch nach Backups/Restores/Aktualisierungen erhalten bleiben?
Theoretisch schon. ^^

Wenn nicht, hab ich's verbockt. Es gab schonmal Probleme, dass Sachen nicht mehr geladen werden konnten, weil die Rechte im entsprechenden Verzeichnis falsch gesetzt waren. Ich hoffe, verloren haben wir noch nix?!
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Antworten