2015-04-28 25 views
6

Rozszerzam moją bibliotekę o Python (2.7), owijając interfejsy SWIG 2.0, i mam obiekt wykresu, w którym chcę utworzyć gościa. W C++ interfejs wygląda tak:Jak przekazać instancję Pythona do C++ za pośrednictwem Pythona/C API

struct Visitor 
    { 
     virtual void OnStateBegin() = 0; 
     virtual void OnNode(Node* n) = 0; 
     virtual void OnStateEnd() = 0; 
    }; 

chciałbym zdefiniować klasę w Pythonie, który robi równowartość, które zostały zdefiniowane w Pythonie, który pozwoli na definicji odwiedzającego:

class GraphVisitor: 
    def __init__(self, label): 
     self._label = label 
     print("__init__(self,{0})".format(self._label)) 
    def OnStateBegin(self): 
     print("OnStateBegin()" + self._label) 
    def OnNode(self, i_node): 
     print("OnNode()" + self._label) 
    def OnStateEnd(self): 
     print("OnStateEnd()" + self._label) 

Co próbuję zrobić, to utworzyć instancję GraphVisitora w skrypcie Pythona i wywołać metody OnStateBegin(), OnNode() i OnStateEnd() dla danej instancji z C++. Oto, co chciałbym robić w Pythonie:

#model is a SWIG wrapped class 
mvis = GraphVisitor("This is a test") 
model.Visit("mvis") # I'm not sure how to pass the instance 'mvis' to C++? 

I w moim C++ owinięty przez Swig, nie jestem pewien, w jaki sposób dostać się na przykład „mvis”? Mogę wywoływać funkcje zdefiniowane w Pythonie bez problemu, ale instancje mnie zaskoczyły!

+1

'mvis' to tylko przykład klasy Pythona. Ma to do czynienia z twoim 'struct Visitor'. W C/C++ można uzyskać do niego dostęp tylko jako 'PyObject *'. – Thomas

+0

Wiem o tym. Próbowałem opisać, że próbuję zdefiniować odwiedzającego w Pythonie i to jest odpowiednik C++. –

+1

Widziałeś moją poprzednią odpowiedź: http://stackoverflow.com/questions/9040669/how-can-i-implement-ac-class-in-python-to-be-called-by-c/9042139#9042139 (możesz pominąć bity dotyczące osadzania, ale bit o konwersję do interfejsu C++ z PyObject robi dokładnie to, co chcesz) – Flexo

Odpowiedz

0

Nie wiem, czy to możliwe dzięki SWIG, ale można to zrobić za pomocą SIP.

sip_vector_test.h:

class EXPORT Node { 
public: 
    explicit Node(int n) : n_(n) {}; 
    int getN() const { return n_; } 
private: 
    int n_; 
}; 
struct EXPORT NodeVisitor { 
    virtual void OnNode(Node* n) = 0; 
}; 
struct EXPORT Graph { 
public: 
    void addNode(int num); 
    void accept(NodeVisitor *nv); 
private: 
    std::vector< std::shared_ptr<Node> > nodes_; 
}; 

visitor.sip:

%Module pyvisit 

%ModuleHeaderCode 
#include "sip_visitor_test.h" 
%End 

class Node { 
public: 
    explicit Node(int n); 
    int getN() const; 
}; 
struct NodeVisitor { 
    virtual void OnNode(Node* n) = 0; 
}; 
struct Graph { 
public: 
    void addNode(int num); 
    void accept(NodeVisitor *nv); 
}; 

Używanie go z Pythona:

>>> import pyvisit 
>>> g = pyvisit.Graph() 
>>> g.addNode(3) 
>>> g.addNode(5) 
>>> class PyNodeVisitor(pyvisit.NodeVisitor): 
>>>  def OnNode(self, node): 
>>>   print(node.getN()) 
>>> pnv = PyNodeVisitor() 
>>> g.accept(pnv) 
3 
5 

Umieściłem zip file zawierający kod źródłowy tego przetestuj projekt na mojej stronie głównej.

+0

Wielkie dzięki za odpowiedź! Nie używam SIP i chciałem sprawdzić, czy można to zrobić bez dodawania nowego typu. –

1

W celu rozwiązania tego problemu, ja odzyskać klasę z modułu biorąc pod uwagę to moduł imię i nazwisko klasa (poniżej kod zakłada, że ​​moduł nie został już załadowany):

void Model::Visit(const char* mod_name, const char* class_name) 
{ 
    PyErr_Clear(); 
    PyObject* mod_name_obj = PyString_FromString(mod_name); 
    PyObject* class_name_obj = PyString_FromString(class_name); 

    PyObject* py_module = PyImport_Import(mod_name_obj); 
    PyObject* err_1 = PyErr_Occurred(); 
    if(err_1) 
     PyErr_Print(); 

Kiedyś miałem moduł, spojrzałem w górę klasy z jego słownika:

if(py_module) 
    { 
     PyObject* py_module_dict = PyModule_GetDict(py_module); 
     PyObject* py_class = PyDict_GetItem(py_module_dict, class_name_obj); 

i uproszczone mojego problemu nieco przez instancję klasy Pythona w C++, a następnie stworzył mój gość, a na końcu odwiedził go:

 if(py_class && PyClass_Check(py_class) && PyCallable_Check(py_class)) 
     { 
      PyObject* inst = PyInstance_New(py_class, 0, 0); 

      if(inst && PyInstance_Check(inst)) 
      { 
       IModel::IVisitorPtr py_visitor = new PyModelVisitor(inst); 

       _model->Visit(py_visitor); 
      } 
     } 
    } 
} 

Użytkownik miał 3 funkcje OnStateBegin(), OnNode() i OnStateEnd(). Dodałem do mojego generatora powiązań Pythona SWIG opcję wygenerowania pliku nagłówkowego dla zewnętrznego dostępu do środowiska wykonawczego SWIG z -external-runtime option, więc mogłem stworzyć klasę w C++ (INode * poniżej) i przekazać ją do Pythona jako argument do Pythona Funkcja członkowska OnNode() w następujący sposób (sprawdzanie błędów usunięte dla zwięzłości):

VisitorCtrl OnNode(INode* node) 
{ 
    Node* node_impl = new NodeImpl(node); 
    PyObject* pynode = SWIG_NewPointerObj(node_impl, SWIG_TypeQuery("Node *"), 0); 
    PyObject* result = PyObject_CallMethodObjArgs(_inst, PyString_FromString("OnNode"), pynode, 0); 

    long rivis = PyInt_AsLong(result); 

    return(static_cast<VisitorCtrl>(rivis)); 
}