用C++扩展Python的功能(一)

来源:互联网 发布:多媒体创作软件 编辑:程序博客网 时间:2024/05/16 10:04


一. 说明


1. 例如我们可以用Python写一个功能包(包里就只有一个py文件, py文件就只有一个函数, 例如HYAdd(a, b)), 因为是同一种语言, 所以很简单.
2. 现在相当于使用C++实现一遍, 但是C++和Python是异构语言, 所以C++需要按照Python的扩展框架, 实现其规定的接口从而扩展Python的功能.
3. 在Windows下, 载体肯定是使用DLL了.
4. 开发环境: VS2008 + Python27


二. 使用C++实现一个Python库: HYPyLib


参考: https://docs.python.org/2/extending/extending.html#writing-extensions-in-c


2.1 HYPyLib的功能和用法


HYPyLib中有函数HYAdd(nA, nB)和函数HYShow(strShow)
使用:

import HYPyLibHYPyLib.HYAdd(2, 9)HYPyLib.HYShow("Hello, World!")

三. 实现步骤


3.1 创建Win32 DLL, 工程名为HYPyLib


3.2 添加Python的h文件盒lib文件路径到DLL工程工程


(右键属性: ... 你懂的)
我的环境是D:\Python27\include和D:\Python27\libs


3.3 在HYPyLib实现如下代码


// HYPyLib.cpp : 定义 DLL 应用程序的导出函数。//#include "stdafx.h"#include <Python.h>#include <iostream>// 对应的C++函数static PyObject* HYCPPShow(PyObject* self, PyObject* args){const char* pStr;// 取出Python传进来的参数if(!PyArg_ParseTuple(args, "s", &pStr)){return 0;}/////////////////////////////////////////////////////////////std::cout << pStr << std::endl;// 输出Py_INCREF(Py_None);return Py_None;}// 对应的C++函数static PyObject* HYCPPAdd(PyObject* self, PyObject* args){int nA = 0;int nB = 0;// 取出Python传进来的参数if(0 == PyArg_ParseTuple(args,"i|i", &nA, &nB)){return 0;}int nResult = nA + nB;// 计算return Py_BuildValue("i", nResult);}// Python函数与C++函数的映射表static PyMethodDef HYPyLibMethods[] = {{"HYAdd", HYCPPAdd, METH_VARARGS, "Add two number."},{"HYShow", HYCPPShow, METH_VARARGS, "Show the input string."},{0, 0, 0, 0}};// 初始化函数PyMODINIT_FUNC initHYPyLib(){Py_InitModule("HYPyLib", HYPyLibMethods);}

3.4 编译


3.4.1 报错误


A. 编译的时候就报无法找到python27_d.lib
B. 连接的时候就报连接错误: unresolved external symbol __imp__Py_InitModule4TraceRefs ...
问题A是因为D:\Python27\libs目录没有python27_d.lib

(如果使用Release编译, 则可以顺利编译生成DLL)


3.4.2 解决方法


解决问题A: 拷贝一份python27.lib, 命名为python27_d.lib

解决问题B: 修改文件 D:\Python27\includeobject.h
修改
#if defined(Py_DEBUG) && !defined(Py_TRACE_REFS)
#define Py_TRACE_REFS
#endif

#if defined(Py_DEBUG) && !defined(Py_TRACE_REFS)
// #define Py_TRACE_REFS
#endif

参考: http://blog.csdn.net/ren911/article/details/6441261


3.5 发布


把编译得到HYPyLib.dll命名为HYPyLib.pdy, 并拷贝到D:\Python27\DLLs


3.6 使用


现在Python可以使用这个库了
>>> import HYPyLib
>>> HYPyLib.HYAdd(4, 8)
12
>>> HYPyLib.HYShow('Hello, world!')
Hello, world!
>>>


四. 函数说明


4.1 initHYPyLib函数


// 初始化函数PyMODINIT_FUNC initHYPyLib(){Py_InitModule("HYPyLib", HYPyLibMethods);}


