[Qt4] Modulare GUI-Applikation - Aber wie?
-
hi community,
ich bin dabei eine plattformunabhängige Qt-Applikation zu entwickeln, die verschiedene Betriebsmodi bieten soll.
Folgender Aufbau:
- Die Applikation selbst. Sie stellt verschiedene Klassen und globale Objekte zur Verfügung, auf die auch die Plugins zugreifen sollen. Die Plugins werden mittels QPluginLoader geladen.- Die Plugins. Ich habe mich hier an das Echo-Beispiel in den Qt Docs gehalten.
Soweit funktioniert alles prima - solange ich nicht auf Objekte zugreifen will, die die Applikation zur Verfügung stellt. Denn da bekomme ich beim linken immer ein "undefined reference to ..." (MinGW) und einen ähnlichen Fehler unter MSVC.
Unter Linux geht das einwandfrei, da sorgt der g++-switch "-shared" dafür, dass ich die Plugins kompilieren kann ohne die 'undefined reference'-Fehlermeldungen zu erhalten.Nur, wie stelle ich das nun unter Windows (Visual Studio 2008) an?
Ich bin mit MSVC kaum bewandert, weil ich jahrelang nur unter Linux gearbeitet habe, nun aber eine Applikation primär für Windows entwickeln muss/will.Jede Hilfe ist gerne gesehen, denn ich versuch' schon seit Tagen herauszufinden wie ich das machen kann.
Anbei noch ein kleiner Beispielcode, der das Problem verdeutlichen soll. Vielen Dank schonmal./* * my_interface.h */ #ifndef _MY_INTERFACE_H #define _MY_INTERFACE_H class MyInterface { public: MyInterface(); ~MyInterface(); void func(); }; extern MyInterface *globalInterface; #endif // _MY_INTERFACE_H /* * mylib.cpp */ #include "my_interface.h" extern "C" void lib_init() { /* das fuehrt zu Problemen. Ich benoetige dies aber dringendst. */ MyInterface *in = globalInterface; in->func(); } /* * Output (Linux): * ne:~$ g++ -o mylib.o -c mylib.cpp -fPIC * ne:~$ g++ -o mylib.so -shared mylib.o * ne:~$ * * Output (Windows / MinGW / GCC 4.4.0): * Prompt> g++ -o mylib.o -c mylib.cpp -fPIC * mylib.cpp:1: warning: -fPIC ignored for target (all code is position independent) * Prompt> g++ -o mylib.so -shared mylib.o * mylib.o:mylib.cpp:(.text+0x7): undefined reference to `globalInterface' * mylib.o:mylib.cpp:(.text+0x15): undefined reference to `MyInterface::func()' * collect2: ld returned 1 exit status * */
Update:
Mit MSVC hab ich's geschafft. MSVC hat mir - trotz dessen dass als Ausgabetyp .dll angegeben war, keine entsprechende .lib-Datei erzeugt die ich zum linken für die Applikation benötigte. Erst als ich die entsprechenden Objekte mittels Q_DECL_EXPORT exportiert hab, hat er die .lib-Datei erzeugt.
Man lernt ja nie aus.
-
nen plugin bauen, fuer dessen benutzung du ne lib linken musst, beisst sich aber im Konzept irgendwie ^^
wie sehen deine schnittstellen aus ???
sind das wirklich implementationslose konstrukte, also reine abstrakte klassen, oder gar nur C-kompatible funktionen ?ich hoffe das MyInterface ist ned dein interface was exportieren willst ... aber es schaut ganz danach aus ...
ich wuerd das bissi anpassen:
class IMyInterface { public: /// MyInterface(); /// Konstruktor brauchst ned zu implementieren, die erstellt der compiler selber, wenn er einen braucht ... /// ~MyInterface(); ///Dtor bei ner Klasse die meilenweit nach vererbung stinkt und ned virtuell ?? /// besser: virtual ~IMyInterface() {} /// und auch gleich im Header implementieren, damit von keiner externen Impl abhaengig bist .... /// falls dein Interface nie von aussen geloescht wird (was zu empfehlen ist) brauchst du das virtual am DTOR ned ... wirklich /// aber statt einen zu implementieren, iss weglassen dann definitiv die bessere wahl ... /// void func(); du erzwingst eine implementierung hier. Das in nem Interface ??? Besser: virtual void func() = 0; /// damit sagst du quasi, dass die basisklasse nix implementiert und die implementierungen in den instanzen zu suchen sind ... };
und schon brauchst keinerlei cpp datei mehr zu deiner deklaration ...
Das rausgeben einer instanz aus ner dll kannst dann in ner globalen exportfunktion deiner Dll machen ala
dllmain.cpp
int getInterface(MyInterface **ppInterface) /// schreibt den zeiger an die vorgegebene Adresse, rueckgabe fuer fehlermeldungen .... { /// hier irgendwie implementieren *ppInterface = &myGlobalInterfaceImpl; /// zum Beispiel return ERRORCODE_SUCCESS; /// zum Beispiel }
und noch schoen exportieren lassen ueber exportdatei(*.def) oder ueber die MS spezifischen Deklaraionen (DLLEXPORT krams).
Ich hoffe du willst ned die App und deine Plugins mit unterschiedlichen Compilern bauen ...
Ciao ...
-
Ja, das ganze war jetzt nur ein Beispiel. Das ganze Programm kann ich nicht posten, da es schon ziemlich umfangreich geworden ist.
Exportiert wird nur C++, aufextern "C"
hab' ich gänzlich verzichtet.Das Stichwort was ich hätte nennen wollen war "impliziertes Linken".
Ich hab' mich ewig gewundert warum mir Visual C++ keine.lib
-Datei erstellt (bei MinGW solls wohl ähnlich sein?) - unter Linux kann man direkt gegen eine dynamische Bibliothek (*.so
) linken, ohne den Zwischenschritt mit der.lib
.
Mittlerweile funktioniert alles so wie ich es brauche.Dank
Q_DECL_EXPORT
sollte das ganze auch auf anderen Plattformen laufen.
-
klar kannst auch statisch linken ... oder impliziet linken.
richtige Plugins werden das aber nie verwenden, siehe Konzept von Plugins.
Bleibt immer noch die Frage ... was brauchst du da eigentlich ? Sind das wirklich plugins ?
Plugins laed man immer dynamisch, also mit loadlibrary / dlopen .
oder halt mit QPlugin.Ciao ....
-
Richtig, die Plugins werden mithilfe der QPluginLoader-Klasse geladen. Mein Problem war, dass ich in den Plugins auf Objekte zugreifen will, die von der Anwendung selbst bereitgestellt werden. Einfaches referenzieren mithilfe von Headern funktionierte da nicht, wodurch ich "undefined reference to ..." erhielt.
Sämtliche Klassen der Anwendung hab' ich nun in eine DLL geschmissen, dort alles was ich brauche exportiert und das erzeugt mir eine schöne .lib-Datei, die ich zum linken für die Plugins brauche.
Die Anwendung selbst besteht nur noch aus einer main-Funktion, in der ich ein Objekt der "Startklasse" instanziere. Die Anwendung wird auch gegen die .lib-Datei gelinkt und lädt die entsprechende .dll beim Programmstart. Genauso wie ich es wollte.
-
Lass dir aber noch warnend gesagt sein: QPluginLoader lädt pro Lib genau eine Instanz! Wenn du an anderer Stelle ein neues QPluginLoader-Objekt erstellst mit einer lib, die schon wo anders geladen wurde, und deren instance noch immer läuft, erhältst du mit dem neuen instance() genau das gleiche Objekt, das schon existiert!
Möchtest du also mehrere Instanzen pro lib, musst du dir eine eigene Factory-Methode bauen. Entweder, deine Plugins müssen eine passende Methode create() implementieren, die auf das QPluginLoader::instance() angewandt wird, oder du lädst mit dem QPluginLoader nur eine Factory, mit der du dann neue Objekte instanziierst. Oder du machst was eigenes mit QLibrary + extern "C" Factory-Methode.