'Exposing a C++ class instance to a python embedded interpreter
I am looking for a simple way to expose a C++ class instance to a python embedded interpreter.
- I have a C++ library. This library is wrapped (using swig for the moment) and I am able to use it from the python interpreter
- I have a C++ main program which instanciates a Foo class from my library and embeds a python interpreter
I would like to expose my C++ world instance of Foo to the python world (and seen as a Foo class).
Is this possible, if so, how?
I think it's almost like in the first answer of : boost::python::ptr or PyInstance_New usage
I guess this means I should use boost.Python to wrap my library?
My only goal is to manipulate my C++ instance of Foo in the embedded python interpreter (not sure that it can be done with the previous method).
In fact, I already have exposed my Foo class to python (with swig).
What I have:
my Foo class:
class Foo{...};
my wrapped library (including the Foo class) exposed to python: so I can start the python interpreter and do something like this :
import my_module
foo=my_modulde.Foo()
What I want:
Having a C++ main program which embeds a python interpreter and manipulates C++ world variables.
int main(int argc, char **argv)
{
Foo foo; // instanciates foo
Py_Initialize();
Py_Main(argc, argv); // starts the python interpreter
// and manipulates THE foo instance in it
Py_Finalize();
return 0;
}
Solution 1:[1]
For reference, here is how you can achieve this using pybind11:
#include <iostream>
#include <pybind11/pybind11.h>
namespace py = pybind11;
// Define C++ class "Foo"
class Foo {
std::string s_;
public:
Foo(const std::string &s) : s_(s) {}
void doSomething() { std::cout << s_ << std::endl; }
};
typedef std::shared_ptr<Foo> FooPtr;
// Define Python module "bar" and Python class "bar.Foo" wrapping the C++ class
PYBIND11_MODULE(bar, m) {
py::class_<Foo, FooPtr>(m, "Foo")
.def("doSomething", &Foo::doSomething);
}
int main(int argc, char **argv)
{
// Create a C++ instance of Foo
FooPtr foo = std::make_shared<Foo>("Hello, World!");
// Initialize Python interpreter and import bar module
PyImport_AppendInittab("bar", PyInit_bar);
Py_Initialize();
PyRun_SimpleString("import bar");
// Make C++ instance accessible in Python as a variable named "foo"
py::module main = py::module::import("__main__");
main.attr("foo") = foo;
// Run some Python code using foo
PyRun_SimpleString("foo.doSomething()");
// Finalize the Python interpreter
Py_Finalize();
return 0;
}
Solution 2:[2]
I know this is an old question, but here is a solution using SWIG.
foo.h:
#pragma once
#include <string>
struct Foo{
Foo();
Foo(std::string const& s);
void doSomething();
std::string m_string;
};
foo.cpp:
#include "foo.h"
#include <iostream>
Foo::Foo() {}
Foo::Foo(std::string const& s) : m_string(s) {}
void Foo::doSomething() {
std::cout << "Foo:" << m_string << std::endl;
}
foo.i:
%module module
%{
#include "foo.h"
%}
%include "std_string.i"
%include "foo.h"
Generate the usual SWIG wrapper together with a runtime
swig -python -c++ -Wall foo.i
swig -python -c++ -Wall -external-runtime runtime.h
Generate the SWIG module containing struct Foo:
g++ -fPIC -Wall -Wextra -shared -o _module.so foo_wrap.cxx foo.cpp -I/usr/include/python2.7 -lpython2.7
If you want to share type information across multiple modules, an argument -DSWIG_TYPE_TABLE=SomeName can be added.
Now, here is how a C++ instance of Foo is passed to the interpreter
#include "foo.h"
#include <Python.h>
#include "runtime.h"
int main(int argc, char **argv) {
Py_Initialize();
PyObject* syspath = PySys_GetObject((char*)"path");
PyObject* pName = PyString_FromString((char*) ".");
int err = PyList_Insert(syspath, 0, pName);
Py_DECREF(pName);
err = PySys_SetObject((char*) "path", syspath);
PyObject *main, *module, *pInstance, *run, *setup;
try {
main = PyImport_ImportModule("__main__");
err = PyRun_SimpleString(
"a_foo = None\n"
"\n"
"def setup(a_foo_from_cxx):\n"
" print 'setup called with', a_foo_from_cxx\n"
" global a_foo\n"
" a_foo = a_foo_from_cxx\n"
"\n"
"def run():\n"
" a_foo.doSomething()\n"
"\n"
"print 'main module loaded'\n");
// Load Python module
module = PyImport_ImportModule("module");
swig_type_info *pTypeInfo = nullptr;
pTypeInfo = SWIG_TypeQuery("Foo *");
Foo* pFoo = new Foo("Hello");
int owned = 1;
pInstance =
SWIG_NewPointerObj(reinterpret_cast<void*>(pFoo), pTypeInfo, owned);
setup = PyObject_GetAttrString(main, "setup");
PyObject* result = PyObject_CallFunctionObjArgs(setup, pInstance, NULL);
Py_DECREF(result);
run = PyObject_GetAttrString(main, "run");
result = PyObject_CallFunctionObjArgs(run, NULL);
Py_DECREF(result);
}
catch (...) {
PyErr_Print();
}
Py_DECREF(run);
Py_DECREF(setup);
Py_DECREF(pInstance);
Py_DECREF(module);
Py_DECREF(main);
Py_Finalize();
return 0;
}
The above can be compiled by:
g++ -Wall -Wextra -I/usr/include/python2.7 main.cpp foo.cpp -o main -lpython2.7
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Jens Munk |
