Python2和Python3的差別很大,這是爲何不少人都不肯意升級的緣由。若是你用C/C++爲Python2寫過擴展模塊,那麼直接用Python3來編譯是通不過的。這篇文章分享下如何編寫兼容的C/C++代碼。html
pip install opencv-python
pip install numpy
SET VS90COMNTOOLS=%VS140COMNTOOLS%
Python官方有一篇文章Porting Extension Modules to Python 3,詳細介紹了Python2和Python3的差別。閱讀完文章你就能夠寫出最簡單的C/C++兼容代碼。python
#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 }
這裏分享下我碰到的接口變化問題。git
Python2用的接口是PyString_FromString,而Python3用的是PyUnicode_FromFormat。github
#if defined(IS_PY3K) result = PyUnicode_FromFormat("%s", tmp->pBarcodeData); #else result = PyString_FromString(tmp->pBarcodeData); #endif
這塊部分網上的信息比較少,我經過源碼找到了方法。windows
#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:app
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])