C++ 扩展和嵌入 Python

C++ 扩展和嵌入 Python

本人很久之前发在vckbase的文章,居然没有显示作者。

Python简介

Python是一种简单易学,功能强大的解释型编程语言,它有简洁明了的语法,高效率的高层数据结构,能够简单而有效地实现面向对象编程,特别适用于快速应用程序开发,也可以用来开发大规模的重要的商业应用。Python是一个理想的脚本语言。

Python免费开源,可移植到多种操作系统,只要避免使用依赖于特定操作系统的特性,Python程序无需修改就可以在各种平台上面运行。

Python拥有现代编程语言所具有的一切强大功能,Python标准库十分庞大,可以帮助开发者处理各种工作,如:图形用户界面、文件处理、多媒体、正则表达式、文档生成、单元测试、线程、数据库、网络通讯、网页浏览器、CGI、FTP、电子邮件、XML、HTML、WAV文件、密码系统、Tk和其他与系统有关的操作。只要安装了Python,这些功能都是可用的除了标准库以外,还有许多其他高质量的库,如wxPython、Twisted和Python图形库等等数不胜数。

Python容易扩展和嵌入。Python提供的许多标准模块支持C或者C++接口。Python和C可以一起工作,它可以嵌入到C或者C++的应用程序当中,因此可用Python语言为应用程序提供脚本接口,由于支持跨语言开发,可用Python设计概念化应用程序,并逐步移植到C,使用前不必用C重写应用程序。(Jython使Python可以和Java一起工作,使开发者可以在Python里面调Java的包,也可以在Java里面使用Python的对象。还有更妙的,由于Jython的解释器完全用Java编写,因此可以在支持Java的任何平台上部署Python程序,甚至WEB浏览器也可以直接运行Python脚本。)

提出问题

在某个C++应用程序中,我们用一组插件来实现一些具有统一接口的功能,我们使用Python来代替动态链接库形式的插件,这样可以方便地根据需求的变化改写脚本代码,而不是必须重新编译链接二进制的动态链接库。Python强大的功能足以胜任,但是有一些操作系统特定的功能需要用C++来实现,再由Python调用。所以,最基础地,我们需要做到:

1. 把Python嵌入到C++应用程序中,在C++程序中调用Python函数和获得变量的值;

2. 用C++为Python编写扩展模块(动态链接库),在Python程序中调用C++开发的扩展功能函数。

常用的Python/C API介绍

下面是例子中用到的几个Python/C API的简要介绍及示例代码。注意,这并不是这些函数的详细介绍,而仅仅是我们所用到的功能简介,更详细内容请参考文档[1]、[2]、[3]、[4]。

打开Microsoft Visual Studio .NET 2003,新建一个控制台程序,#include ,并在main函数里加入示例代码。

//先定义一些变量

1
2
3
char *cstr;
PyObject *pstr, *pmod, *pdict;
PyObject *pfunc, *pargs;

1. void Py_Initialize( )

初始化Python解释器,在C++程序中使用其它Python/C API之前,必须调用此函数,如果调用失败,将产生一个致命的错误。例:

1
Py_Initialize();

2. int PyRun_SimpleString( const char *command)

执行一段Python代码,就好象是在__main__ 函数里面执行一样。例:

1
2
PyRun_SimpleString( "from time import time,ctime\n"
"print ''Today is'',ctime(time())\n" );

3. PyObject* PyImport_ImportModule( char *name)

导入一个Python模块,参数name可以是*.py文件的文件名。相当于Python内建函数__import__()。例:

1
pmod = PyImport_ImportModule( "mymod" ); //mymod.py

4. PyObject* PyModule_GetDict( PyObject *module)

相当于Python模块对象的__dict__ 属性,得到模块名称空间下的字典对象。例:

1
pdict = PyModule_GetDict(pmod);

5. PyObject* PyRun_String( const char *str, int start, PyObject *globals, PyObject *locals)

执行一段Python代码。

1
pstr = PyRun_String( "message" , Py_eval_input, pdict, pdict);

6. int PyArg_Parse( PyObject *args, char *format, ...)

解构Python数据为C的类型,这样C程序中才可以使用Python里的数据。例:

1
2
3
/* convert to C and print it*/
PyArg_Parse(pstr, "s" , &cstr);
printf ( "%s\n" , cstr);

7. PyObject* PyObject_GetAttrString( PyObject *o, char *attr_name)

返回模块对象o中的attr_name 属性或函数,相当于Python中表达式语句:o.attr_name。例:

