Wie konvertiere ich eine Rust-Schließung in einen C-Style Callback?

9

Ich versuche, einen Rusty-Wrapper für ein Stück C-API zu schreiben. Es gibt ein C-Konstrukt, mit dem ich kämpfe:

%Vor%

Die Funktion führt ihre Aufgabe für einen Zahlenbereich aus, es sei denn, der Listener gibt false zurück. In diesem Fall bricht es die Berechnung ab. Ich möchte einen Rust-Wrapper so haben:

%Vor%

rust-bindgen hat das für mich erstellt, aus Gründen der Klarheit leicht bearbeitet:

%Vor%

Wie sollte ich eine Closure- oder Trait-Referenz in einen C-Style-Callback in meinen do_with -Funktionen konvertieren:

%Vor%     
Tomo 28.08.2015, 11:27
quelle

3 Antworten

16

Dies ist nur möglich, wenn die C-API die Übergabe eines vom Benutzer bereitgestellten Rückrufparameters erlaubt. Ist dies nicht der Fall, können Sie nur statische Funktionen verwenden.

Der Grund ist, dass Schließungen nicht "nur" funktionieren. Wie ihr Name andeutet, "schließen" Schließungen Variablen aus ihrem lexikalischen Geltungsbereich. Jede Schließung hat eine zugeordnete Dateneinheit, die entweder Werte von erfassten Variablen (wenn das move Schlüsselwort verwendet wird) oder Verweise darauf enthält. Diese Daten können als unbenannte, anonyme struct betrachtet werden.

Der Compiler fügt automatisch eine Implementierung der entsprechenden Fn* traits für diese anonymen Strukturen hinzu. Wie Sie sehen können akzeptieren Methoden für diese Merkmale self zusätzlich zur Schließung Argumente. In diesem Zusammenhang ist self der struct , auf dem das Merkmal implementiert ist. Das bedeutet, dass jede Funktion, die einem Closure entspricht, auch einen zusätzlichen Parameter besitzt, der die Closure-Umgebung enthält.

Wenn Ihre C-API nur Funktionen ohne benutzerdefinierte Parameter übergeben kann, können Sie keinen Wrapper schreiben, mit dem Sie Closures verwenden können. Ich denke, es ist möglich, einen globalen Halter für die Schließungsumgebung zu schreiben, aber ich bezweifle, dass es einfach und sicher wäre.

Wenn Ihre C-API es erlaubt, ein benutzerdefiniertes Argument zu übergeben, dann ist es möglich, das, was Sie wollen, mit Eigenschaftenobjekten zu tun:

%Vor%

Dies funktioniert nur, wenn do_something den Zeiger nicht irgendwo auf den Callback speichert. Wenn dies der Fall ist, müssen Sie das Objekt Box<Fn(..) -> ..> trait verwenden und es nach dem Übergeben an die Funktion leakieren. Dann sollte es nach Möglichkeit aus Ihrer C-Bibliothek zurückgeholt und entsorgt werden. Es könnte so aussehen:

%Vor%

Doppelte Indirektion (d. h. Box<Box<...>> ) ist notwendig, weil Box<Fn(..) -> ..> ein Merkmalobjekt und daher ein Fettzeiger ist, der wegen unterschiedlicher Größe mit *mut c_void inkompatibel ist.

    
Vladimir Matveev 28.08.2015, 11:38
quelle
2

In C hat ein Funktionszeiger keinen zugeordneten Kontext, weshalb normalerweise eine C-Callback-Funktion normalerweise ein zusätzliches void* -Argument über den Kontext ...

hinausgibt %Vor%

... oder haben Sie eine API, um die Benutzerdaten speichern zu lassen ...

%Vor%

Wenn die Bibliothek, die Sie umbrechen möchten, keine der oben genannten Funktionen bereitstellt, müssen Sie den Kontext über andere Kanäle übergeben, z. über eine globale Variable, obwohl dieser Kontext über den gesamten Prozess hinweg geteilt wird:

%Vor%

Es ist auch möglich, eine Trampolin-Funktion zu erstellen Stick den Kontext direkt in der Funktion, aber es ist extrem schwierig und unsicher.

Antwort manuell migriert von Ссылка

    
kennytm 06.03.2017 08:08
quelle
1

Der erste Ausschnitt von Vladimir Matveev funktioniert nicht mehr wie geschrieben. Die Größe von &mut FnMut(i32) -> bool und *mut c_void ist unterschiedlich und solche Umwandlungen führen zu einem Absturz. Korrigiertes Beispiel ( Laufstall ):

%Vor%     
Mingun 03.03.2017 20:24
quelle

Tags und Links