1. 初始化函数, 没有参数, 无返回值
2. define PyMODINIT_FUNC extern "C" __declspec(dllexport) void
PyMODINIT_FUNC的定义相当于定义为导出函数, 供Python虚拟机调用.
3. 这个函数的命名必须是initXXX, 其中XXX与DLL的文件名相同.
很容易理解的就是Python导入模块式import XXX, 通过XXX就可以找到initXXX函数.
4. Py_InitModule, 相当于把Python函数与C++函数映射起来, 通过映射表HYPyLibMethods
5. initXXX除了映射之外, 还可以做一些其他事情, 看你的需求.


4.2 映射表


// Python函数与C++函数的映射表static PyMethodDef HYPyLibMethods[] = {{"HYAdd", HYCPPAdd, METH_VARARGS, "Add two number."},{"HYShow", HYCPPShow, METH_VARARGS, "Show the input string."},{0, 0, 0, 0}};


1. 从表项就知道各项的用途:{"HYAdd"(Python函数的名称), HYCPPAdd(对应C++的函数名), METH_VARARGS(函数的参数说明), "Add two number."(函数描述)}
2. 以一个空项结束: {0, 0, 0, 0}


4.3 C++的映射函数


static PyObject* HYCPPShow(PyObject* self, PyObject* args){const char* pStr;if(!PyArg_ParseTuple(args, "s", &pStr)){return 0;}std::cout << pStr << std::endl;// 输出Py_INCREF(Py_None);return Py_None;}static PyObject* HYCPPAdd(PyObject* self, PyObject* args){int nA = 0;int nB = 0;int nResult = 0;if(0 == PyArg_ParseTuple(args,"i|i", &nA, &nB)){return 0;}nResult = nA + nB;// 计算return Py_BuildValue("i", nResult);} 


1. C++的映射函数就是固定的格式: static PyObject* HYCPPShow(PyObject* self, PyObject* args)
有两个参数(PyObject* self, PyObject* args)和返回PyObject*
2. 函数体的实现应该有解析参数, 实现, 生成返回值这三个步骤.
3. 对于一些复杂的数据结构应该也支持(暂时没试过).
4. 看C++函数 HYCPPShow, HYShow是没有返回值的, 所以HYCPPShow需要这样子构造返回值,(表示没有返回值)
 Py_INCREF(Py_None);
 return Py_None;


4.4 PyArg_ParseTuple函数和Py_BuildValue函数


看: https://docs.python.org/2/c-api/arg.html?highlight=pyarg_parsetuple#c.PyArg_ParseTuple

Py_BuildValue("")                        NonePy_BuildValue("i", 123)                  123Py_BuildValue("iii", 123, 456, 789)      (123, 456, 789)Py_BuildValue("s", "hello")              'hello'Py_BuildValue("ss", "hello", "world")    ('hello', 'world')Py_BuildValue("s#", "hello", 4)          'hell'Py_BuildValue("()")                      ()Py_BuildValue("(i)", 123)                (123,)Py_BuildValue("(ii)", 123, 456)          (123, 456)Py_BuildValue("(i,i)", 123, 456)         (123, 456)Py_BuildValue("[i,i]", 123, 456)         [123, 456]Py_BuildValue("{s:i,s:i}",              "abc", 123, "def", 456)    {'abc': 123, 'def': 456}Py_BuildValue("((ii)(ii)) (ii)",              1, 2, 3, 4, 5, 6)          (((1, 2), (3, 4)), (5, 6))

五. 小结


1. 爽.
2. 这里只是最简单的实现, 当需要实现一些复杂的需求时, 需要注意的地方有很多, 例如传递复杂的数据结构等
参考:
https://docs.python.org/2/extending/extending.html#writing-extensions-in-c
3. 有些点还是不明白, 例如C++类与Python类又如何映射
参考:
http://bbs.chinaunix.net/thread-1593255-1-1.html



0 0