Search code examples
c++pythonboostswigboost-python

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

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