您的位置:首页 > 编程语言 > Python开发

让主程序可以通过python脚本扩展功能及boost.python的使用

2006-05-25 20:23 1031 查看
/**
/file FILENAME
/mainpage 让主程序可以通过python脚本扩展功能及boost.python的使用

/brief 本文的内容是:在主程序中嵌入一种脚本语言:python,并且可以通过写python脚本扩展程序功能。
/author 刘凯 mslk.sa@gmail.com
/date 20060525

一般情况,为python写个扩展模块(即python脚本中调用扩展模块的功能),或者主程序中调用python解释器执行脚本,
都是单向的。
但要想让python脚本做为主程序的扩展,就像emacs中写lisp来扩展功能,情况就会复杂一点,
即主程序要调用python解释器执行脚本,脚本执行中,要使用主程序的功能或者修改主程序中对象状态。
如果在使用主程序的功能时,主程序又要访问修改当前执行脚本中的python对象的状态或功能,情况会更加复杂。就是必须有双向的通信关系。

如果程序中提供有脚本扩展的能力,好处还是非常大,某些功能用户可以自己实现,
比如有程序中生成了很多对象,都有名字和一些属性,用户可以写段脚本按名字分类整理计算(有了新公式对数据进行处理)输出数据等等。
某些性能无关的功能,用脚本实现真是很方便。

其实嵌入任何脚本都是不错的,脚本语言本身都很简单,但boost.python实在简化了大量大量的工作,所以就选python做扩展,简单嘛。
这里把我的一点经验说一下,
首先,要搞清楚,想从python脚本中访问的功能,就必须做成python的扩展模块(用BOOST_PYTHON_MODULE)
其次,模块中要导出主程序中的根对象和其功能(boost::python::class_),通过根对象可以逐渐访问主程序中其它需要的对象(也要导出),
这也包括需要的一些继承关系为多态(boost::python::wrapper, boost::python::bases)和全局函数(boost::python::def)。

注意:
1.可以建多个扩展模块,有继承关系的类,不能放到不同的模块中,否则子类就不能在脚本中使用了。
2.对于任何模块,如:BOOST_PYTHON_MODULE(PowerApp),要在“Py_Initialize();”调用之后
调一下initPowerApp()(模块名前加init)这个函数,最好也调一下”PyRun_SimpleString("import PowerApp/n");“。
3.def的函数如果有返回值,可能就需要bp::return_value_policy<>,把这个相关的文档先看看
4.新建的mfc项目要把:[项目属性 >> c/c++ >> 语言] 中的[启用运行时类型信息]打开,
[项目属性 >> c/c++ >> 代码生成]中的[运行时库]要选 ”多线程[调试] DLL“

具体用法很简单看boost.python文档,我英文半级的水平都可以看。

为了快速建立信心,这里举个小例子:假定都有这行代码:namespace bp = boost::python
先决定把程序中那个对象作为根来导出,这里根对象的意思是其它对象都可以通过根的属性、函数等直接或间接的访问到,当然可以导出多个根。
如果无法决定,可以先把CWinApp的子类(假设为:CPowerApp)作为根好了,就是AfxGetApp()得到的static_cast一下。
我们在PowerApp模块中提供函数来访问得到它的引用。

主程序中所有的类,用类似的方法均可导出到扩展模块中,更具体的细节:异常的处理,复杂的数据,参考boost.python文档都没什么问题。
*/
# include "stdafx.h"
# include <cassert>
# include <boost/python.hpp>
# ifdef _DEBUG
# pragma comment(lib, "boost_python-vc71-mt-gd-1_33_1")
# else
# pragma comment(lib, "boost_python-vc71-mt-1_33_1")
# endif

# include "power.h"
CPowerApp *GetApp()
{
assert(dynamic_cast<CPowerApp*>(AfxGetApp()));
return static_cast<CPowerApp*>(AfxGetApp());
}

# include <string>
/// 演示用的父类
class IPeople
{
std::string _name;
public:
virtual void msg_box() = 0;
std::string const& get_name();
void set_name(std::string const&);
};

/// 演示用的子类
class ApplicationAuthor : public IPeople
{
std::wstring _content; // 对程序的一下说明
public:
void msg_box();
void set_content(std::wstring const&);
};

