python对c兼容_如何让C/C++代码同时兼容Python2和Python3

Python2和Python3的差异很大,这是为什么很多人都不愿意升级的原因。如果你用C/C++为Python2写过扩展模块,那么直接用Python3来编译是通不过的。这篇文章分享下如何编写兼容的C/C++代码。

环境搭建

OpenCV 3.3.0

pip install opencv-python

Numpy 1.11.2

pip install numpy

Visual Studio 2015

SET VS90COMNTOOLS=%VS140COMNTOOLS%

你需要的C/C++库。我这里用的是Dynamsoft Barcode Reader 5.2 for Windows。你需要把DLL拷贝到Python35\Lib\site-packages目录下。

模块初始化

Python官方有一篇文章Porting Extension Modules to Python 3,详细介绍了Python2和Python3的差异。阅读完文章你就可以写出最简单的C/C++兼容代码。

#include "Python.h"

struct module_state {

PyObject *error;

};

#if PY_MAJOR_VERSION >= 3

#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))

#else

#define GETSTATE(m) (&_state)

static struct module_state _state;

#endif

static PyObject *

error_out(PyObject *m) {

struct module_state *st = GETSTATE(m);

PyErr_SetString(st->error, "something bad happened");

return NULL;

}

static PyMethodDef myextension_methods[] = {

{"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},

{NULL, NULL}

};

#if PY_MAJOR_VERSION >= 3

static int myextension_traverse(PyObject *m, visitproc visit, void *arg) {

Py_VISIT(GETSTATE(m)->error);

return 0;

}

static int myextension_clear(PyObject *m) {

Py_CLEAR(GETSTATE(m)->error);

return 0;

}

static struct PyModuleDef moduledef = {

PyModuleDef_HEAD_INIT,

"myextension",

NULL,

sizeof(struct module_state),

myextension_methods,

NULL,

myextension_traverse,

myextension_clear,

NULL

};

#define INITERROR return NULL

PyMODINIT_FUNC

PyInit_myextension(void)

#else

#define INITERROR return

void

initmyextension(void)

#endif

{

#if PY_MAJOR_VERSION >= 3

PyObject *module = PyModule_Create(&moduledef);

#else

PyObject *module = Py_InitModule("myextension", myextension_methods);

#endif

if (module == NULL)

INITERROR;

struct module_state *st = GETSTATE(module);

st->error = PyErr_NewException("myextension.Error", NULL, NULL);

if (st->error == NULL) {

Py_DECREF(module);

INITERROR;

}

#if PY_MAJOR_VERSION >= 3

return module;

#endif

}

Python3接口的使用

这里分享下我碰到的接口变化问题。

String

Python2用的接口是PyString_FromString,而Python3用的是PyUnicode_FromFormat。

#if defined(IS_PY3K)

result = PyUnicode_FromFormat("%s", tmp->pBarcodeData);

#else

result = PyString_FromString(tmp->pBarcodeData);

#endif

NumPy相关数据获取:buffer, width, height, stride

这块部分网上的信息比较少,我通过源码找到了方法。

#if defined(IS_PY3K)

//Refer to numpy/core/src/multiarray/ctors.c

Py_buffer *view;

int nd;

PyObject *memoryview = PyMemoryView_FromObject(o);

if (memoryview == NULL) {

PyErr_Clear();

return -1;

}

view = PyMemoryView_GET_BUFFER(memoryview);

char *buffer = (char*)view->buf;

nd = view->ndim;

int len = view->len;

int stride = view->strides[0];

int width = view->strides[0] / view->strides[1];

int height = len / stride;

#else

PyObject *ao = PyObject_GetAttrString(o, "__array_struct__");

if ((ao == NULL) || !PyCObject_Check(ao)) {

PyErr_SetString(PyExc_TypeError, "object does not have array interface");

return NULL;

}

PyArrayInterface *pai = (PyArrayInterface*)PyCObject_AsVoidPtr(ao);

if (pai->two != 2) {

PyErr_SetString(PyExc_TypeError, "object does not have array interface");

Py_DECREF(ao);

return NULL;

}

编译

在setup.py中通过Python版本来区分2和3:

from distutils.core import setup, Extension

import sys

dbr_include_dir = 'e:\\Program Files (x86)\\Dynamsoft\\Barcode Reader 5.2\\Components\\C_C++\\Include'

dbr_lib_dir = 'e:\\Program Files (x86)\\Dynamsoft\Barcode Reader 5.2\\Components\\C_C++\\Lib'

numpy_include_dir = None

if sys.version_info[0] == 2 and sys.version_info[1] == 7:

numpy_include_dir = "F:\\Python27\\Lib\\site-packages\\numpy-1.11.2-py2.7-win32.egg\\numpy\\core\\include\\numpy"

else:

numpy_include_dir = "F:\\Python35\\Lib\\site-packages\\numpy-1.11.2-py3.5-win32.egg\\numpy\\core\\include\\numpy"

module_dbr = Extension('dbr', sources=['dbr.c'], include_dirs=[

numpy_include_dir, dbr_include_dir], library_dirs=[dbr_lib_dir], libraries=['DBRx86'])

setup(name='DynamsoftBarcodeReader',

version='1.0',

description='Python barcode extension',

ext_modules=[module_dbr])

源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值