Ich arbeite gerade mit Boost.Python und möchte etwas Hilfe, um ein heikles Problem zu lösen.
Kontext
Wenn eine C ++ - Methode / -Funktion mit Python in Verbindung gebracht wird, muss sie die GIL (Global Interpreter Lock) freigeben, damit andere Threads den Interpreter verwenden können. Auf diese Weise kann der Interpreter von anderen Threads verwendet werden, wenn der Python-Code eine C ++ - Funktion aufruft. Fürs Erste sieht jede C ++ - Funktion folgendermaßen aus:
%Vor%Um es an boost python zu übergeben, mache ich:
%Vor%Problem
Dieses Schema funktioniert gut, aber es impliziert, dass module.cpp
ohne guten Grund von Boost.Python
abhängt. Im Idealfall sollte nur python_exposure.cpp
von Boost.Python
abhängen.
Lösung?
Meine Idee war, mit Boost.Function
zu spielen, um die Funktionsaufrufe so zu verpacken:
Hier wäre wrap
verantwortlich dafür, die GIL während des Aufrufs von myfunction
zu entsperren. Das Problem mit dieser Methode ist, dass wrap
dieselbe Signatur wie myfunction
haben muss, was ziemlich viel bedeutet, dass Boost.Function
...
Ich wäre sehr dankbar, wenn jemand einen Vorschlag für dieses Problem hätte.
Das Anzeigen von Funktoren als Methoden wird nicht offiziell unterstützt >. Der unterstützte Ansatz wäre, eine Nicht-Member-Funktion verfügbar zu machen, die an die Member-Funktion delegiert. Dies kann jedoch zu einer großen Anzahl von Codeblöcken führen.
Soweit ich das beurteilen kann, schließt die Implementierung von Boost.Python Funktoren nicht explizit aus, da Instanzen von python::object
als Methode verfügbar gemacht werden können. Boost.Python stellt jedoch einige Anforderungen an den Objekttyp, der als Methode verfügbar gemacht wird:
o
kann o(a1, a2, a3)
. boost::python::detail::get_signature()
auf, um diese Metadaten zu erhalten. Die Meta-Daten werden intern verwendet, um den korrekten Aufruf einzurichten, sowie um von Python nach C ++ zu senden. Die letzte Anforderung ist, wo es komplex wird. Aus irgendeinem Grund, der mir nicht sofort klar ist, ruft Boost.Python get_signature()
über eine qualifizierte ID auf und verhindert so das argumentabhängige Nachschlagen. Daher müssen alle Kandidaten für get_signature()
vor dem Definitionskontext der aufrufenden Vorlage deklariert werden. Die einzigen Überladungen für get_signature()
, die berücksichtigt werden, sind beispielsweise diejenigen, die vor der Definition von Vorlagen deklariert wurden, die sie aufrufen, z. B. class_
, def()
und make_function()
. Um dieses Verhalten zu berücksichtigen, muss beim Aktivieren eines Funktors in Boost.Python eine get_signature()
-Überladung angegeben werden, bevor Boost.Python eingeschlossen wird, oder explizit eine Meta-Sequenz bereitgestellt werden, die die Signatur für make_function()
darstellt.
Lassen Sie uns einige Beispiele für die Aktivierung der Funktor-Unterstützung durcharbeiten und stellen Funktoren zur Verfügung, die Wächter unterstützen. Ich habe mich entschieden, keine C ++ 11-Funktionen zu verwenden. Daher wird es einen Standardcode geben, der mit variablen Vorlagen reduziert werden könnte. Darüber hinaus verwenden alle Beispiele dasselbe Modell, das zwei Nichtmitgliedsfunktionen und eine spam
-Klasse mit zwei Memberfunktionen bereitstellt:
boost::function
Bei Verwendung der bevorzugten Syntax für Boost.Function Die Zerlegung der Signatur in Meta-Daten, die Boost.Python-Anforderungen erfüllen, kann mit Boost.FunctionTypes . Hier ist ein vollständiges Beispiel, das boost::function
funktors als Boost.Python-Methode verfügbar macht:
Und seine Verwendung:
%Vor% Wenn ein Funktor bereitgestellt wird, der eine Member-Funktion aufruft, muss die angegebene Signatur das Äquivalent der Nicht-Member-Funktion sein. In diesem Fall wird int(spam::*)(int)
zu int(spam&, int)
.
Auch können Argumente mit boost::bind
an die Funktoren gebunden werden . Beispielsweise muss der Aufruf von example.times_two()
kein Argument bereitstellen, da 21
bereits an den Funktor gebunden ist.
Nach dem obigen Beispiel kann man benutzerdefinierte Funktortypen mit Boost.Python verwenden. Lassen Sie uns einen Funktor namens guarded_function
erstellen, der RAII verwendet und nur die Wrapped-Funktion während der Lebensdauer des RAII-Objekts aufruft.
Das guarded_function
bietet eine ähnliche Semantik wie die with
Anweisung von Python. Um mit den Namen des Boost.Python-API-Namens zu bleiben, bietet eine with()
C ++ - Funktion eine Möglichkeit, Funktoren zu erstellen.
Dadurch können Funktionen freigelegt werden, die auf nicht-intrusive Weise mit einem Guard laufen:
%Vor% Darüber hinaus bietet die with()
-Funktion die Möglichkeit, die Funktionssignaturen abzuleiten, sodass die Metadatensignatur Boost.Python explizit zur Verfügung gestellt werden kann, anstatt boost::python::detail::get_signature()
zu überladen.
Hier ist das vollständige Beispiel mit zwei RAII-Typen:
no_gil
: Gibt GIL im Konstruktor frei und ruft GIL in destructor erneut ab. echo_guard
: Druckt in Konstruktor und Destruktor. Und seine Verwendung:
%Vor% Beachten Sie, wie mehrere Wächter mithilfe eines Containertyps wie boost::tuple
:
Bei Aufruf in Python erzeugt example.times_two(21)
die folgende Ausgabe:
Wenn jemand interessiert ist, hatte ich ein kleines Problem mit dem Code von Tanner Sansbury , wenn ich sein letztes Arbeitsbeispiel benutze. Aus irgendeinem Grund hatte ich immer noch das Problem, dass er im letzten generierten boost::function
eine falsche Signatur hatte:
auch beim Überladen von boost::python::detail::get_signature()
. Verantwortlich dafür war boost::function_types::components
; Es hat einen Standard-Template-Parameter ClassTranform = add_reference<_>
, der diese Klassenreferenz erstellt. Um dies zu beheben, änderte ich einfach die mpl_signature
struct wie folgt:
Und jetzt funktioniert alles wie ein Zauber.
Wenn jemand bestätigen kann, dass dies in der Tat die richtige Lösung ist, wäre ich interessiert:)
Tags und Links python c++ boost function boost-python