Exposing a C++ class instance to a python embedded interpreter Exposing a C++ class instance to a python embedded interpreter python python

Exposing a C++ class instance to a python embedded interpreter


Boost python Allows you to expose c++ classes to python in a very tightly integrated way - you can even wrap them so that you can derive python classes from your c++ ones, and have virtual methods resolved to the python overrides.

The boost python tutorial is a good place to start.


edit:

You can create a c++ object and pass a reference to it to an internal python interpreter like this:

#include <boost/shared_ptr.hpp>#include <boost/make_shared.hpp>#include <boost/python.hpp>#include <string>#include <iostream>namespace bp = boost::python;struct Foo{    Foo(){}    Foo(std::string const& s) : m_string(s){}    void doSomething() {        std::cout << "Foo:" << m_string << std::endl;    }    std::string m_string;};typedef boost::shared_ptr<Foo> foo_ptr;BOOST_PYTHON_MODULE(hello){    bp::class_<Foo, foo_ptr>("Foo")        .def("doSomething", &Foo::doSomething)    ;};int main(int argc, char **argv){    Py_Initialize();    try {        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"        );        foo_ptr a_cxx_foo = boost::make_shared<Foo>("c++");        inithello();        bp::object main = bp::object(bp::handle<>(bp::borrowed(            PyImport_AddModule("__main__")        )));        // pass the reference to a_cxx_foo into python:        bp::object setup_func = main.attr("setup");        setup_func(a_cxx_foo);        // now run the python 'main' function        bp::object run_func = main.attr("run");        run_func();    }    catch (bp::error_already_set) {        PyErr_Print();    }    Py_Finalize();    return 0;}


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++ classPYBIND11_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;}


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