C++ Dll allgemein nutzen



  • Hallo zusammen
    Ich habe mir mittlerweile schön öfters die Frage gestellt, wozu man eine DLL in C++ erstellen soll, wenn man diese anschliessend nicht allgemein nutzen kann (verschiedene Compiler, oder sogar verschiedene Sprachen).
    Konkret habe ich mir eine DLL erstellt, die mir verschiedenste Funktionen zum Kommunizieren mit einer eigenen Hardware/Firmware bereit stellt. Zum Beispiel:

    • Version von Bootloader/Golden-Applikation/Main-Applikation auslesen
    • FW Update von Bootloader/Golden-Applikation/Main-Applikation
    • Standardisierte Komponenten (digitale Ein- und Ausgänge...)

    Die objektorientierte Struktur hatte mir hier das Leben massiv vereinfacht, da eine Klasse die Funktionen schön gruppiert hat und auch oft wiederverwendet wird.
    Wenn ich nun die DLL ausserhalb der gewohnten Umgebung nutzen möchte (z.B. in einem C# Projekt, oder bei einem anderen Compiler) muss ich die schöne Struktur wieder aufbrechen und >500 Funktionen schreiben (was wiederum sehr aufwändig zum pflegen ist und auch fehleranfällig), die mir simple C-Schnittstellen nach aussen bereitstellen??? Da kann man ja von Anfang an gleich auf C++ verzichten und auf gutes altes C setzen. Oder wie macht ihr das?

    Gruss
    P51D



  • Naja, nur weil man ein C Interface bereitstellen muss heisst dass ja noch nicht das man intern auf C++ verzichten muss.
    Typischerweise wird man aber wie Du selbst sagst jeden Aufruf kapseln müssen.
    Ausserdem braucht man noch zusätzliche Funktionen um die Objekte zu erzeugen/zerstören.
    über das C-Interface gehen dann immer nur "opaque handles" meist in Form von void* zu den C++ Objekten.
    Beispiel aus einem meiner Projekte:

    void* __stdcall InitEx(const podball::INITINFO *pInitInfo)
    {
    	// allocate memory for the module.
    	CDummyTeam* pModule = new CDummyTeam;
    
    	pModule->TeamInitEx(*pInitInfo);
    
    	// return the pointer to our module.
    	// we will get this pointer in every call to the Play function for this match and team.
    	return pModule;
    }
    
    void __stdcall Play(podball::WORLDSTRUCT *pWorld, void *pUser)
    {
    	((CDummyTeam*)pUser)->TeamPlay(*pWorld);
    }
    
    void __stdcall Dispose(void *pUser)
    {
    	delete pUser;
    }
    

    Eine andere aber weniger kompatible Variante Objekte über DLLs zu sharen (unter Windows) ist COM (https://en.wikipedia.org/wiki/Component_Object_Model) bzw neuere Ableger davon.
    Und das gibt's schon seit den 1990er Jahren.
    Weitere Alternativen findest Du über den "See also" Abschnitt im obigen Wikipedia Artikel.



  • Mhm, wenn man dann aber verschachtelte Objekte hat, muss wirklich für jede Ebene einen Wrapper erstellt werden?
    Z.B.

    class __declspec(dllexport) CMotor
    {
    	private:
    		UInt32 m__u32Leistung;
    	public:
    		CMotor(UInt32 p__u32Leistung);
    		UInt32 u32Leistung() const;
    };
    
    class __declspec(dllexport) CAuto
    {
    	private:
    		Color m__oFarbe;
    		
    	public:
    		CMotor m__oMotor;
    		CAuto(UInt32 p__u32Leistung, Color p__oColor);
    		Color oFarbe();
    };
    

    Muss für CAuto und für CMotor einen eigenen Wrapper erstellt werden, oder kann dies auf der Ebene CAuto geschehen (halt mit allen nötigen Funktionen wie u32Leistung von Motor)?

    Aktuell tendiere ich mehr dazu, dass ich das bisherige UI-Programm von C# auf C++ portiere, damit ich dort die C++ neu erstellte Bibliothek vollumfänglich nutzen kann.
    Am schönsten wäre es zwar schon, mit einer modularen/erweiterbaren Schnittstelle zu arbeiten, damit die Bibliothek auch in C oder C# Projekten verwendet werden kann.



  • Wenn die mit C++ erstellte DLL in einem C# projekt verwendet werden soll, dann braucht es kein C-Interface.
    Man braucht da nur eine Wrapper (Klasse) in C++/CLR (C++ Erweiterung für .Net) erstellen.
    Diese Wrapper klasse kann man dann wie eine C# Klasse in C# code verwendet werden.



  • @firefly sagte in C++ Dll allgemein nutzen:

    Man braucht da nur eine Wrapper (Klasse) in C++/CLR (C++ Erweiterung für .Net) erstellen.

    In dieser müssten dann aber auch alle public-Methoden der kompletten Bibliothek (inkl. alle Vererbungen und public-Objekte) enthalten sein, oder?



  • @p51d Ja. Für jede Klasse die in .Net code verwendet werden soll muss ein C++/CLR Wrapper klasse erstellt werden, wenn man in z.b. C# die klassen genauso nutzen soll wie wenn ein in C++ geschriebenes Programm die DLL verwenden würde.



  • Schade, dass das so kompliziert sein muss. Bedeutet für mich unterm Strich, dass ich C# nicht mehr verwenden werde (haben wir sowieso nur für einfache UI-Programme genutzt). Hätte nicht gedacht, dass C# hier so einen massiven Negativpunkt hat.
    Die Bibliothek wurde absichtlich in C++ geschrieben, damit diese möglichst Plattformunabhängig ist (FPGA, ARM, PC, SPS...). Da wir die SW und FW sowieso in C++ schreiben, hat sich das gerade angeboten.
    Dann werde ich wohl oder übel das UI-Programm nach QT portieren müssen, was aber unterm Strich der kleinere Aufwand ist und zukunftsträchtiger...



  • Das Problem ist dabei aber nicht C#, sondern die nicht standardisierte ABI von C++.
    Das selbe Problem hättest du ja auch, wenn du für Qt einen anderen Compiler nutzen würdest als für deine DLL.



  • @th69 sagte in C++ Dll allgemein nutzen:

    Das Problem ist dabei aber nicht C#, sondern die nicht standardisierte ABI von C++.
    Das selbe Problem hättest du ja auch, wenn du für Qt einen anderen Compiler nutzen würdest als für deine DLL.

    Und zusätzlich ist hier die Problematik C# ist eine managed sprache und C++ nicht. Da muss eh gemarshaled werden, um daten von der einen domäne in die andere zu bekommen.
    Für C-APIs liefert C# bzw. der Compiler direkt support mit um das marshalling zu machen man muss die methoden nur entsprechen in C# deklarieren.
    Da die ABI in C++, wie @Th69 schon schrieb, nicht standardisiert ist und zusätzlich die Symbole gemangled sind, ist sowas nicht so einfach mögliche.
    AFAIK dafür hat Microsoft auch C++/CLR entwickelt um einfacher C++ code in .Net code integrieren zu könnnen.

    Ich könnte mir gut vorstellen dass es auch code generatoren gibt, welche die Wrapper-klassen und managed code aus den C++-Headern erzeugt.
    @P51D Das Problem existiert immer, wenn man Code aus zwei verschiedenen Sprachen kombinieren muss (z.b. C++ und Python) es muss immer ein art wrapper gebaut werden, der von der einen Sprache/Laufzeitsystem ins andere die Daten konvertiert (Bei .Net/C# nennt sich das marshalling)



  • Mhm, ich merke schon wieder, dass ich zu wenig programmiere...

    Ich habe mich nun dazu entschieden ein C-Interface zu basteln. So habe ich die grösste Flexibilität.
    Leider stehe ich nun etwas auf dem Schlauch:

    • Alle enums innerhalb der einzelnen Klassen kann ich ja nicht direkt nutzen. Muss ich diese jetzt wieder neu definieren?
    • Ich habe diverse "Interface"-Klassen die ausserhalb der Bibliothek definiert werden müssen (z.B. ein serieller Treiber). Die effektiv genutzte Klasse hat im Kontruktor diese Interface-Klassen als Pointer, so dass ich die effektive Implementierung ausserhalb lösen und intern trotzdem darauf zugreifen kann. Wie ich mit einem Handel der Hauptklasse arbeite ist mir klar, nur wie löse ich nun das mit den Interfaces?

    Gruss


Log in to reply