将C++类实例暴露给Python嵌入式解释器
我想找一个简单的方法,把一个C++类的实例暴露给一个嵌入的Python解释器。
- 我有一个C++库。这个库已经被包装过(目前是用swig),我可以在Python解释器中使用它。
- 我有一个C++主程序,它实例化了我库中的一个Foo类,并嵌入了一个Python解释器。
我希望把我C++中的Foo实例暴露给Python世界(在Python中能看到它是一个Foo类)。
这可能吗?如果可以,怎么做?
我觉得这有点像第一个答案中的内容: boost::python::ptr或PyInstance_New的用法
我猜这意味着我应该用boost.Python
来包装我的库吗?
我的唯一目标是在嵌入的Python解释器中操作我C++的Foo实例(不确定之前的方法是否能做到这一点)。
实际上,我已经把我的Foo类暴露给Python了(用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;
}
4 个回答
我知道这个问题已经很久了,但这里有一个使用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
。
现在,下面是如何将C++中的Foo
实例传递给解释器:
#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
这里有一个例子,教你如何使用 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;
}
Boost python 是一个工具,它可以让你把 C++ 的类很方便地用在 Python 里。也就是说,你可以把 C++ 写的类包装起来,这样你就可以在 Python 中从这些 C++ 类继承,甚至可以让 Python 中的方法覆盖 C++ 中的虚拟方法。
boost python 教程 是一个很好的入门资源。
编辑:
你可以创建一个 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;
}