cython 包装DLL:从C ++到Cython到Python
示例
这展示了一个用Cython包装C++dll的简单例子。它将涵盖以下主要步骤:
使用VisualStudio使用C++创建示例DLL。
用Cython包裹DLL,以便可以在Python中调用它。
假定您已安装Cython,并可以在Python中成功导入它。
对于DLL步骤,还假定您熟悉在VisualStudio中创建DLL。
完整的示例包括以下文件的创建:
complexFunLib.h:C++DLL源的头文件
complexFunLib.cpp:C++DLL源的CPP文件
ccomplexFunLib.pxd:Cython“头文件”
complexFunLib.pyx:Cython“包装器”文件
setup.py:complexFunLib.pyd使用Cython创建的Python安装文件
run.py:导入已编译的Cython包装的DLL的示例Python文件
C++DLL来源:complexFunLib.h和complexFunLib.cpp
如果您已经有了DLL和标头源文件,请跳过此步骤。首先,我们创建C++源,使用VisualStudio从中编译DLL。在这种情况下,我们要使用复杂的指数函数进行快速数组计算。以下两个函数对数组and进行计算,结果存储在中。有两种功能可同时满足单精度和双精度。请注意,这些示例函数使用OpenMP,因此请确保在项目的VisualStudio选项中启用了OpenMP。k*exp(ee)keeres
H档
// Avoids C++ name mangling with extern "C"
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
#include <complex>
#include <stdlib.h>
//处理64位复数,即两个32位(4字节)浮点数
EXTERN_DLL_EXPORT void mp_mlt_exp_c4(std::complex<float>* k,
std::complex<float>* ee,
int sz,
std::complex<float>* res,
int threads);
//处理128位复数,即两个64位(8字节)浮点数
EXTERN_DLL_EXPORT void mp_mlt_exp_c8(std::complex<double>* k, std::complex<double>* ee,
int sz,
std::complex<double>* res,
int threads);CPP文件
#include "stdafx.h"
#include <stdio.h>
#include <omp.h>
#include "complexFunLib.h"
void mp_mlt_exp_c4(std::complex<float>* k,
std::complex<float>* ee,
int sz,
std::complex<float>* res,
int threads)
{
//使用OpenMP并行指令进行多处理
#pragmaomp并行num_threads(线程)
{
#编译指示
for (int i = 0; i < sz; i++) res[i] = k[i] * exp(ee[i]);
}
}
void mp_mlt_exp_c8(std::complex<double>* k,
std::complex<double>* ee,
int sz, std::complex<double>* res,
int threads)
{
//使用OpenMP并行指令进行多处理
#pragmaomp并行num_threads(线程)
{
#编译指示
for (int i = 0; i < sz; i++) res[i] = k[i] * exp(ee[i]);
}
}Cython来源:ccomplexFunLib.pxd和complexFunLib.pyx
接下来,我们创建包装C++DLL所需的Cython源文件。在此步骤中,我们进行以下假设:
您已经安装了Cython
您拥有一个有效的DLL,例如上述的DLL
最终目标是创建将这些Cython源文件与原始DLL结合使用,以编译.pyd可作为Python模块导入并公开用C++编写的函数的文件。
PXD文件
该文件对应于C++头文件。在大多数情况下,您可以通过Cython特定的较小更改将标头复制粘贴到此文件中。在这种情况下,使用了特定的Cython复合体类型。请注意c在开头添加ccomplexFunLib.pxd。这不是必需的,但是我们发现这样的命名约定有助于维护组织。
cdef extern from "complexFunLib.h":
void mp_mlt_exp_c4(float complex* k, float complex* ee, int sz,
float complex* res, int threads);
void mp_mlt_exp_c8(double complex* k, double complex* ee, int sz,
double complex* res, int threads);PYX文件
该文件对应于C++cpp源文件。在此示例中,我们将指向Numpyndarray对象的指针传递给导入DLL函数。也可以将内置的Cythonmemoryview对象用于数组,但是其性能可能不如ndarray对象(但是语法明显更清晰)。
cimport ccomplexFunLib # Import the pxd "header"
#对于Numpy导入,请注意,C导入大多数在Python导入之后
import numpy as np #导入PythonNumpy
cimport numpy as np #导入C块
#从Python和Cstdlib导入一些功能
fromcpython.pycapsulecimport *
#Python包装函数。
#请注意,可以在签名中添加类型
def mp_exp_c4(np.ndarray[np.complex64_t, ndim=1] k,
np.ndarray[np.complex64_t, ndim=1] ee,
int sz,
np.ndarray[np.complex64_t, ndim=1] res,
int threads):
'''
TODO: Python docstring
'''
#在参数上调用导入的DLL函数。
#注意,我们正在传递一个指向每个数组中第一个元素的指针
ccomplexFunLib.mp_mlt_exp_c4(&k[0], &ee[0], sz, &res[0], threads)
def mp_exp_c8(np.ndarray[np.complex128_t, ndim=1] k,
np.ndarray[np.complex128_t, ndim=1] ee,
int sz,
np.ndarray[np.complex128_t, ndim=1] res,
int threads):
'''
TODO: Python docstring
'''
ccomplexFunLib.mp_mlt_exp_c8(&k[0], &ee[0], sz, &res[0], threads)Python来源:setup.py和run.py
setup.py
该文件是执行Cython编译的Python文件。其目的是生成编译后的.pyd文件,然后可以由Python模块将其导入。在这个例子中,我们一直所需的所有文件(即complexFunLib.h,complexFunLib.dll,ccomplexFunLib.pxd和complexFunLib.pyx)在同一目录中setup.py。
创建此文件后,应使用以下参数从命令行运行该文件:build_ext--inplace
一旦执行了该文件,它应产生一个.pyd文件而不会引起任何错误。请注意,在某些情况下,如果有错误,.pyd可能会创建,但无效。setup.py使用生成的之前,请确保在执行时不会抛出任何错误.pyd。
fromdistutils.coreimport setup
fromdistutils.extensionimport Extension
fromCython.Distutilsimport build_ext
import numpy as np
ext_modules = [
Extension('complexFunLib',
['complexFunLib.pyx'],
#请注意,此处指定了C++语言
#默认语言是C
language="c++",
libraries=['complexFunLib'],
library_dirs=['.'])
]
setup(
name = 'complexFunLib',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules,
include_dirs=[np.get_include()] #这将获取所有必需的Numpy核心文件
)运行
现在complexFunLib可以直接导入到Python模块中,并调用包装的DLL函数。
import complexFunLib import numpy as np #创建要求幂的非平凡复数数组, #即res=k*exp(ee) k = np.ones(int(2.5e5), dtype='complex64')*1.1234 + np.complex64(1.1234j) ee = np.ones(int(2.5e5), dtype='complex64')*1.1234 + np.complex64(1.1234j) sz =k.size #获取大小整数 res = np.zeros(int(2.5e5), dtype='complex64') #创建结果数组 #通话功能 complexFunLib.mp_exp_c4(k, ee, sz, res, 8) #打印结果 print(res)