1
2
/* to call mymod.transform(mymod.message) */
pfunc = PyObject_GetAttrString(pmod, "transform" );

8. PyObject* Py_BuildValue( char *format, ...)

构建一个参数列表,把C类型转换为Python对象,使Python可以使用C类型数据,例:

1
2
cstr= "this is hjs''s test, to uppercase" ;
pargs = Py_BuildValue( "(s)" , cstr);

9. PyEval_CallObject(PyObject* pfunc, PyObject* pargs)

此函数有两个参数,都指向Python对象指针,pfunc是要调用的Python 函数,通常可用PyObject_GetAttrString()获得;pargs是函数的参数列表,通常可用Py_BuildValue()构建。例:

1
2
3
pstr = PyEval_CallObject(pfunc, pargs);
PyArg_Parse(pstr, "s" , &cstr);
printf ( "%s\n" , cstr);

10. void Py_Finalize( )

关闭Python解释器,释放解释器所占用的资源。例:

1
Py_Finalize();

Python2.4环境没有提供调试版本的Python24d.lib,所以上述示例在release模式下编译。编译完成后,把可行文件和附2给出的mymod.py文件放在一起,再点击即可运行。为了简化编程,附3 给出了simplepy.h。这样,调用mymod.transform变成如下形式:

1
2
3
4
5
6
//#include”simplepy.h”
CSimplepy py;
py.ImportModule( "mymod" );
std::string str=py.CallObject( "transform" ,
"this is hjs''s test, to uppercase" );
printf ( "%s\n" , str.c_str());

接下来,我们来用C++为Python编写扩展模块(动态链接库),并在Python程序中调用C++开发的扩展功能函数。生成一个取名为pyUtil的Win32 DLL工程,除了pyUtil.cpp文件以外,从工程中移除所有其它文件,并填入如下的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// pyUtil.cpp
#ifdef PYUTIL_EXPORTS
#define PYUTIL_API __declspec(dllexport)
#else
#define PYUTIL_API __declspec(dllimport)
#endif
 
#include< windows.h >
#include< string >
#include< Python.h >
BOOL APIENTRY DllMain( HANDLE hModule,
                        DWORD  ul_reason_for_call,
                        LPVOID lpReserved
                     ?)
{
     switch (ul_reason_for_call)
     {
     case DLL_PROCESS_ATTACH:
     case DLL_THREAD_ATTACH:
     case DLL_THREAD_DETACH:
     case DLL_PROCESS_DETACH:
         break ;
     }
     return TRUE;
}
std::string Recognise_Img( const std::string url)
{
     //返回结果
     return "从dll中返回的数据... : " +url;
}
static PyObject* Recognise(PyObject *self, PyObject *args)
{
     const char *url;
     std::string sts;
     if (!PyArg_ParseTuple(args, "s" , &url))
         return NULL;
     sts = Recognise_Img(url);
     return Py_BuildValue( "s" , sts.c_str() );
}
static PyMethodDef AllMyMethods[] = {
     { "Recognise" ,  Recognise, METH_VARARGS}, //暴露给Python的函数
     {NULL,      NULL}        /* Sentinel */
};
extern "C" PYUTIL_API void initpyUtil()
{
     PyObject *m, *d;
     m = Py_InitModule( "pyUtil" , AllMyMethods); //初始化本模块,并暴露函数
     d = PyModule_GetDict(m);
}

在Python代码中调用这个动态链接库:

1
2
3
import pyUtil
result = pyUtil.Recognise( "input url of specific data" )
print "the result is: " + result    

用C++为Python写扩展时,如果您愿意使用Boost.Python库的话,开发过程会变得更开心J,要编写一个与上述pyUtil同样功能的动态链接库,只需把文件内容替换为下面的代码。当然,编译需要boost_python.lib支持,运行需要boost_python.dll支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include< string >
#include < boost/python.hpp >
using namespace boost::python;
#pragma comment(lib, "boost_python.lib")
std::string strtmp;
char const * Recognise( const char * url)
{
     strtmp = "从dll中返回的数据... : " ;
     strtmp+=url;
     return strtmp.c_str();
}
BOOST_PYTHON_MODULE(pyUtil)
{
     def( "Recognise" , Recognise);
}

所有示例都在Microsoft Windows XP Professional + Microsoft Visual Studio .NET 2003 + Python2.4环境下测试通过,本文所用的Boost库为1.33版本。

参考资料

