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:
Wie sollte ich eine Closure- oder Trait-Referenz in einen C-Style-Callback in meinen do_with
-Funktionen konvertieren:
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:
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.
In C hat ein Funktionszeiger keinen zugeordneten Kontext, weshalb normalerweise eine C-Callback-Funktion normalerweise ein zusätzliches void*
-Argument über den Kontext ...
... 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 Ссылка
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 ):