/// 伪造一个CPowerApp的成员函数
/**
如果改为成员函数,需要在CPowerApp加一个成员变量和函数
<power.h>
-# ApplicationAuthor _author;
-# ApplicationAuthor &GetAuthor();

<power.cpp>
-# ApplicationAuthor &GetAuthor() {return _author;} // 不要inline,取不到地址了

修改”关于“对话框显示:_author.get_name() 和 _author.get_content()这两个成员的内容
*/
ApplicationAuthor& CPowerApp_GetAuthor(CPowerApp *pApp)
{
static ApplicationAuthor suppose_PowerApp_memeber; // 假定为CPowerApp的成员变量
return suppose_PowerApp_memeber;
}

namespace bp = boost::python;
BOOST_PYTHON_MODULE(PowerApp)
{
bp::def("GetApp", &GetApp, bp::return_value_policy<bp::reference_existing_object>());

bp::class_<CPowerApp, boost::noncopyable>("App")
.def("About", &CPowerApp::OnAppAbout)
.def("GetAuthor", &CPowerApp_GetAuthor, bp::return_value_policy<bp::reference_existing_object>())
;
}

/// 对于抽象类,需要包装,把纯虚函数处理一下,具体参考文档
struct IPeopleWrapper : public IPeople, public bp::wrapper<IPeople>
{
void msg_box() {this->get_override("msg_box")();}
};

BOOST_PYTHON_MODULE(DataManager)
{
bp::class_<IPeopleWrapper, boost::noncopyable>("IPeople")
.def("set_name", &IPeople::set_name)
.def("get_name", &IPeople::get_name, bp::return_internal_reference<>())
.def("msg_box", &IPeople::msg_box)
;
bp::class_<ApplicationAuthor, bp::bases<IPeople> >("ApplicationAuthor")
.def("set_content", &ApplicationAuthor::set_content)
;
}

std::string const& IPeople::get_name() {return _name;}
void IPeople::set_name(std::string const& v) {_name = v;}
void ApplicationAuthor::set_content(std::wstring const& c) {_content = c;}
void ApplicationAuthor::msg_box() {MessageBoxW(NULL, _content.data(), L"PowerApp good! Content:", MB_OK);
MessageBoxA(NULL, get_name().data(), "IPeople::name", MB_OK);}

/**
遗留的问题:
-# .def的函数,std::string、wstring作为函数的返回值有问题,但作为函数的参数可以

*/

/// CPowerApp构造函数调用这个函数
void InitPy()
{
Py_Initialize();

PyRun_SimpleString("import wx/n");

initPowerApp();
PyRun_SimpleString("import PowerApp/n");

/// 模块太多的话,就需要简化一下了,定义一个宏,把模块名传入宏
# define INIT_PM(v) init##v##();PyRun_SimpleString("import " #v "/n");

INIT_PM(DataManager);

# undef INIT_PM
}

/// CPowerApp的析构调用这个函数:Py_Finalize()

/// 最后一下,加个菜单调一下这个函数,
void pyShell()
{
PyRun_SimpleString("from PyShell import main/nmain()/n");
}

/** 使用指南
要同时安装好:
python24(http://www.python.org)
wxPython(http://www.wxpython.org)
编译好:boost.python(http://www.boost.org)
编译方法很简单,参考文章http://blog.csdn.net/mslk/archive/2005/11/08/525278.aspx的最后说明。
bjam --with-python-root=python24主目录路径

将PowerApp.exe,boost_python* 放入python24的主目录,执行调出pyShell
dir (PowerApp)
dir (DataManager)
app = PowerApp.GetApp()
app.About()
a = app.GetAuthor()
a.msg_box()
a.set_name("LiuKai mslk.sa@gmail.com")
a.set_content("boost.python Very Good!")
a.msg_box()
app.About()
……………………
*/

/** 菜鸟指南:
打开vs2003,[文件 >> 新建 >> 项目]
选 [mfc >> mfc 应用程序],名称:Power,完成

[工具 >> 选项 >> 项目 >> vc++目录]
配置[包含文件]添加boost,和python24/include
配置[库文件]添加boost/bin和python24/libs

把这篇文章保存到一个cpp后缀的文件,加入项目中
找到CPowerApp::CPowerApp函数,添加函数调用
InitPy();
CPowerApp构造函数前面加上声明:
void InitPy();
void pyShell();

打开资源,添加一个菜单名:pyShell,消息处理函数选为CPowerApp的成员,
消息处理函数内调用:
pyShell();

再强调一下:
[项目属性 >> c/c++ >> 语言] 中的[启用运行时类型信息]打开,
[项目属性 >> c/c++ >> 代码生成]中的[运行时库]要选 ”多线程[调试] DLL“

编译后,将Power.exe拷入Python24的主目录,执行后点菜单,弹出来pyShell,
按照使用指南键入命令,即可看到效果。

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: