Klassen als eigene DLLs?

Verschiedenes zu Qt
Antworten
ViktorZacek
Beiträge: 8
Registriert: 3. Februar 2010 15:14

Klassen als eigene DLLs?

Beitrag von ViktorZacek »

Seid gegrüßt,

Ich programmiere insgesamt schon recht lange, mit Qt erst ganz kurz... also "Achtung Anfängerfrage" ;-)
Als Entwicklungsumgebung verwende ich den Qt Creator 1.3.1, basierend auf Qt 4.6.1 unter Windows.

Sicher kennen einige von euch userfriendly.org und die Comic-Strips von dort... da ich von "Hallo Welt" als Anfangsbeispiel nicht mehr viel halte, ist eines meiner ersten Programme in diversen Programmiersprachen ein Tool, dass ein Anfangs- und ein Enddatum akzeptiert, und die Comicstrips aus diesem Zeitraum runterlädt.
Dadurch komme ich immer in den Genuß gleich mehr vom Programm zu lernen... hier wären das z.B. das Handling von QDateEdit, Buttons und Slots, Netzwerk-Handling, Stringersetzung, und und und.
Jetzt hänge ich gerade daran, dass ich einige Komponenten in eigene Klassen packe, und diese in eigenen DLLs handhaben will.

In der Hilfe gibt es ja Einträge (namentlich "Creating Shared Libraries" und der Eintrag zu "QLibrary"), aber das funktioniert alles nicht so, wie ich das gerne hätte.

Ist das Auslagern in eigene DLLs das übliche Vorgehen in Qt? Oder werden da where QtPlugins verwendet?
Gibt es irgendwo ein funktionierendes Beispiel für eine Implementierung?


Liebe Grüße,
Viktor
Zuletzt geändert von ViktorZacek am 12. Februar 2010 13:07, insgesamt 2-mal geändert.
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

http://qt.nokia.com/doc/4.6/qmake-commo ... -a-library
Bringt dich das weiter?

Prinzipiell kannst du natürlich eine library bauen - wenn es absehbar ist, dass du die Klassen in anderen Projekten wieder verwenden willst.
Ansonsten kannst du doch alle source-files in ein ausführbares Kompilat hauen. Ist auch kein Problem. Das ist was was automatisch mit einem "qmake -project" passiert.

Plugins sind nur dann wirklich nützlich, wenn du dein Programm erweitern willst, ohne alles nochmal neu bauen, packen und ausliefern willst. Erweiterung eben ;) In deinem Fall wäre das eine Idee, wenn du neben dem userfriendly.org noch andere Fetcher Programmieren willst.
ViktorZacek
Beiträge: 8
Registriert: 3. Februar 2010 15:14

Beitrag von ViktorZacek »

franzf hat geschrieben:http://qt.nokia.com/doc/4.6/qmake-commo ... -a-library
Bringt dich das weiter?
Das ist quasi das, was auch in der Hilfe drinsteht.
Hab jetzt in nem Buch von Galileo Press aber das gefunden, was ich suche... nämlich dass ich zusätzlich zur eigentlichen Funktion in der Klasse noch die Funktion nochmal "ganz klassisch" exportieren muß. Das war auch schon alles, was mir gefehlt hat... hatte gehofft, dass mir da Qt mehr abnimmt ;-)
franzf hat geschrieben:Prinzipiell kannst du natürlich eine library bauen - wenn es absehbar ist, dass du die Klassen in anderen Projekten wieder verwenden willst.
Ansonsten kannst du doch alle source-files in ein ausführbares Kompilat hauen. Ist auch kein Problem. Das ist was was automatisch mit einem "qmake -project" passiert.
Ne, mir ist das lieber, wenn es gerade nicht monolithisch ist, sondern die passenden Komponenten wirklich dynamisch dazugeladen werden.
franzf hat geschrieben:Plugins sind nur dann wirklich nützlich, wenn du dein Programm erweitern willst, ohne alles nochmal neu bauen, packen und ausliefern willst. Erweiterung eben ;) In deinem Fall wäre das eine Idee, wenn du neben dem userfriendly.org noch andere Fetcher Programmieren willst.
Gut erkannt ;-)
Aber ist das nicht das gleiche, wie wenn ich eine DLL wirklich dynamisch lade?

Liebe Grüße,
Viktor
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

ViktorZacek hat geschrieben:Gut erkannt ;-)
Aber ist das nicht das gleiche, wie wenn ich eine DLL wirklich dynamisch lade?
Jein :)
Wenn du QtPlugin verwendest, stehen da noch zusätzliche Sachen drinnen, z.B. eine Plugin-Version, mit welcher Qt-Version das Plugin gebaut wurde, usw.

