non-pods über DLL-Grenzen herumreichen



  • Hab ich doch gesagt: Damit ich die Funktion beim Namen finde.



  • Und was genau bringt dir das, wenn doch sowieso schon alles implementation-defined ist?



  • Versteh ich nicht. Irgendwie muss ich die Funktion ja finden, um sie benutzen zu können?



  • dllimport?



  • Ich glaube du verstehst nicht... Die Funktionen liegen in einer DLL. Nicht alle Funktionen in der DLL müssen vorhanden sein, damit die DLL mit dem Programm zusammenarbeitet. Die Funktion destruct() ist z.B. optional und wird nur aufgerufen, wenn vorhanden.



  • @314159265358979
    D.h. du lädst die DLL dynamisch bzw. holst dir die Funktions-Adressen dynamisch (GetProcAddress)?
    Wenn ja, dann wäre das ne wichtige Info gewesen.

    extern "C" __declspec(dllexport) world::chunk generate(world::chunk::coords);
    

    ->

    extern "C" __declspec(dllexport) world::chunk* generate(world::chunk::coords);
    

    BTW: du solltest aus extern "C" Funktionen auch keine Exceptions werfen. Einige Compiler (wie MSVC mit Default-Einstellungen) gehen nämlich davon aus, dass extern "C" Funktionen keine Exceptions werfen, und erzeugen dann u.U. den für's Stack-Unwinding nötigen Code nicht.
    Und wenn die Funktion dann doch ne Exception wirft, dann kann alles mögliche schief gehen.

    All das kann man aber halbwegs gut umgehen, indem man sich aus der DLL nur ein Interface holt, und alles weitere dann über dieses Interface macht:

    class dll_interface
    {
    public:
        virtual world::chunk generate(world::chunk::coords) = 0;
        virtual bool is_destruct_required() const = 0;
        virtual void destruct(was_auch_immer) = 0; // NOP wenn is_destruct_required() == true
    };
    
    extern "C" __declspec(dllexport) dll_interface* get_dll_interface();
    


  • hustbaer schrieb:

    @314159265358979D.h. du lädst die DLL dynamisch bzw. holst dir die Funktions-Adressen dynamisch (GetProcAddress)?
    Wenn ja, dann wäre das ne wichtige Info gewesen.

    Ja, ich wusste bis vor deinem Post nicht mal, dass man Code aus DLLs auch anders laden kann... Daher bin ich davon ausgegangen, dass das selbstverständlich ist.

    hustbaer schrieb:

    extern "C" __declspec(dllexport) world::chunk generate(world::chunk::coords);
    

    ->

    extern "C" __declspec(dllexport) world::chunk* generate(world::chunk::coords);
    

    Der Chunk selbst ist bereits eine Art Smartpointer, ich will den einfach beim Aufruf der Funktion moven.

    Exceptions werfe ich keine.



  • All das kann man aber halbwegs gut umgehen, indem man sich aus der DLL nur ein Interface holt, und alles weitere dann über dieses Interface macht:

    Kann das nicht Probleme machen wenn die DLLs mit unterschiedlichen Compilern kompiliert wurden? Oder ist das VTable-Layout ähnlich genug um da von soetwas wie binärer Kompatibilität zwischen den Mainstream-Compilern wie MSVC und MinGW zu sprechen?

    Bei einem Plugin-System (Soll es das sein?) halte ich es für wichtig die Beiden zu unterstützen. MSVC nutzen viele, MinGW kann deutlich mehr C++11. Da ist es blöd LKeute auszusperren.



  • @314159265358979
    Das Zurückgeben eines Zeigers war als "Lösung" für die Warning von MSVC gedacht, nicht weil ich vorschlagen wollte dass du Zeiger verwendest.

    Du kannst es natürlich auch über Output-Parameter machen:

    extern "C" __declspec(dllexport) bool generate(world::chunk::coords, world::chunk* out_chunk);
    

    Das ist im Prinzip eh genau das was Compiler intern aus Funktionen mit Returntypen machen die keine eingebauten Typen sind (Zeiger, ints etc.).
    D.h. du kannst da auch "reinmoven" oder was auch immer du willst.

    @Ethon
    Das VTable-Layout ist auf Windows eher kein Problem, da jeder Compiler COM unterstützen möchte, und daher ein COM-kompatibles VTable Layout verwendet.
    Problematisch wird es bei anderen Dingen, z.B. Exceptions, RTTI oder gar der Standard-Library (std::string zwischen DLLs verwenden etc.).

    Ethon schrieb:

    Bei einem Plugin-System (Soll es das sein?) halte ich es für wichtig die Beiden zu unterstützen. MSVC nutzen viele, MinGW kann deutlich mehr C++11. Da ist es blöd LKeute auszusperren.

    Ich würde auf Windows einfach sagen MSVC Version so-und-so, und aus. Wer Plugins entwickeln will, muss sich halt das passende Studio installieren (Express Version ist eh gratis).
    Das machen viele Programme so, und es ist auch für viele Programme ausreichend.

    Wenn man wirklich mal was entwickeln sollte, wo es absolut wichtig ist dass jeder Plugins dafür entwickeln kann, dann sollte man auf C++ sowieso ganz verzichten. Dann kommen IMO nur klassische Win32/C DLLs in Frage, oder automation kompatible COM Interfaces.



  • Warum kann ich nicht einfach den Chunk ganz normal per Value zurückgeben und im Programm dann moven?



  • Weil "world::chunk" kein C-kompatibler Typ ist.

    Funktionieren wird es vermutlich eh, nur ... extern "C" sagt halt, dass du hier ne C-kompatible Funktion machst.
    Und etwas was mit structs arbeitet, die keine PODs sind, kann wohl kaum C-kompatibel sein.

    Zeiger auf solche Dinge sind dagegen kein Problem, da es den Zeiger selbst ja nicht wirklich interessiert auf was er zeigt.

    Du könntest auch den Linker-Switch /EXPORT verwenden, um die Funktion mit einem "unmangled" Namen zu exportieren. Natürlich musst du dazu erstmal den "mangled" Name kennen. In der DLL hast du dann trotzdem den "schönen" Namen, und du musst auch nicht mehr befürchten dass sich das Name-Mangling irgendwann ändert - wenn das so wäre, würdest du beim Kompilieren der DLL dann nen Linker-Fehler bekommen, und könntest an der Stelle den "mangled" Name anpassen.



  • Die MSDN sagt ja selbst, dass es okay ist, also kann ich doch einfach die Warnung deaktivieren?



  • Ja, kannst du 🙂



  • Vielen Dank. 🙂


Anmelden zum Antworten