关于C++和Python之间互相调用的问题,可以查找到很多资料。本文将主要从解决实际问题的角度看如何构建一个Python和C++混合系统。
力为 2009
一、概念
混合系统:采用多种语言构建的系统。比如Native C++和dotNet的混合系统,Python/Lua和C++的混合系统。
Python和C++各有各的优缺点,构建两者混合系统的主要目的就是利用Python的灵活性和C++高效性,增加程序的扩展性。当然还有其他好处,此处就不展开谈了。脚本语言有很多,至于为什么不采用其他的(如Lua、Ruby等),这里也不考虑。脚本语言的采用主要是个人喜好的问题。采用Lua可以查到很多资料,以前也曾用LuaBind实现过。
二、系统架构
混合系统可以简化成如下模型:
系统的运行过程为:
首先在应用程序中嵌入脚本语言解析器,然后用脚本语言提供的C/C++ API封装已有的C++功能库,再用脚本调用封装的服务。此后,应用层便可以执行脚本里的内容,其效果与应用层直接调用服务层类似。
三、实现
本例中采用脚本语言Python,因此主要工作为在应用层嵌入Python解析器,用Python封装服务层。为了说明问题,应用层的实现为PythonConsole.ex,服务层的实现为Mythma.dll,封装层为MythmaPy.dll。
1、假定Mythma.dll中的一个类为CHelloWorld:
{
public:
CHelloWorld(void);
~CHelloWorld(void);
void SetId(int nId) { m_Id = nId;}
int GetId() const { return m_Id; }
private:
int m_Id;
} ;
2、现在用Boost.Python封装该类:
#include < boost / python.hpp >
using namespace boost::python;
BOOST_PYTHON_MODULE(MythmaPy)
{
class_<CHelloWorld>("CHelloWorld")
.def("SetId", &CHelloWorld::SetId)
.def("GetId", &CHelloWorld::GetId)
;
}
3、在Python脚本中调用该封装
from MythmaPy import *
world = CHelloWorld()
world.SetId( 100 )
print world.GetId()
4、在PythonConsole.exe中嵌入Python解析器
{
// Initialize the interpreter
Py_Initialize();
if (python::handle_exception(exec_mythma))
{
if (PyErr_Occurred())
{
BOOST_ERROR("Python Error detected");
PyErr_Print();
}
else
{
BOOST_ERROR("A C++ exception was thrown for which "
"there was no exception translator registered.");
}
}
char ch;
std::cin >> ch;
// Boost.Python doesn't support Py_Finalize yet, so don't call it!
return boost::report_errors();
}
在PythonConsole中调用Python脚本
{
std::cout << "exec extension module Mythma" << std::endl;
python::dict global;
python::object result = python::exec_file(
".//axxscript.py", global, global);
python::object world = global["world"];
CHelloWorld& py = python::extract<CHelloWorld&>(world) BOOST_EXTRACT_WORKAROUND;
std::cout << py.GetId() << std::endl;
std::cout << "success!" << std::endl;
}
四、结果分析
到此为止,一切感觉都很良好。但假如现在就运行程序,为得到如下的错误:
假如直接用Python.exe运行上面的Python脚本,并不会产生错误,切运行结果正确。这是什么原因引起的呢?
从错误提示可以看出加载MythmaPy模块失败。这是因为使用Boost.Python需要注意一个问题,即在执行脚本之前,需要用init<ModuleName>注册封装模块。
加上次限制后,我们就需要对上面的架构做些调整,MythmaPy.dll需要暴露接口供PythonConsole.exe在恰当的时机调用注册该模块。
五、小结
搭建混合系统很简单,麻烦的地方在于API的封装。即使采用dotNet封装Native C++也是麻烦在这个地方。如果采用一种统一的API描述方法,使用工具自动生成封装,这样就可以大大简化封装的复杂度,若可以按需生成不同语言的封装,那更是锦上添花了。
本例的搭建环境为VS2008,Python2.5,Boost1.38。完整工程下载在这里。
六、广告