Delegate als C Feature

Ich hatte ja schon mal über Delegetes geschrieben und meine Lösung angegeben, die ich schon seit über 10 Jahren einsetze.

Doch jetzt kommt alles anders:

Reiner C Code soll Delegate tauglich werden!


In C++ ist mit Templates ja so gut wie alles möglich. Das fing schon vor Jahrzehnten mit std::bind an und hat heute mit Lambdas einen weiteren Höhepunkt erreicht.

Auch meine bisherigen Delegate-Implementierung nutzten Templates um mit “einer” Klasse alle möglichen Konstellationen von Parametertypen abdecken zu können.

Doch in C haben wir dieses Feature nicht und müssten für jede Parameterzusammenstellung eine eigene Implementierung von Hand schaffen.
Auch Makros lösen dieses Problem meines Erachtens nicht (obgleich sie einige Ansätzen liefern könnten).

Die große Zielsetzung des GATE Projektes ist jedoch die Schaffung einer binären Schnittstelle für alle Basisoperationen, und das ist bereits in vielen Teilen gelungen, nur nicht bei Callbacks und Delegates.

Jetzt gibt es aber tatsächlich eine Möglichkeit, wie man alle Formen von Parameterkonstellationen durch eine Schnittstelle, genauer gesagt einen einzigen Funktionstypen jagen kann, und der heißt:
Variadic functions, kurz var_args.

Denn sobald eine Funktion mit einem ... in der Parameterliste endet, darf der Anwender jeden erdenklichen Parameter an die Funktion übergeben. Das Knifflige dabei ist, dieses Parameter in der Funktion wieder vom Stack herunter zu bekommen.

Wie sieht also das GATE C-Delegate Konzept aus?

Ein Delegate Objekt besteht aus einem var_arg Funktionszeiger und einem ausreichend großem Speicherplatz für die gewünschte Zielfunktion.

Will man ein solches Delegate Objekt nun an seine eigene Funktion oder Objektmethode binden, muss eine Dispatcher Funktion erstellt werden, die die Parameter aus einem var_arg Feld herauskitzeln kann und damit einen Funktionszeiger oder Methodenzeiger aufruft, der in im oben erwähnten Speicherplatz geparkt ist.

Der Aufruf des Delegates ist einfach, denn hier wird die im Delegate gelegene Dispatcher-Funktion über den Funktionszeiger direkt aufgerufen und der erste Parameter ist das Delegate-Objekt selbst, gefolgt von jenen “freien” Parameter, die zur “delegierten” Funktion passen.

Aber Achtung! Eines muss man gleich im Vorfeld sagen: Typensicherheit seitens des Compilers gibt es dann natürlich keine, denn die var_args fressen alles, doch die Dispatcher können nur das verdauen, wozu sie berufen sind.

Auch gibt es keinen Support für Kopien von Objekten (weil C keine Kopierkonstruktoren kennt) oder Referenzen (obwohl sich diese leicht in Pointer umwandeln lassen).

Wir schaffen somit eine Lösung, wo C Code einen Delegate aufrufen kann und dieser den Aufruf in eine C Funktion, eine C++ Objektmethode, in einen Funktor oder Lambdaausdruck oder auch etwas ganz anderes übersetzen kann.

Besonders spannend wird es, wenn man dieses Feature auf andere Sprachen wie Java, Python oder Lua anwendet.

Während wir in C auf Makros setzen müssen um Dispatcher zu generieren, werden auf der C++ Seite Templates die angesprochenen Dispatcher automatisch erzeugen. Sogar der Support von Exceptions ist teilweise gegeben, weil die aufgerufene Funktion ge-try-catch-t und in einen gate_result_t abgebildet wird.
Gleichzeitig sieht die C++ Kapselung des Aufruf eines Delegates vor, dass ein empfangener gate_result_t wieder auf eine Exception übersetzt wird.
Dabei gehen (derzeit) zwar einige Informationen verloren, doch der logische Ablauf bleibt gleich.

Auf eines müssen wir ebenso verzichten: Return-values, denn diese müssen fix gate_result_t Codes sein und dürfen nur über Erfolg und Misserfolg des Aufrufs berichten.

Wenn man aber in Richtung Multicast-Delegates denkt, wo ein Aufruf mehrere Zielaufrufe bedeutet, ist das sogar ein Vorteil, weil man dann nicht mehrere Return-Values wieder zu einem einzigen zusammensetzen muss.


Die GATE UI Bibliotheken sind bereits für weitere Tests auf die neuen “alten” C Delegates umgestellt und werden weiter “beobachtet”, ob sie auch wirklich so gut funktionieren wie gedacht.

Offen gesagt freut es mich etwas, dass nun nach 10 Jahren endlich wieder etwas ganz Neues in meine Codes eingeflossen ist, was die Bandbreite der Anwendung erhöhen kann und gleichzeitig Kompatibilität bis in die 80er Jahre zurück ermöglicht.

Natürlich sind diese Konstrukte keine Rennpferde und daher werden klassische Callbacks auch weiterhin intern eingesetzt. Doch gerade bei UI-Events oder generell “Events”, die nicht zig mal pro Sekunde aufgerufen werden, ist dieser Layer durchaus mal einen Versuch wert.

Mal sehen wie sich das weiter entwickeln lässt ;)