当前位置: 首页 > 编程笔记 >

cython 包装DLL:从C ++到Cython到Python

华烈
2023-03-14
本文向大家介绍cython 包装DLL:从C ++到Cython到Python,包括了cython 包装DLL:从C ++到Cython到Python的使用技巧和注意事项,需要的朋友参考一下

示例

这展示了一个用Cython包装C ++ dll的简单例子。它将涵盖以下主要步骤:

  • 使用Visual Studio使用C ++创建示例DLL。

  • 用Cython包裹DLL,以便可以在Python中调用它。

假定您已安装Cython,并可以在Python中成功导入它。

对于DLL步骤,还假定您熟悉在Visual Studio中创建DLL。

完整的示例包括以下文件的创建:

  1. complexFunLib.h:C ++ DLL源的头文件

  2. complexFunLib.cpp:C ++ DLL源的CPP文件

  3. ccomplexFunLib.pxd:Cython“头文件”

  4. complexFunLib.pyx:Cython“包装器”文件

  5. setup.py:complexFunLib.pyd使用Cython创建的Python安装文件

  6. run.py:导入已编译的Cython包装的DLL的示例Python文件

C ++ DLL来源:complexFunLib.h和complexFunLib.cpp

如果您已经有了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]);
    }
}

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  # 导入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)

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)
           

 类似资料:
  • 问题内容: 我有一个接受回调的C ++函数,如下所示: 我想通过给它一个闭包从Cython调用此函数,即,如果我从C ++调用它,我会用lambda来完成。如果这是一个C函数,它将有一些额外的参数: 然后我只想通过的(有更详细的在这里例子)。 有没有办法以C ++的方式做更多的事情,而不必诉诸显式的? 问题答案: 我相信您的目标是将可调用的Python对象传递给接受的对象。您需要创建一些C ++代

  • 问题 你想使用Cython来创建一个Python扩展模块,用来包装某个已存在的C函数库。 解决方案 使用Cython构建一个扩展模块看上去很手写扩展有些类似, 因为你需要创建很多包装函数。不过,跟前面不同的是,你不需要在C语言中做这些——代码看上去更像是Python。 作为准备,假设本章介绍部分的示例代码已经被编译到某个叫 libsample 的C函数库中了。 首先创建一个名叫 csample.p

  • 问题内容: 我想从Python应用程序调用C库。我不想包装整个API,只包装与我的情况相关的函数和数据类型。如我所见,我有三个选择: 在C中创建一个实际的扩展模块。可能有点过头了,我还想避免学习扩展编写的开销。 使用Cython将C库的相关部分公开给Python。 使用Python与外部库进行通信,从而完成整个工作。 我不确定2)还是3)是更好的选择。3)的优点是它是标准库的一部分,并且生成的代码

  • 问题内容: 我有许多C函数,我想从python调用它们。cython似乎是要走的路,但我无法真正找到实现此目的的示例。我的C函数如下所示: 我要做的就是指定前三个参数(一个字符串和两个整数),并恢复8个numpy数组(或python列表。所有双精度数组都有N个元素)。我的代码假定指针指向已分配的内存块。同样,产生的C代码应该链接到一些外部库。 问题答案: 这是一个从逻辑上将numpy数组传递给外部

  • 准确说Cython是单独的一门语言,专门用来写在Python里面import用的扩展库。实际上Cython的语法基本上跟Python一致,而Cython有专门的&ldquo;编译器&rdquo;先将 Cython代码转变成C(自动加入了一大堆的C-Python API),然后使用C编译器编译出最终的Python可调用的模块。

  • 问题内容: 我刚刚开始熟悉Cython,尝试将一些结构从C库包装到Python方法和类。我真正不了解的是从(初始化的)C结构到相应的Python类的转换应该如何工作。我在这里想念的是什么: C头文件中的片段: 我的.pxd对应的代码段: 我的.pyx: 直到return语句的所有内容都可以正常工作。我在等中获得了正确的值。但是return语句中的强制转换似乎不正确或缺少更多信息。发出Python段