2009-08-10 5 views
2

In meinem C++ Code, den ich eine Klasse Foo mit vielen Methoden habe einen Bar-Typen Variable als Argument nehmen:Wie Sie eine benutzerdefinierte implizite Konvertierung zu einem C++ - Typ in Boost :: Python hinzufügen?

class Foo { 
public: 
    void do_this(Bar b); 
    void do_that(Bar b); 
    /* ... */ 
}; 

Bar hat eine Reihe von Konstrukteuren, die ein neues Objekt aus vielen gängigen Typen wie int erstellen, std :: string, float, etc:

class Bar { 
public: 
    Bar(int i); 
    Bar(float f); 
    Bar(std::string s); 
    /* ... */ 
}; 

ich wickelte diese mit boost :: Python und ich bin jetzt in der Lage meine Foo Methoden verwenden Python Literale direkt aufrufen, da sie implizit Objekte Bar erhalten umgewandelt.

f = Foo() 
f.do_this(5) 
f.do_that("hello") 

Nun, ich möchte in der Lage sein, auch andere Python-Typen zu verwenden, wie Tupel, wie folgt aus:

f.do_that((1,2,3)) 

Aber ich will nicht die Original-Bar Definition berühren, und Ich möchte meine C++ - Bibliothek nicht mit Boost :: Python-Sachen verseuchen. Ich möchte eine Wrapper-Funktion in meinen Bindungscode schreiben, aber ich kann einfach nicht verstehen, ob dies möglich ist und was der richtige Weg dafür ist.

Mit anderen Worten: Kann ich eine Factory-Funktion registrieren, die in Python als automatische Konvertierung verwendet wird?

Antwort

0

Erstellen Sie eine Art TupleCollector mit überschriebene "Operator (int)", so dass Sie

f.do_that((TuppleCollector(), 1, 2, 3)) 

endlich schaffen Konvertierung zwischen TupleCollector und erwarteten Ziel

+0

danke, das ist eine interessante Idee, aber nicht das, was ich suche ... Ich möchte Tupel direkt verwenden. Es ist mir gelungen, einen neuen Konstruktor für Bar-akzeptierende Tupel zu erstellen, aber es gibt keine implizite Konvertierung, ich muss Bar ((1,2,3)) explizit schreiben. : | – UncleZeiv

+0

Ableiten von Tupel-Klasse und machen Sie eine beliebige Zuweisung von Int Sie wollen. (Operator, - ist auch anwendbar) – Dewfy

-1

schreiben können, können Sie Funktion exportieren do_that dem Schub nehmen: : python :: object param, überprüfe, ob param ein Python-Tupel ist, extrahiere Daten und gebe sie an object weiter.

2

Subklassen-Bar in der Nähe des Wrapper-Code und geben Sie Ihre Unterklasse eine Ctor, die eine bp :: Objekt (oder eine spezifischere Python-Typ)

struct Bar_wrapper:Bar,bp::wrapper<Bar> 
{ 
    Bar_wrapper(bp::object arg) 
    { 
     //code to build a Bar_wrapper Here 
    } 
} 

Dann exportieren ein Bar_wrapper Python statt einer Bar stattfindet, und nennen es eine Bar als python Name:

class<Bar_wrapper>("Bar") 
    ... 
    .def(init<bp::object>()) 
    ... 
+0

Nur um sicher zu gehen: Sie würden dann die neuen Überladungen hinzufügen, die andere Python-Typen in Bar_wrapper akzeptieren, richtig? – UncleZeiv

+0

bp :: object umfasst alles, einschließlich int und str. Setzen Sie die .defs für spezifischere Typen zuerst. Andere als das ja, überladen Sie den Ctor mit welchen Arten auch immer Sie sich fühlen. –

1

Fügen sie einen neuen Konstruktor template <typename T> Bar(T) in Ihrem Kopf und implementieren als template <> Bar::Bar(Tupple) {}

+0

UncleZeiv in der Frage erwähnt, dass er Bar nicht ändern wollte. Es ist nicht klar, warum du das herausforderst. –

0

Sie können eine statische Factory-Methode erstellen und sie dann als einen der Python-Konstruktoren für die Klasse verfügbar machen. Stellen Sie einfach einen Konverter her, der ein beliebiges Python-Objekt aufnehmen kann, und Sie können alles tun, was Sie wollen.

using namespace boost::python; 

Bar CreateBar(object obj) 
{ 
    // Do your thing here 

    return Bar; 
} 

// .................. 

class_<Bar>("Bar") 
    // ..................... 
    .def("__init__", make_constructor(&CreateBar)) 
    //............. 
    ; 
0

Sie können aus-Python-Wandler registrieren, die Bar Instanz von beliebigem Objekt konstruieren wird. Siehe here und ein Beispiel meiner eigenen (konvertiert entweder (Vector3,Quaternion) Tupel oder 7 * Doppel-Tupel zu 3D-Transformation Se3) here.

Beachten Sie, dass die Logik zwei Stufen hat, zuerst Sie bestimmen, ob das Objekt umwandelbar ist (convertible, in Ihrem Fall, Sie prüfen, ob es sich um eine Sequenz ist, und hat die richtige Anzahl der Elemente), dann ist die construct Methode aufgerufen wird , die die Instanz tatsächlich zurückgibt, die mit dem als Parameter übergebenen Zeiger zugewiesen wurde.

Der Konverter muss dann unter BOOST_PYTHON_MODULE registriert werden. Da die Registrierung des Konverters global ist, wird sie nach der Registrierung automatisch überall verwendet. Alle Funktionsargumente des Typs Bar oder const Bar& sollten gut behandelt werden (noch sicher über Bar& von der Spitze meines Kopfes).