这展示了一个用Cython包装C ++ dll的简单例子。它将涵盖以下主要步骤:
使用Visual Studio使用C ++创建示例DLL。
用Cython包裹DLL,以便可以在Python中调用它。
假定您已安装Cython,并可以在Python中成功导入它。
对于DLL步骤,还假定您熟悉在Visual Studio中创建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文件
如果您已经有了DLL和标头源文件,请跳过此步骤。首先,我们创建C ++源,使用Visual Studio从中编译DLL。在这种情况下,我们要使用复杂的指数函数进行快速数组计算。以下两个函数对数组and进行计算,结果存储在中。有两种功能可同时满足单精度和双精度。请注意,这些示例函数使用OpenMP,因此请确保在项目的Visual Studio选项中启用了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) { // 使用Open MP并行指令进行多处理 #pragma omp并行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) { // 使用Open MP并行指令进行多处理 #pragma omp并行num_threads(线程) { #编译指示 for (int i = 0; i < sz; i++) res[i] = k[i] * exp(ee[i]); } }
接下来,我们创建包装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 # 导入Python Numpy cimport numpy as np # 导入C块 # 从Python和C stdlib导入一些功能 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)
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)