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

2024/10/18 13:14:32

I'm extending my library with Python (2.7) by 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 does the equivalent, all defined in python, that will allow for the definition of a visitor:

class GraphVisitor:def __init__(self, label):self._label = labelprint("__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 a GraphVisitor in python script, and call the methods OnStateBegin(), OnNode(), and OnStateEnd() for a given instance from C++. Here's what I'd 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 by Swig, I'm not sure how to get at the instance 'mvis'? I can call functions defined in Python no problem, but instances has me stumped!

Answer

In order to solve this problem, I retrieved the class from the module given it's module name and class name (the code below assumes the module hasn't already been loaded):

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

Once I had the module, I looked up the class from it's dictionary:

    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 instantiating 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 SWIG python binding generator an option to generate a header file for external access to the SWIG runtime with the -external-runtime option, so I could create a class in C++ (INode* below) and pass it to Python as the argument to the python OnNode() member function as follows (error checking 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));
}
https://en.xdnf.cn/q/73165.html

Related Q&A

REST API in Python with FastAPI and pydantic: read-only property in model

Assume a REST API which defines a POST method on a resource /foos to create a new Foo. When creating a Foo the name of the Foo is an input parameter (present in the request body). When the server creat…

a class with all static methods [closed]

Closed. This question is opinion-based. It is not currently accepting answers.Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.Clo…

How can I find null values with SELECT query in psycopg?

I am using psycopg2 library in python and the INSERT query works good when I insert null Value with None, but when I want to do SELECT null values, with None doesnt return any.cur.execute("SELECT …

Pause and continue stopwatch

I am trying to create stopwatch. I have done it but I would like to pause and continue the time whenever I want. I have tried some things but I have no idea how to do it. Is there anybody who would exp…

How do I escape `@` letter from SQL password in connection URI [duplicate]

This question already has an answer here:handle @ in mongodb connection string(1 answer)Closed 9 years ago.when you connect to mongodb using python from SQLAlchamey, we use mongodb://username:password@…

Set WTForms submit button to icon

I want a submit button that displays an icon rather than text. The button is a field in a WTForms form. I am using Bootstrap and Open Iconic for styling and icons. How do I set the submit field to d…

what is the significance of `__repr__` function over normal function [duplicate]

This question already has answers here:Purpose of __repr__ method?(6 answers)Closed 5 years ago.I am trying to learn python with my own and i stucked at __repr__ function. Though i have read lots of p…

Using celery with Flask app context gives Popped wrong app context. AssertionError

Im more or less using the setup to run Celery tasks using your flask app context from here: http://flask.pocoo.org/docs/0.10/patterns/celery/Im getting the same error message as Create, manage and kill…

How do I reduce the verbosity of chromedriver logs when running it under selenium?

My jenkins failure reports for my functional tests are full of lines like this:selenium.webdriver.remote.remote_connection: DEBUG: Finished Request selenium.webdriver.remote.remote_connection: DEBUG: P…

How to model python properties in UML diagram

What is a good practice to model Python properties in a UML class diagram? Properties themselves are class objects, their getter and setter are class functions. From Outside the class they look like i…