[1] Python Documentation Release 2.4.1. 2005.3.30,如果您以默认方式安装了Python2.4,那么该文档的位置在C:\Program Files\Python24\Doc\Python24.chm;

[2] Michael Dawson. Python Programming for the Absolute Beginner. Premier Press. 2003;

[3] Mark Lutz. Programming Python, 2nd Edition. O''Reilly. 2001.3 ;

[4] Mark Hammond, Andy Robinson. Python Programming on Win32. O''Reilly. 2000.1 ;

Python主页:http://www.python.org;

Boost库主面:www.boost.org;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
附1 text.txt
this is test text in text.txt.
附2 mymod.py
import string
message = '' original string ''
message =message+message
msg_error= ""
try :
        text_file = open( "text.txt" , "r" )
        whole_thing = text_file.read()
        print whole_thing
        text_file.close()
except IOError, ( errno , strerror ):
        print "I/O error(%s): %s" % ( errno , strerror )
def transform(input):
     #input = string.replace(input, ''life'', ''Python'')
     return string.upper(input)
def change_msg(nul):  
     global message #如果没有此行,message是函数里头的局部变量
     message= '' string changed ''
     return message
def r_file(nul):
     return whole_thing
def get_msg(nul):
return message
 
附3 simplepy.h
#ifndef _SIMPLEPY_H_
#define _SIMPLEPY_H_
// simplepy.h v1.0
// Purpose: facilities for Embedded Python.
// by hujinshan @2005年9月2日9:13:02
#include
using std::string;
#include
//--------------------------------------------------------------------
// Purpose: ease the job to embed Python into C++ applications
// by hujinshan @2005年9月2日9:13:18
//--------------------------------------------------------------------
class CSimplepy // : private noncopyable
{
public :
     ///constructor
     CSimplepy()
     {
         Py_Initialize();
         pstr=NULL, pmod=NULL, pdict=NULL;
         pfunc=NULL, pargs=NULL;
     }
     ///destructor
     virtual ~CSimplepy()   
     {  
         Py_Finalize();
     }
     ///import the user module
     bool ImportModule( const char * mod_name)
     {
         try {
             pmod  = PyImport_ImportModule( const_cast (mod_name));
             if (pmod==NULL)
                 return false ;
             pdict = PyModule_GetDict(pmod);
         }
         catch (...)
         {
             return false ;
         }
         if (pmod!=NULL && pdict!=NULL)
             return true ;
         else
             return false ;
     }
     ///Executes the Python source code from command in the __main__ module.
     ///If __main__ does not already exist, it is created.
     ///Returns 0 on success or -1 if an exception was raised.
     ///If there was an error, there is no way to get the exception information.
     int Run_SimpleString( const char * str)
     {
         return PyRun_SimpleString( const_cast (str) );
     }
     ///PyRun_String("message", Py_eval_input, pdict, pdict);
     ///Execute Python source code from str in the context specified by the dictionaries globals.
     ///The parameter start specifies the start token that should be used to parse the source code.
     ///Returns the result of executing the code as a Python object, or NULL if an exception was raised.
     string Run_String( const char * str)
     {
         char *cstr;
         pstr  = PyRun_String(str, Py_eval_input, pdict, pdict);
         if (pstr==NULL)
             throw ( "when Run_String, there is an exception was raised by Python environment." );
         PyArg_Parse(pstr, "s" , &cstr);
         return string(cstr);
     }
     ///support olny one parameter for python function, I think it''s just enough.
     string CallObject( const char * func_name, const char * parameter)
     {
         pfunc=NULL;
         pfunc = PyObject_GetAttrString(pmod, const_cast (func_name));
         if (pfunc==NULL)
             throw (string( "do not found in Python module for: " )
+func_name).c_str();
         char * cstr;
         pargs = Py_BuildValue( "(s)" , const_cast (parameter));
         pstr  = PyEval_CallObject(pfunc, pargs);
         if (pstr==NULL)
             throw ( "when PyEval_CallObject, there is an exception was raised by Python environment" );
         PyArg_Parse(pstr, "s" , &cstr);     
         return string(cstr);
     }
     //PyObject *args;
     //args = Py_BuildValue("(si)", label, count);   /* make arg-list */
     //pres = PyEval_CallObject(Handler, args);     
protected :
     PyObject *pstr, *pmod, *pdict;
     PyObject *pfunc, *pargs;
};

#endif // _SIMPLEPY_H_

// end of file

原文地址:http://blog.csdn.net/tohjs/article/details/78703238

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值