http://qt.nokia.com/doc/4.6/plugins-howto.html
http://qt.nokia.com/doc/4.6/deployment-plugins.html
usw.

Schau dir auch ruhig mal die ganzen examples und demos an. Da gibts auch eines für Plugins, glaub "plug and paint" hieß das.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Aber ist das nicht das gleiche, wie wenn ich eine DLL wirklich dynamisch lade?
Du solltest schon ein bisschen feiner differenzieren.
dll iss eher ne technik, nen Plugin iss mehr nen Konzept :-)

dlls wendest primaer an wenn:
- wenn die Dinger unabhangig von der Exe austauschbar sein sollen
- wenn dir mehrmaliges laden des codesegments bei mehreren prozessen sparen willst (eher geringer vorteil)
- wenn funktionalitaet soweiso schon in dll form vorliegt (systemdlls, dll bibliotheken)
- wenn du ne eigene uebersetzungseinheit fuer funktionalitaet brauchst (z.b. incompatible compilerflags, oder gar unterschiedliche buildsysteme, beispiel delphi code in ne c++ anwendung einbetten)

Da du bei loadlibrary ggf. schon auf das vorhandensein einer dll reagieren kannst, und da funktionalitaet ein und ausschaltbar machen kannst, kann ne reine dll schon nen Teil-Plugin sein ...

von vollwertigen plugins redet man, wenn es eine eigene pluginverwaltung gibt, und durch mehrere plugins die selbe (fuer die Application) funktionalitaet zur verfuegung steht, und der user durch direkte oder indirekte auswahl bestimmt, welches plugin verwendet wird.
plugins auf c++ ebene sind technisch bedingt meist mittels dlls realisiert.
müssen aber nicht:
du kannst auch

- ueber externe prozesse plugins realisieren
also deine exe ruft andere exes auf, die die gleichen schnittstellen (commandozeilenparameter, IPC mechanismen) haben. Also hier wieder mehrere unterschiedliche implementierungen zugleich moeglich, und user kann auswaehlen welche (direkt oder indirekt).

- ueber scriptengines
deine App bindet eine (oder mehrere) scriptengine(s) ein, und ueber die schnittstellen werden damit unterschiedliche scipte (textform oder vorkompiliert) aufgerufen, welche das gleiche (fuer die App) tun.
Also hier wieder mehrere unterschiedliche implementierungen zugleich moeglich, und user kann auswaehlen welche (direkt oder indirekt).

Du siehst, plugin und dll sind eigentlich 2 unterschiedliche sichtweissen ...

Ciao ...
ViktorZacek
Beiträge: 8
Registriert: 3. Februar 2010 15:14

Beitrag von ViktorZacek »

Ich hätte da mal jetzt eine konkrete Frage zum gleichen Thema ;-)
Ich hab eine Klasse namens "ZaViOracle", in der ich diversen Code zu Oracle sammeln will. Das will ich dann als Grundlage für verschiedene andere Projekte verwenden.
Das zweite Projekt ("OracleChecker") bietet die GUI, und lädt die dll von ZaViOracle dynamisch, holt sich daraus die Klasse und arbeitet dann darauf weiter.
( An der Stelle die kurze Bemerkung: ZaViOracle wird zwangsweise als Grundlage vorausgesetzt, sonst startet das Programm gar nicht erst... aber ich hab noch andere DLLs im Sinn, die dann optional sind... daher schlag ich mich damit lieber gleich am Anfang rum, als später in Probleme zu laufen )

Sobald ich dann aus die Klasse versuche zuzugreifen, wirft mir der Compiler Fehler um die Ohren:
W:\temp\qt\OracleChecker/mainwindow.cpp:37: undefined reference to `_imp___ZN10ZaViOracle12getLastErrorEv'
W:\temp\qt\OracleChecker/mainwindow.cpp:37: undefined reference to `_imp___ZN10ZaViOracle12getLastErrorEv'
:-1: error: collect2: ld returned 1 exit status
Ich verstehe die Meldung, verstehe aber nicht, WARUM ich sie bekomme.
Wenn ich mir meine DLL im DepencyWalker anschaue, ist alles schick, da wird getLastError richtig angezeigt (nämlich genau so, wie es hier oben steht).

Damit man auch helfen kann, da gibts die beiden Projekte zum Download... bisher sind die natürlich sehr leer *g*

http://download.bugschleuder.de/qt/OracleChecker.zip
http://download.bugschleuder.de/qt/ZaViOracle.zip


Jeder Hinweis ist gern gesehen... und sei es nur eine verbale Ohrfeige mit dem Hinweis auf eine Stelle im Handbuch ;-)


