首页 > 解决方案 > 使用 ctypes 从 C 结构数组到 NumPy 数组的高效转换

问题描述

在我试图将 C++ DLL 与. 由于繁重的 SO 浏览,我现在使用 ctypes 成功调用了我对 DLL 感兴趣的函数。我现在面临在 Python 中使用结构数组的结果的问题。Cythonctypes

这个C函数如下:

void myfun(
                 double         a,
                 //...more double parameters
                 int            max_iter,
                 int *          nb_iter,
                 myStruct *     res_arr,
                 bool *         ok
                );

定义myStruct如下:

typedef struct  {
             double dat;

             int    k;
             int    m;
            // ... more int

             double b;
             double v;
             //...more double

            } myStruct;

我通过以下 Python 代码调用此函数:

import ctypes
lib = ctypes.CDLL('PATH_TO_DLL\\lib.dll')

myFunPy = getattr(lib,"?myFun@@YANNNNN_BUNCH_OF_Ns_NNNHPEAHPEAUmyStruct@@PEA_N@Z") # name found through dumpbin.exe (due to C++)

class myStruct(ctypes.Structure):
    _fields_ = [("k", ctypes.c_int),
                ("m", ctypes.c_int),
                #...more int parameters

                ("b", ctypes.c_double),
                ("v", ctypes.c_double)
                #...more double parameters
               ]

myFunPy.argtypes = [ctypes.c_double,
                   // ... more double parameters

                   ctypes.c_int,
                   ctypes.POINTER(ctypes.c_int),
                   ctypes.POINTER(myStruct),
                   ctypes.POINTER(ctypes.c_bool)]

myFunPy.restype = ctypes.c_void_p

max_iter = 10000
a = ctypes.c_double(0.1)
// ... more double parameters definitions

nb_iter = ctypes.c_int(0) # value doesn't matter, it is initialized in myFun
ok = ctypes.c_bool(True)

res_arr = (myStruct * max_iter)()

myFunPy(a, ..., max_iter, ctypes.byref(nb_iter), res_arr, ctypes.byref(ok))

现在myFun修改res_arrwhich 是一个结构数组,从上面的代码可以看出。正是

<__main__.myStruct_Array_10000 at 0x97966c8>)

在上面显示的代码之后,但我无法理解如何将其转换为 NumPy 数组以供将来有效使用。

当然,我可以使用此处for field, _ in struct._fields_所示的内容进行 for 循环,但这不是重点,因为我使用 DLL 使我的计算更快(我真的看到了执行时间的差异)。范围从 200 kb 到 1 Mb,有数万行和几十列,所以我确信有一种方法可以不用循环遍历所有内容,但我不知道该怎么做太好了。res_arr

似乎如果它不是一个结构数组,它会更容易。有一些 SO问题(也在这里这里这里这里)接近这个主题,但它要么只是转换一个结构,只是一个数组,或者是一些接近但从不完全像我的东西,我没有成功 调整这些解决方案,所以也许有办法以此为基础回答,但无论如何我都在听。

标签: pythonc++cnumpyctypes

解决方案


我们有几乎相同的问题,但就我而言,我使用的是 CUDA DLL,所以我的编译器是nvcc. 但我相信这也可以通过普通的g++编译器来完成。无论如何,这里是我为了将结构数组从我的 CPP 文件转换为可用的 Python 列表/数组而执行的步骤。我不会浏览你的代码;相反,我只会给你一个可以在这里找到的例子:https ://github.com/jcbacong/python-cpp.git

但重要的步骤总结如下:

  1. extern "C"使用包含函数声明的必要头文件创建一个 .cpp 文件。在我的 .cpp 文件中,我返回了一个结构数组,而不是返回一个void.

  2. 使用您的编译器创建一个 .dll 文件。同样,在我的情况下,它是nvcc. 我通过我的 github 帐户链接的示例代码是使用nvcc.

  3. 在您的 .py 文件中:

    3.1 创建一个 Python 类,ctypes.Structure以便复制 .cpp/.h 文件中的结构定义。

    3.2 使用 . 初始化您的输入/输出argtype/restype。由于我的 .cpp 函数返回一个结构数组,因此由 .cpprestype给出ctypes.Pointer(<your Python Class(ctypes.Structure)>)

    3.3 我将所有输入转换为可读的 ctypes。在我的 .py 文件中调用函数后,_results可以使用 ( ) 将生成的结构数组(在示例中)转换为 Python 列表results = _results[:ARRAY_SIZE]

我希望这有帮助!!


推荐阅读