How to pass Python instance to C ++ using Python / C API

I am expanding my library with Python (2.7), wrapping interfaces with SWIG 2.0, and have a graph object in which I want to create a visitor. In C ++, the interface looks like this:

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

I would like to define a class in Python that fulfills the equivalent defined in python that will allow the visitor to be defined:

 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) 

And what I'm trying to do is create an instance of GraphVisitor in a python script and call the OnStateBegin (), OnNode () and OnStateEnd () methods for this instance from C ++. Here is what I would like to do in Python:

 #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++? 

And in my C ++ wrapped up by Swig, I'm not sure how to get to the mvis instance? I can call functions defined in Python without problems, but the instances confuse me!

+6
source share
2 answers

To solve this problem, I extracted the class from the module, given its module name and class name (the code below assumes that the module is not loaded yet):

 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(); 

As soon as I got a module, I looked for a class from it:

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

I simplified my problem a bit by creating an instance of the python class in C ++, then created my visitor, and finally visited it:

  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); } } } } 

The visitor had 3 functions OnStateBegin (), OnNode () and OnStateEnd (). I added to my python SWIG binding generator the ability to generate a header file for external access to the SWIG runtime with an external long-term option , so I could create a C ++ class (below INode *) and pass it to Python as an argument to the function- Python OnNode () member as follows (validation error removed for brevity):

 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)); } 
+1
source

I don't know if this is possible using SWIG, but you can do it using 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); }; 

Using it from Python:

 >>> 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 

I put the zip file containing the source code of this test project on my home page.

0
source

Source: https://habr.com/ru/post/986176/


All Articles