P.S. Trotzdem ich Hinweise darauf gefunden habe, dass es so funktionieren sollte... eigentlich ist ja der "Trick" dabei, dass ein C-Interface exportiert wird. C kennt aber nu eigentlich ja gar keine Klassen... wie funktioniert das dann?
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

undefined reference to `_imp___ZN10ZaViOracle12getLastErrorEv'
Übersetzt: Klasse ZaViOracle Methode getLastError
kennt er ueber die H Datei, kann aber keine Implementation (symbol fuer die methode) finden ...

da gibts ne menge ursachen ....

in deiner Version :

extern "C" bedeutet, du klopfst C++ namen platt. Namen im sinne von linker symbolen die generiert werden.
statt die c++ typischen Symbole ala "_imp___ZN10ZaViOracle12getLastErrorEv" wird irgendwas mehr C-Likes genereirt und exportiert

du verwendest ne Klasse in der exportierten funktion ! das heisst deine schnittstelle wird compilerabhaengig. Das heisst, das namemergling Dir eh egal iss, und nen c-Compiler wird den code nie uebersetzen koennen.

Lass das extern c mal weg .. und poste dann noch mal die fehlermeldung.

Ciao ...
ViktorZacek
Beiträge: 8
Registriert: 3. Februar 2010 15:14

Beitrag von ViktorZacek »

Sooo... Ich hab mir das jetzt angeschaut...

Also das extern "C" rauszunehmen ändert in Bezug auf den Fehler gar nichts.
Es kommt weiterhin die gleiche Fehlermeldung.


Vielleicht ist es ne blöde Frage... aber warum wirft der Linker hier überhaupt einen Fehler? Die Library soll doch komplett dynamisch nachgeladen werden, die Header-Datei hatte ich mir nur für die Klassendefinition gedacht.
Vielleicht ist das ja eher mein Denkfehler als jetzt ein Fehler im Code?


P.S. Ich mach mir hier nochmal nen generischen Code als Testfall fertig, und poste den dann hier nochmal... damit sozusagen keine Verwirrung links und rechts ist (z.B. durch irgendwelche SQL-Sachen oder sowas), und man sich wirklich nur auf das Problem konzentrieren kann.
ViktorZacek
Beiträge: 8
Registriert: 3. Februar 2010 15:14

Beitrag von ViktorZacek »

Sooo.... hab jetzt ein ganz einfaches Beispiel gestrickt.

Es gibt eine Class "DynamicLibrary", die in einer DLL gekapselt ist.
Weiterhin gibt es noch "DynamicLoader"... samt GUI, so dass man bequem nen Empfänger eintippen kann, und dann auf nen Button drückt.

Idee ist, dass man entweder einen QString übergibt (eine Person, die gegrüßt werden soll) oder ohne Parameter ("Hello World").

Fehler ist "exakt der gleiche", nur dass jetzt die Funktion anders heißt.
W:/temp/qt/DynamicLoader/mainwindow.cpp:65: undefined reference to `_imp___ZN14DynamicLibrary13greetAudienceEv'
:-1: error: collect2: ld returned 1 exit status
Das ganz gibt es unter:
http://download.bugschleuder.de/qt/2010 ... oading.zip
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Prinzipiell :

deine Schnittstelle enthaelt eine klasse, in deinem fall:

DynamicLibrary , deklariert in DynamicLibrary.h

Deiner Applicazion machst die Klasse bekannt durch includen von DynamicLibrary.h

jetzt rufst du an der Klasse irgendwo in der App eine memberfunktion auf.
in deinem fall dyn->greetAudience();

deine App weis das dyn eine klasse vom Typ ist DynamicLibrary iss. weiterhin weis es, das es eine methode greetAudience hat.

was du ihm aber (noch) nicht gesagt hasst, iss dass er greetAudience auch in der dll zu suchen hat ...
Deine Applikation implementiert nirgends DynamicLibrary::greetAudience
In deiner Exportliste deiner DLL steht dfinitiv nur drinn dass die GetClass und GetClassNoParameter zur verfuegung stellt.

Selbst wenn Sie DynamicLibrary::greetAudience zur verfuegung stellen wuerde, wuerde es dir nix helfen.
DU verwendest DynamicLibrary::greetAudience statisch in deinem code. Es muss also beim linken das Symbol DynamicLibrary::greetAudience vorhanden sein.
Aber die DLL wird spaeter dynamisch hinzugeladen.

Das funktioniert also so ned ....

entweder schreibst du dir klassen, die DynamicLibrary::greetAudience an deiner App implementieren und intern zu der dll durchschiessen
machen ueberigens die importlibs, die unter windows gelaufig sind.

