2016-06-07 13 views
0

Wir haben eine Reihe von C++ - Klassen, die wir Python mit Swig aussetzen. Wir fügen den Methoden oft neue Argumente hinzu. Auf der anderen Seite haben wir eine Reihe von Python-Skripten, die wir so einfach wie möglich verwalten möchten. Wenn wir also die Menge der Argumente einer exponierten Methode in C++ ändern, wollen wir nicht alle unsere Python-Skripte ändern müssen, die diese Methode verwenden. Zu diesem Zweck haben wir für jede unserer C++ - Klassen eine sekundäre Schnittstellenklasse (auch C++ - Klasse) erstellt, die einen Zeiger auf eine Instanz der ursprünglichen Klasse besitzt und eine vereinfachte Schnittstelle zur Verfügung stellt.Swig: Der beste Weg, um eine vereinfachte Schnittstelle zu handhaben

zum Beispiel vorstellen, dass wir eine Klasse A mit einer Methode foo haben:

class A { 
    public : 
    void foo (int a, int b, double c, char * d, ....); 
}; 

Und wenn wir zufrieden sind nur die a, b und c Argumente in Python (Standardwert für die andere vorbei).

Wir schaffen eine APY Klasse

class APy { 
public : 
    void foo(int a,int b, double c) { 
    A->foo(a,b,c,default,default,...); 
    } 
protected : 
    A * ref; 
}; 

Der Vorteil besteht darin, dass es möglich ist, neue Parameter A :: foo hinzufügen, die Parameter Aufträge ändern, den Namen des Verfahrens und allgemeiner für komplexere Änderungen, kodieren Sie eine Äquivalenz der ursprünglichen Methode ohne Änderung der bestehenden Python-Skripte. Wenn ein Skript ein neues Argument benötigt, ist es außerdem möglich, es über ein Standardargument in APy :: foo hinzuzufügen, ohne dass dies in der ursprünglichen A :: foo-Methode gemacht werden muss.

Der Nachteil ist, dass es delikat ist, mit dem Löschen umzugehen, da es zwei Objekte gibt. Sollten wir A löschen, wenn APy zerstört wird? Es kommt darauf an. Manchmal ist A ein inneres Objekt eines größeren Objekts und sollte nicht zerstört werden. Aber APy sollte immer dann zerstört werden, wenn es vom Python-Skript nicht mehr verwendet wird.

Ich frage mich, ob es möglich wäre, eine einfachere, aber so bequeme Architektur zu entwickeln, indem man direkt die ursprünglichen Klassen und Methoden abbildet, indem man swig bittet, die Argumentliste selbst zuzuordnen und zu vereinfachen.

Ist es möglich, in der .i-Datei Swig zu bitten, die Reihenfolge der Argumente zu ändern, Standardwerte für nicht belichtete Argumente (nicht .h Standardwerte) zu setzen?

Zum Beispiel, wenn ich

void foo(int a, double b, int c); 

eine Funktion habe, und ich möchte nur ein und c in umgekehrter Reihenfolge in der Skriptsprache halten und setzen b bis 10,0

Also, wenn ich würde rufen:

foo(3, 5) 

es tatsächlich abgebildet werden würde:

foo(5,10.0,3) 

Wie auch immer, ich würde gerne von Ihrer Erfahrung hören, was Sie für die beste Art halten, mit solchen Problemen umzugehen.

+0

Ist hier das richtige Werkzeug? Automatisch zeigt Swig jede Änderung an, die Sie vornehmen. Und das versuchen Sie zu verhindern. Für Python-Erweiterungen, die Sie die Python-API von steuern möchten, möchten Sie vielleicht PyCXX betrachten. Sie müssen die Klassen für sich selbst definieren, aber Sie haben die vollständige Kontrolle über die API und können die Abwärtskompatibilität auf Ihre Weise anpassen. –

+0

Wenn die Beziehung zwischen 'A' und' APy' eins zu eins ist. Dh wenn 'APy' exclusive eine eigene Instanz von' A' hat, dann können Sie nach [RAII] (https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization) 'A' in' APy's' zuweisen und freigeben c'tor' und 'd'tor' jeweils. Für Ihre Anforderung wird "APy" als "Proxy" von "A" fungieren. – sameerkn

+0

Ich kannte PyCXX nicht. Werde einen Blick darauf werfen. Problem ist, dass es nicht mehr möglich wäre, eine Schnittstelle für andere Skriptsprachen zu erzeugen. Das RAII-Design wäre in Ordnung, aber es ist nicht immer möglich. Zum Beispiel. Stellen Sie sich eine Netzwerk- und Knoten-Klasse mit einer Network :: getNode() -Methode vor. Der zurückgegebene Knoten sollte nicht gelöscht werden, da Knoten Teile der Netzwerkobjekte sind (nicht des NodePy-Schnittstellenobjekts). –

Antwort

0

Ich denke, dass Sie Ihr Problem schön mit SWIG lösen können, indem Sie Tricks mit seinen %ignore, %rename und %extend Richtlinien spielen. Bitte schauen Sie sie in der SWIG docs für Details.Aber für Ihr obiges Beispiel könnten Sie die folgende Schnittstelle Datei:

%module example 

%{ 
    #define SWIG_FILE_WITH_INIT 
%} 

%ignore A::foo(int a, double b, int c); 

%inline %{ 
    class A { 
    public : 
    double foo(int a, double b, int c) { 
     return a * b - c; 
    } 
    }; 
%} 

%extend A { 
    double foo(int c, int a) { 
    return $self->foo(a, 10.0, c); 
    } 
}; 

(Beachten Sie, dass anstelle von %inline %{ ... %} können Sie natürlich auch %{ #include "example.h" %} und %include "example.h" verwenden, wenn Sie Ihre Funktionen in einer Header-Datei deklariert haben.)

Mit diesem Ansatz müssen Sie die SWIG-Schnittstellendatei aktualisieren, wenn Sie den C++ - Code ändern, aber Sie sollten in der Lage sein, die API anzupassen, die Python verfügbar gemacht wird.

Es gibt einen anderen Trick, den Sie wissen sollten, nämlich, dass Sie %ignore anweisen können, viel mehr zu entsprechen, z. Alle Methoden oder alle Versionen von A::foo. Aber dann müssen Sie sie zuerst "unignore", bevor Sie %extend verwenden können. Also hier ist ein etwas aufwendigeres/Fortgeschrittene Beispiel, das diesen (Standard SWIG) Trick zeigt:

%module example 

%{ 
    #define SWIG_FILE_WITH_INIT 
%} 

%ignore A::foo; 

%inline %{ 
    class A { 
    public : 
    double foo(int a, double b, int c) { 
     return a * b - c; 
    } 
    double foo(int a, double b) { 
     return a * b; 
    } 
}; 
%} 

%rename("%s") A::foo; 

%extend A { 
    int foo(int c, int a) { 
    return $self->foo(a, 10.0, c); 
    } 
}; 

Auch hier hat einen Blick auf der SWIG-Dokumentation für die genauen Details.

Verwandte Themen