C++ Framework mit C# GUI-Code verbinden


  • Administrator

    Kommt meiner Meinung nach etwas darauf an, wie gross die Schnittstelle von diesem C++ Framework ist. Wenn sie eher klein ist und die Aufrufe grössere Aufgaben ausführen, heisst es gibt nicht viel Verkehr, dann würde ich eine C Schnittstelle und P/Invoke verwenden. Dazu brauchst du kein unsafe!
    Sobald die Schnittstelle allerdings komplexer wird, sofort zu C++/CLI wechseln, wobei man sich hier aber auch keine Illusionen machen darf. C++/CLI heisst meistens trotzdem eine rechte Menge Arbeit an Wrapperklassen und co. Auch empfinde ich es persönlich oft etwas als Gefrickel. Aber man hat halt mehr Kontrolle, kann objektorientiert mit Destruktoren, virtuellen Methoden, etc. arbeiten und im allgemeinen ist die Performance eher besser.

    Grüssli



  • Die Schnittstelle besteht aus 3 nicht besonders großen Interface-Klassen, die der Client ansprechen muss, zwei verschiedenen Eventklassen, die er verarbeiten muss, sowie einer abstrakten Basisklasse, die im Grunde nur als Callback-Sammlung dient, für die er eine Implementierung bereitstellen muss. Also wirklich überschaubar. Ich hatte mir sowieso überlegt, zusätzlich zur C++-Schnittstelle einen C-Wrapper anzubieten, das dürfte nicht allzu umfangreich werden 😉



  • Dann mach einen C-Wrapper.
    Der grösste Nachteil eines C-Wrappers ist vermutlich, dass du in C# nochmal wrappen musst - bzw. halt mit den "unmanaged" Handles/... der C-Seite hantieren. Und natürlich die doofen "extern" Deklarationen schreiben.
    Wenn es aber nur 3-4 Klassen sind, wird das weniger ins Gewicht fallen.


  • Administrator

    hustbaer schrieb:

    Der grösste Nachteil eines C-Wrappers ist vermutlich, dass du in C# nochmal wrappen musst - bzw. halt mit den "unmanaged" Handles/... der C-Seite hantieren.

    Das wirst du aber auch in C++/CLI nicht anders machen können. Wrappen muss man es immer. Und meistens muss man in den Wrapper-Klassen das Dispose-Pattern verwenden. Ob P/Invoke oder C++/CLI spielt dabei wirklich keine grosse Rolle.

    hustbaer schrieb:

    Und natürlich die doofen "extern" Deklarationen schreiben.

    Und in C++/CLI musst vieles vom Marshaling selber machen, was vielfach in ein kleines Gefrickel endet. Zumindest ist das meine bisherige Erfahrung 🙂

    Grüssli



  • pumuckl schrieb:

    1. Einen C-Wrapper für das Framework schreiben und den aus dem C#-Code "unsafe" aufrufen
    2. Einen C++/CLI Wrapper für das Framework schreiben, der dem C#-Code die nötige .NET Schnittstelle anbietet.

    Was ist vorzuziehen?

    Meiner Erfahrung nach wäre es vorzuziehen, gar keinen Wrapper zu schreiben. Besser einfach direkt den untersten Layer der entsprechenden Logik hinter ein C bzw. .NET basiertes Interface packen.

    pumuckl schrieb:

    Nebenbei brauche ich eine kleine Anwendung um C# zu lernen (d.h praktisch anzuwenden was ich theoretisch schon weiß), daher ist meine Wahl auf eine C#-GUI gefallen.

    Ich weiß nicht ob du dich wirklich mit Interop und C++/CLI rumschlagen willst, wenn du mit .NET noch nicht soviel Erfahrung hast...



  • Dravere schrieb:

    hustbaer schrieb:

    Der grösste Nachteil eines C-Wrappers ist vermutlich, dass du in C# nochmal wrappen musst - bzw. halt mit den "unmanaged" Handles/... der C-Seite hantieren.

    Das wirst du aber auch in C++/CLI nicht anders machen können. Wrappen muss man es immer.

    Warum soll man in C++/CLI nochmals was wrappen? Man kan hier native C/C++ direkt verwenden.

    Wenn es sich um Klassen in C++ handel, ist der einfachere und übersichtliche Weg auf jeden Fall C++/CLI...


  • Administrator

    Jochen Kalmbach schrieb:

    Warum soll man in C++/CLI nochmals was wrappen? Man kan hier native C/C++ direkt verwenden.

    Du kannst die C++ Klassen zwar in C++/CLI verwenden aber dann nicht in C#. Um sie in C# verwenden zu können, musst du einen Wrapper bauen.

    Grüssli



  • Dravere schrieb:

    Jochen Kalmbach schrieb:

    Warum soll man in C++/CLI nochmals was wrappen? Man kan hier native C/C++ direkt verwenden.

    Du kannst die C++ Klassen zwar in C++/CLI verwenden aber dann nicht in C#. Um sie in C# verwenden zu können, musst du einen Wrapper bauen.

    Und genau davon würde ich abraten. Denn Effektiv verbringst du dann viel Zeit (und vor allem Nerven, das würd ich bei C++/CLI nicht unterschätzen) damit, deinen Wrapper zu bauen, nur damit du dann in C# irgendwelche Logik damit implementierst, die du genausogut gleich in C++/CLI bauen und einfach von C# aus verwenden hättest können. Vor allem: Wenn sich was an dem Code um den der Wrapper gewrapped ist ändert, musst du nicht nur den Wrapper anpassen, sondern auch den C# Code der ihn verwendet. Wenn du einfach die unterste Schicht deiner Logik direkt in C++/CLI implementierst, musst du nur die anpassen/erweitern...

    Wrapper sind meiner Erfahrung nach selten (nie?) eine gute Idee. Bau dir lieber eine Bibkiothek, die genau die Funktionalität implementiert, die du in deiner Anwendung benötigst und nicht nur einfach ein Spiegelbild irgendeiner Library.



  • dot schrieb:

    Wenn du einfach die unterste Schicht deiner Logik direkt in C++/CLI implementierst, musst du nur die anpassen/erweitern...

    Vielleich thast Du ja die Frage nicht ganz verstanden, aber die C++ Klassen sind schon vorhanden... da ist nichts, was man neu machen müsste oder wollte...



  • Dravere schrieb:

    Jochen Kalmbach schrieb:

    Warum soll man in C++/CLI nochmals was wrappen? Man kan hier native C/C++ direkt verwenden.

    Du kannst die C++ Klassen zwar in C++/CLI verwenden aber dann nicht in C#. Um sie in C# verwenden zu können, musst du einen Wrapper bauen.

    Aber du musst mit C++/CLI nur einen Wrapper schreiben.

    A: ein C++/CLI Wrapper um native C++ Klassen

    B: ein C Wrapper um native C++ Klassen + noch ein C# Wrapper um die so entstandene C API

    Was wird wohl im Normalfall weniger Aufwand sein?

    Dravere schrieb:

    Und in C++/CLI musst vieles vom Marshaling selber machen, was vielfach in ein kleines Gefrickel endet. Zumindest ist das meine bisherige Erfahrung 🙂

    In C++ musst du gar kein Marshaling machen. Du schreibst einfach einen managed Wrapper um die native C++ Klassen, und fertig.
    Oder meinst du jetzt so Sachen wie System.String <-> C-String? Das muss man machen, ja. Dafür kann man sich auch aussuchen wie es gemacht wird. Und es gibt ja Haufenweise Helper-Klassen die einem den Grossteil der Arbeit abnehmen.



  • Jochen Kalmbach schrieb:

    dot schrieb:

    Wenn du einfach die unterste Schicht deiner Logik direkt in C++/CLI implementierst, musst du nur die anpassen/erweitern...

    Vielleich thast Du ja die Frage nicht ganz verstanden, aber die C++ Klassen sind schon vorhanden... da ist nichts, was man neu machen müsste oder wollte...

    Wieso, wenn es C++ Klassen sind kann ich sie doch nicht einfach so in .NET verwenden!?



  • Im GOF wird der Adapter auch Wrapper genannt.
    sind also die Patterns aus dem GOF Buch keine gute Idee? 😃



  • David W schrieb:

    sind also die Patterns aus dem GOF Buch keine gute Idee? 😃

    Hältst du Singleton für eine gute Idee? Das steht auch im GoF Buch...

    Das GoF Buch ist trotzdem gut. Das Problem sind nicht die dort beschriebenen Pattern, sondern wie sie verwendet werden.



  • dot schrieb:

    Wieso, wenn es C++ Klassen sind kann ich sie doch nicht einfach so in .NET verwenden!?

    Dann hast Du noch nie C++/CLI gemacht... genau dafür ist die Sprache entworfen worden 🤡



  • dot schrieb:

    David W schrieb:

    sind also die Patterns aus dem GOF Buch keine gute Idee? 😃

    Hältst du Singleton für eine gute Idee?

    Jup.



  • Jochen Kalmbach schrieb:

    dot schrieb:

    Wieso, wenn es C++ Klassen sind kann ich sie doch nicht einfach so in .NET verwenden!?

    Dann hast Du noch nie C++/CLI gemacht... genau dafür ist die Sprache entworfen worden 🤡

    Das hab ich natürlich schon, sonst würd ich hier doch nicht mitdiskutieren. Aber vielleicht gibts ja irgendwas ganz grundlegendes was ich nicht weiß. Könnte es tatsächlich einen Weg geben, wie ich eine native C++ Klasse nehmen kann und diese, ohne auch nur irgendwas an Code zu ändern/hinzuzufügen, direkt in .NET verwenden kann? I don't think so. Zumindest müsste ich eine ref class draus machen. Dann ist es aber keine C++ Klasse mehr...



  • David W schrieb:

    dot schrieb:

    David W schrieb:

    sind also die Patterns aus dem GOF Buch keine gute Idee? 😃

    Hältst du Singleton für eine gute Idee?

    Jup.

    Singleton ist natürlich an sich weder gut noch schlecht. Das Problem ist wieder wie es verwendet wird. Und das wird es leider in 100% (nein, nicht 99.9999%) aller Fälle, die ich bisher je gesehen hab, falsch, obwohl es im GoF Buch richtig steht.

    Aber die Diskussion hatten wir hier schon oft genug 😉


  • Administrator

    @dot,
    Das halte ich für keine gute Idee. Man verwendet die Bibliothek dann meistens nicht nur in diesem Programm. Willst du jedesmal nach C++/CLI greifen und die Logik dort implementieren? Dann musst du auch die anderen Bibliotheken, welche mit dieser interagieren, in C++/CLI verwenden. Am Ende machst du immer alles in C++/CLI und nur gerade das GUI in C#. Ich würde sagen, dass wird recht wartungsunfreundlich.

    Die darunterliegende Bibliothek sollte sich zudem nicht all zu stark verändern. Man wrappt ja normalerweise stabile Versionen. Dadurch verändert sich über die Zeit nicht all zu viel.

    @hustbaer,
    Ich bin davon ausgegangen, dass er eine C Schnittstelle schon anbietet, da er diese ja bereits implementieren wollte. Von daher wird er diese sowieso machen und der Aufwand ist dann gleich.

    In anderen Fällen hast du natürlich recht, dass man noch eine C Schnittstelle entwerfen muss.

    Zum Marshaling:
    Strings und Arrays ja. Ich empfand das als recht mühsam in C++/CLI. Ich hatte aber auch viele Strings zu verarbeiten bei meiner letzten Begegnung mit C++/CLI und dann auch noch zum Teil über unterschiedliche Encodings. Über P/Invoke empfand ich dies als wesentlich angenehmer.

    @David W,
    Die GOF Leute würden das Singleton Pattern heutzutage rausnehmen, wenn sie das Buch nochmals schreiben würden. 😉
    (Finde gerade die Quelle nicht mehr, aber war eine Aussage von einem der vier und wenn ich mich recht erinnere, hatte er mit 2 der anderen darüber gesprochen)

    Grüssli



  • Dravere schrieb:

    @dot,
    Das halte ich für keine gute Idee. Man verwendet die Bibliothek dann meistens nicht nur in diesem Programm. Willst du jedesmal nach C++/CLI greifen und die Logik dort implementieren? Dann musst du auch die anderen Bibliotheken, welche mit dieser interagieren, in C++/CLI verwenden. Am Ende machst du immer alles in C++/CLI und nur gerade das GUI in C#. Ich würde sagen, dass wird recht wartungsunfreundlich.

    Ich weiß nur, dass ich mir bisher jedes Mal, wenn ich was gewrapped hab, früher oder später gewünscht hab, dass ich es nicht gewrapped sondern abstrahiert hätte. Und zwar eigentlich sogar ganz unabhängig davon, in welcher Sprache ich grad unterwegs war. Darum meine Empfehlung nicht zu wrappen sondern zu abstrahieren 😉
    Mag natürlich sein, dass das daran liegt, dass es sich dabei praktisch immer um irgendwelche low-level APIs gehandelt hat...



  • Also, der Klarheit halber:

    1. Das C++-Framework ist in native C++ geschrieben, ist aber noch in der Entwicklung. Das heißt, es ändert sich noch, wobei die Schnittstellenklassen erstmal größtenteils fertig sind, nur die Zahl der möglichen Events wird noch deutlich wachsen.
    2. Die Logik-schicht im Framework wird nicht in C++/CLI geschrieben. Heißt, wie hustbaer sagt, dass eine C++/CLI-Schicht ein reiner Adapter wäre, um auf die native Schnittstelle aus .NET zugreifen zu können.
    3. Wenn ich aber irgendwann™ die C-API sowieso zur Verfügung stellen möchte, ist es doch relativ wurscht, ob ich den .NET-Adapter auf der C-API (in C#) oder auf dem Framework selber (in C++/CLI) aufsetze oder?

    Die Schnittstellen des Frameworks sind relativ simpel gehalten. Ich könnte mir vorstellen, die C-API und evtl. auch den .NET-Adapter zu ~90% generieren zu lassen.


Anmelden zum Antworten