Oder du verwendest die methoden ned direkt sondern gleich seperat in ner flachen C-Struktur Ala QString Greetaudience(DynamicLibrary * dyn) in deiner dll und dementsprechend auch exportiert wird.

oder du verpasst DynamicLibrary ne implementation und verlaesst dich drauf das die dll und die app zueinander binaerkompatible versionen der implementierung verwenden ... hasst aber doppelten code. (so wie es in deinem Fall bei QString funktioniert)

weiss ned ob unter linux folgendes funtioniert :
mach nen Interface fuer DynamicLibrary ala IDynamicLibrary.

Code: Alles auswählen

class IDynamicLibrary
{
public: 
    virtual  ~IDynamicLibrary() {} 
    virtual  QString greetAudience() = 0;
}; 
Uebergib ueber die DLL nur das Interface aber lass deine Dll die klasse irgendwie implementieren.

Hintergrund:
Bei abstrakten methoden wird fuer die Klasse eine vtable generiert. die vtable wird als member der instanz irgendwo relativ zu deinem memberpointer abgelegt. Bei binaerkompatiblen compilern iss die stelle immer gleich. bei VS auch bei debug und release versionen.
Aus vtable wird die anzuspringende Adresse beim aufruf abgefragt, und beim kreiren der instanz mit der ausgepraegten klasse (deshalb kann meine keine abstrakten klassen instanziieren) wurde die ja befuellt, was irgend ein funktionspointer in der dll iss (die hasst ja in der dill erzeugt), der aber ueber den ganzen adressraum gueltig iss, also auch in der App.
Deine app will dann nur noch den prototyp der methode haben (kriegt sie durch das interface) die implementation (den funktionspointer drauf) kriegt sie aber durch die vtable, also mag kein symbol dafuer haben.
Verstanden ?



Funktioniert unter windows super, nutzen wir ziemlich ausgiebig (achtung, man wird kompilerabhaengig, aber das iss man eh schon viel extremer, wenn man QString exportiert) ...

Ciao
ViktorZacek
Beiträge: 8
Registriert: 3. Februar 2010 15:14

Beitrag von ViktorZacek »

Okee... *grübel* *ratter*
Danke erstmal für den umfangreichen Hinweis!

Ich glaub, dann ist das doch nicht so richtig der Weg, wie ich ihn gehen will... bzw. er wird umständlicher, als ich dachte.

Da werd ich schauen, wie ich das (für mich) am geschicktesten lösbar ist.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Ich glaub, dann ist das doch nicht so richtig der Weg, wie ich ihn gehen will...
Doch doch ^^
Ehrlich gesagt das muss weh tun :-) mit Dlls hasst soviel fallstricke, es ist besser das die internas so eingetrichtert bekommst, weil du bekommst explicit den hinweis, unter welchen bedingungen es funktioniert. Schlimmer isses wenn die bedingungen ned weisst ...
Tolle effekte hasst z.b. wenn denn die App die QT version x.z verwendet, du aber mit der dll noch x.y anziehst ^^ dein compiler / linker hat Null chance das zu erkennen, wenn das statische interface der klassen gleich iss (davon kannst bei QT ausgehen). Wenn sich aber binaer nur bissi was geaendert hat, krachts an den unerwarteten stellen. Und wenn noch multithreading drinne hasst, kommst du nie drauf ^^

Ne dll hundertprozentig dynamisch anziehen, iss nen halbes plugin. und bei plugins sollt es einen ins blut uebergehen, das man niemals implementierte Klassen ueber die schnittstelle schickt. Interfaces und C-Schnittstellen sind um welten robuster.

Ciao ...
ViktorZacek
Beiträge: 8
Registriert: 3. Februar 2010 15:14

Beitrag von ViktorZacek »

Na ja, genau deswegen meine ich ja, dass es nicht das ist, was ich will :lol:

Ist mir auch ein wenig rätselhaft, warum das so umständlich ist... Objektorientierung und Klassen gibts ja nicht erst seit gestern ;-)


Aber wie gesagt, wenn ich mir mal die Zeit nehme, schaue ich mir das an, fürs erste ist Qt (und damit C/C++) erstmal für mich gestorben, weil ich keine Lust habe, jetzt mich wochenlang mit irgendwelchen Internas - die der Steinzeit geschuldet sind - rumzuschlagen ;-)


P.S. Ich hab nix gegen Qt, das find ich super, mich stören da nur die "Fesseln der Vergangenheit". Da ich mir das nur just 4 fun beibringe, kann ich momentan mit meiner Zeit besseres anfangen... StarTrek Online z.B. ;-)
Antworten