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

后端 未结 3 470
醉梦人生
醉梦人生 2020-12-25 15:01

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 th
相关标签:
3条回答
  • 2020-12-25 15:22

    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;
    }
    
    0 讨论(0)
  • 2020-12-25 15:25

    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
    
    0 讨论(0)
  • 2020-12-25 15:34

    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;
    }
    
    0 讨论(0)
提交回复
热议问题