Klassen innerhalb einer DLL nutzen - aber ohne Export =)



  • Hallo,

    es wird Zeit das Forum mit meinem ersten Problem zu beglücken^^. Also ich möchte folgendes tun: Ich bin gerade dabei eine DLL in C++ zu verwirklichen. Das Exportieren von Funktionen bzw. Methoden klappt soweit... nur habe ich alles mal ein bisschen (der Übersicht und Wartung wegen) in Klassen aufgeteilt. Genau diese sollen intern in den DLL-Funktionen verwendet werden können und dabei aber nicht exportiert werden. Anbei folgenden Pseudoode:

    /* dllmain.cpp */
    
    #include <windows.h>
    #include <test.h>
    
    extern "C" __declspec(dllexport) BOOL set_test();
    
    BOOL set_test() {
    	Test test();
    	test.clear();
    	return TRUE;
    }
    
    /* test.h */
    
    #ifndef TEST_H
    #define TEST_H
    
    #include <windows.h>
    
    class Test
    {
    public:
    	Test( void );
    	virtual ~Test( void );
    	inline void clear( void );
    };	   // class Test
    
    #endif // TEST_H
    
    /* test.cpp */
    
    #include "test.h"
    
    Test::Test( void )
    {
    	return;
    }
    
    Test::~Test( void )
    {
    	return;
    }
    
    void Test::clear( void )
    {
    	return;
    }
    

    Wenn ich das bei MVC++ 2010 so kompiliere, kommt schon beim Übersetzen ein Fehler:

    1>dllmain.obj : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""public: void __thiscall Test::clear(void)" (?clear@Test@@QAEXXZ)" in Funktion "_set_test"..
    

    Gut, denk' ich mir, also verändere ich mal die Klasse Test in »test.h« in Zeile 8 so, dass ich sie nutzen kann:

    class __declspec(dllexport) Test
    

    Funktioniert, nun exportiert er aber eben die gesamte Klasse. Man kommt zwar von außen nicht so leicht ran, liest man aber die Funktionen mit entsprechenden Tools aus, sieht man den ganzen Salat^^.

    Meine Frage: Kann man Klassen innerhalb einer DLL nutzen, ohne sie exportieren zu müssen? Habe schon Stunden mit meinem Freund Google verbracht und nichts passendes gefunden... ich lande immer wieder bei Themen, wie man Klassen exportieren kann, aber das will ich ja nicht^^.

    ~ Gruß Tortigar



  • Tortigar schrieb:

    inline void clear( void );

    Nimm mal das inline da weg (oder definier die Funktion auch inline).



  • seldon schrieb:

    Nimm mal das inline da weg (oder definier die Funktion auch inline).

    Mmmh... wie denn... das war das Problem? Jetzt bin ich erstaunt^^. Also ist es einem nicht gestattet irgendwelche Funktionen in einer DLL als inline zu markieren, oder? Ein bisschen Schade, aber ich hoffe die Optimierung vom MVC++ macht das wieder wett.

    Aber vielen Dank für deine Hilfe =).


  • Mod

    Ich glaube dir ist gar nicht klar, was inline überhaupt macht. Mit Optimierung hat das heutzutage nur noch wenig zu tun. In erster Linie geht es darum, das man mit inline die ODR verletzen kann, was es einem erlaubt, Funktionen in Headern zu definieren.

    Und Funktionen die wirklich inline sind (im Optimierungssinn) und dynamische Bibliotheken schließen sich ohnehin gegenseitig aus. Wie soll das funktionieren?



  • Die Klasse soll in die DLL, dabei wollte ich aber auch (wie sooft) alles richtig machen und ich las mehrmals, dass man eine Funktion (sofern es sich lohnt) auch inline machen sollte. Man kann ja nicht ahnen, dass dies bei DLLs wieder nicht unbedingt zum Erfolg führt.

    Aber mal was anderes... da mir inline wirklich nur (sagen wir mal) zu 65% klar scheint: Ist das obige Beispiel in Bezug auf Inline-Funktionen wohl falsch. Wäre es so richtig?

    /* test.h */
    
    #ifndef TEST_H
    #define TEST_H
    
    class Test
    {
    public:
        Test( void );
        virtual ~Test( void );
        inline void clear( void )
        {
            // Leere etwas
            return;
        }
    };       // class Test
    
    #endif // TEST_H
    
    /* test.cpp */
    
    #include "test.h"
    
    Test::Test( void )
    {
        // Tue etwas
        return;
    }
    
    Test::~Test( void )
    {
        // Zerstöre etwas
        return;
    }
    


  • war es denn das Problem ?
    Kann ich mir grad ned vorstellen, das es das inline allein ist ^^
    Hab aber auch grad nicht die musse nen Project zu erstellen und es selber zu testen 🙂

    inline sollte den Compiler nur den Hinweis geben, das er die methode inlinen koennte. Das ist keine Pflicht fuer den Compiler.
    Manche schauen bei inline 2 mal hin, manche machen auch gar nix ....
    unabhaengig davon inlinet der compiler auch selber ... wenn er denkt das es richtig ist 🙂

    bei erfolgreichem inline bekommst du kein symbol fuer die funktion.
    Dein Fehler sagt aber nur aus, dass du das Symbol fuer deine funktion nicht findest.
    Also muss der compiler mindestens einmal druebergegangen sein und hat das inline ignoriert.

    test.h wird an 2 stellen includiert, also in 2 definitions dateien.
    dllmain.cpp
    test.cpp

    da test.cpp bei ignorierten inline dafuer zustaendig wäre, das test::clear symbol zu erzeugen, weisst deine fehlermeldung drauf hin, das es von test.cpp aus nicht ignoriert wurde.
    dllmain.cpp wiederum sucht das symbol, ein indiz das es das inline ignorieren will.
    Das ist eher ungewöhnlich, da Du in dllmain.cpp nix machst, was das inlinen vom compiler beeinflussen wuerde ... es sei denn es wuerde versuchen Test mit zu exportieren, oder eine Export direktive ändert dir das verhalten.

    was ungewoehnlich ist, du deklarierst und definierst in der datei deine exportfunktion 🙂

    lager das

    extern "C" __declspec(dllexport) BOOL set_test();
    

    mal in ne eigene headerdatei aus, und includier die in dllmain.cpp

    generell sollte es aber auch mit dem inline gehen ...
    wobei es schon üblicher ist, inline funktionen im header zu implementieren.

    generell noch:
    (void) ist C
    in c++ kannst und solltest das weglassen
    Test(); langt vollkommen.
    nur bei rueckgabe ist das void "zwingend" (sonst nimmt er int ^^ );

    void Test::clear( void )
    {
        return;
    }
    

    das return ist vollkommen sinnfrei ^^



  • Wenn du eine Funktion inline deklarierst, muss sie in allen Übersetzungseinheiten, die sie benutzen, auf die gleiche Weise definiert sein. Das war bei dir nicht der Fall, und daher hast du das Problem. Du kannst die Funktion durchaus inline deklarieren, und das geht dann genau so, wie du im zweiten Anlauf schreibst.

    Die technischen Garantien, die inline dir gibt, sind etwas esoterisch und haben vor allem mit Linkage zu tun. Die ursprüngliche Idee hinter inline war, dem Compiler zu empfehlen, die Funktion nicht wie eine normale Funktion aufzurufen, sondern an der Aufrufsstelle stattdessen einfach den Inhalt der Funktion einzufügen, um den Aufwand eines Funktionsaufrufes zu sparen. inline zwingt den Compiler nicht dazu, das zu tun, weil es nicht in allen Fällen möglich (Stichwort Rekursion) oder sinnvoll (bei sehr langen Funktionen) ist, und mit der Erfindung der Linkzeitoptimierung haben sich die Spielregeln da doch deutlich geändert - viele Compiler brauchen inzwischen nicht mehr den kompletten Quellcode einer Funktion, um sie inlinen zu können - aber da kommt das ganze her.



  • Und Funktionen die wirklich inline sind (im Optimierungssinn) und dynamische Bibliotheken schließen sich ohnehin gegenseitig aus. Wie soll das funktionieren?

    Ob er in ner dll innerhlab inlinet oder ned ist doch wurscht, und geht den von aussen nichts an, weil er von test überhaupt nix wissen muss.
    nur exportierte klassen kann er nicht inlinen, weil fuer die vollstaendig symbole erzeugt werden.
    Und die klasse exportieren will er aber auch nicht ..
    Das inline an sich ist damit nicht das problem ...
    Das problem ist eher, das der compiler mehr macht, als er grad da erwartet 🙂

    Ciao ...


  • Mod

    RHBaum schrieb:

    inline sollte den Compiler nur den Hinweis geben, das er die methode inlinen koennte. Das ist keine Pflicht fuer den Compiler.
    Manche schauen bei inline 2 mal hin, manche machen auch gar nix ....
    unabhaengig davon inlinet der compiler auch selber ... wenn er denkt das es richtig ist 🙂

    Das ist etwas ungenau. Das ist nicht alles was inline macht. Eigentlich ist diese Bedeutung von inline mehr oder weniger obsolet, weil Compiler sowieso alles selber entscheiden, wenn sie die Möglichkeit haben. Und wenn sie die Möglichkeit nicht haben (weil es z.B. in einer anderen Übersetzungseinheit steht), dann wird's sowieso nichts mit dem inlinen (moderne Techniken wie Link-Time-Code-Generation mal außen vor belassen)

    bei erfolgreichem inline bekommst du kein symbol fuer die funktion.
    Dein Fehler sagt aber nur aus, dass du das Symbol fuer deine funktion nicht findest.
    Also muss der compiler mindestens einmal druebergegangen sein und hat das inline ignoriert.

    Das ist ungenau die zweite Bedeutung beschrieben. Noch einmal genauer gesagt: Inline Funktionen dürfen mehrmals definiert werden, wenn diese Definitionen identisch sind. Das Ergebnis verhält sich dann so wie eine einzige Version der Funktion mit externer Linkage. Das heißt, alle diese Funktionen haben die gleiche Adresse und auch statische Variablen funktionieren so als wäre alles eins.
    Außerdem tauchen Funktionen die inline sind nicht mehr als Symbol im Objektcode auf, wenn sie in einer Übersetzungseinheit nicht explizit benutzt wurden.



  • Ich muss das erstmal alles ausgiebig testen - muss auch gleich auf Arbeit. Resultate gibt's also erst morgen oder heute Abend.

    RHBaum schrieb:

    generell noch:
    (void) ist C
    in c++ kannst und solltest das weglassen
    Test(); langt vollkommen.
    nur bei rueckgabe ist das void "zwingend" (sonst nimmt er int ^^ );

    Okay, dann lass ich auch dies mal weg. Ich freue mich immer wieder über Vorschläge, um meinen Stil ein bisschen zu verbessern. C++ ist immer noch etwas neu für mich und ich suche immer wie ein blöder im WWW, wie es am schönsten aussieht :3.

    RHBaum schrieb:

    das return ist vollkommen sinnfrei ^^

    Ich weiß, aber ich wollte die Funktion zur Veranschaulichung mit irgendwas füllen^^.


  • Mod

    RHBaum schrieb:

    Und Funktionen die wirklich inline sind (im Optimierungssinn) und dynamische Bibliotheken schließen sich ohnehin gegenseitig aus. Wie soll das funktionieren?

    Ob er in ner dll innerhlab inlinet oder ned ist doch wurscht, und geht den von aussen nichts an, weil er von test überhaupt nix wissen muss.
    nur exportierte klassen kann er nicht inlinen, weil fuer die vollstaendig symbole erzeugt werden.
    Und die klasse exportieren will er aber auch nicht ..
    Das inline an sich ist damit nicht das problem ...
    Das problem ist eher, das der compiler mehr macht, als er grad da erwartet 🙂

    Ja, da hatte ich nicht den ganzen Thread gelesen. Ich dachte er wollte, dass die Funktionen aus seiner DLL in dem Programm welches diese benutzt inline werden.



  • Denk das problem ist wirklich deine "schreibweisse"

    den header test.h liesst er nur einmal ein, dank include Guards.
    der unterschied liegt also in den cpp dateien.
    in dllmain.cpp geht er von aus, das er nicht inlined, bei test.cpp wiederum schon ... einzig was ich mir erklären kann, ist das er in test.cpp die definition kennt, in dllmain.cpp eben nicht und er deswegen ned inlinen kann.

    aber ich denke eigentlich das ich sowas trotzdem schon funktional gesehen hab, grad beim MSVC ... also inline und definition trotzdem in der cpp datei. (auch wenn es sich ned gehört).

    Vielleicht spielt da die reihenfolge rein, wie er die definitionen übersetzet ???
    wenn test.cpp vor dllmain.cpp käme ... vielleicht.

    Das zum Thema Esotherik 😃

    Ciao ...



  • So... also gut. Das Problem lag wirklich an meiner fragwürdigen Gestaltung der Header- und CPP-Dateien. Ich habe die Inline-Funktionen nun direkt in den Headerdateien definiert und jetzt geht's ohne Probleme. An alle ein herzliches Dankeschön =).


Anmelden zum Antworten