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

原创 2006年05月25日 20:23:00
/**
/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,
按照使用指南键入命令,即可看到效果。

*/
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

arcgis中Python脚本的使用

  • 2015-04-28 12:21
  • 1.08MB
  • 下载

写一个python脚本,实现简单的http服务器功能:

原文链接:http://blog.csdn.net/ordeder/article/details/22490373 写一个python脚本,实现简单的http服务器功能: 1.浏览器中输入网站地...

怎样编写python脚本的C扩展模块

引用 http://blog.csdn.net/oldsmith/article/details/3628282 /*demo.c   module spam*/ #include stati...

使用PyInstaller2将Python脚本转化为可执行文件

使用PyInstaller2将Python脚本转化为可执行文件 最近使用Python为项目开发一款测试工具。因为使用者在另一个部门,领导希望能把Python脚本发布为脱离Python平台运行的可...

arcgis中Python脚本的使用

  • 2013-09-20 23:33
  • 1.08MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)