【问题标题】:Exposing a C++ class instance to a python embedded interpreter将 C++ 类实例暴露给 python 嵌入式解释器
【发布时间】:2012-01-03 18:51:11
【问题描述】:

我正在寻找一种将 C++ 类实例公开给 python 嵌入式解释器的简单方法。

  • 我有一个 C++ 库。这个库被包装了(暂时使用 swig),我可以从 python 解释器中使用它
  • 我有一个 C++ 主程序,它从我的库中实例化一个 Foo 类并嵌入一个 python 解释器

我想将我的 Foo 的 C++ 世界实例公开给 python 世界(并被视为 Foo 类)。

这可能吗,如果可以,怎么做?

我认为这几乎就像在第一个答案中一样: boost::python::ptr or PyInstance_New usage

我想这意味着我应该使用boost.Python 来包装我的库?

我唯一的目标是在嵌入式 python 解释器中操作我的 Foo 的 C++ 实例(不确定是否可以使用以前的方法完成)。

希望我清楚,感谢您的帮助。

更新

感谢您的回答。事实上,我已经将我的 Foo 类暴露给了 python (with swig)。

我有什么:

我的 Foo 类:

class Foo{...};

我的包装库(包括 Foo 类)暴露给 python: 所以我可以启动 python 解释器并执行以下操作:

import my_module
foo=my_modulde.Foo()

我想要什么:

有一个 C++ 主程序,它嵌入了一个 python 解释器并操作 C++ 世界变量。

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

现在更清楚了吗? :)

【问题讨论】:

    标签: c++ python boost swig boost-python


    【解决方案1】:

    Boost python 允许您以非常紧密集成的方式将 c++ 类公开给 python - 您甚至可以包装它们,以便您可以从您的 c++ 类派生 python 类,并将虚拟方法解析为 python 覆盖。

    boost python tutorial 是一个很好的起点。


    编辑:

    您可以创建一个 c++ 对象并将对它的引用传递给内部 python 解释器,如下所示:

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

    【讨论】:

    • 感谢您的回答,我也会考虑 boost.Python。我已经知道 boost.Python 公开 C++ 类的能力,但我有很多遗留代码要包装。所以 swig 对我来说似乎是一个更好的解决方案(更快?更简单?和更多语言目标)...... :(我找到了你的一个旧答案here。它仍然晦涩难懂:) 但我觉得这就是我寻找(不仅仅是一个实例,而是全球理念)?我希望用 swig 和 python api 来做到这一点? :(
    • 我已经添加了一个示例,试图用 boost::python 回答您的确切问题 - 恐怕我忍不住痛饮。
    • 非常感谢!这正是我想要的(除了 boost.python)。我已经提到您的答案很有用(实际上非​​常有用:))。我会等待其他回复(尤其是关于 swig 的回复),然后更深入地了解您的解决方案。
    【解决方案2】:

    作为参考,以下是使用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;
    }
    

    【讨论】:

    • 非常感谢您的回答,它非常适合我的情况!
    【解决方案3】:

    我知道这是一个老问题,但这里有一个使用 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"
    

    与运行时一起生成常用的 SWIG 包装器

    swig -python -c++ -Wall foo.i
    swig -python -c++ -Wall -external-runtime runtime.h
    

    生成包含struct Foo 的 SWIG 模块:

    g++ -fPIC -Wall -Wextra -shared -o _module.so foo_wrap.cxx foo.cpp -I/usr/include/python2.7 -lpython2.7
    

    如果您想跨多个模块共享类型信息,可以添加参数-DSWIG_TYPE_TABLE=SomeName

    现在,Foo 的 C++ 实例是如何传递给解释器的

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

    以上可以通过以下方式编译:

    g++ -Wall -Wextra -I/usr/include/python2.7 main.cpp foo.cpp -o main -lpython2.7
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多