使用ctypes从C数组结构到NumPy数组的高效转换

时间:2018-06-12 16:57:46

标签: python c++ c numpy ctypes

question之后我尝试使用带有Cython的C ++ DLL,并且教程适合我从未起作用的情况,我决定使用ctypes。我现在使用ctypes成功调用了我感兴趣的函数,这要归功于大量的SO浏览。我现在面临的问题是在Python中使用结构数组结构。

这个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_arr这是一个struct数组,从上面的代码可以看出。 这完全是

<__main__.myStruct_Array_10000 at 0x97966c8>)

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

当然,我可以使用for field, _ in struct._fields_之类的东西进行循环,如here所示,但这不是重点,因为我使用DLL来使计算更快(我真的看到了执行时间处理时间)。 res_arr的范围从200 kb到1 Mb,并且有数万行和几十列,所以我确信有一种方法可以不通过循环来完成所有这些,但我不能弄清楚如何做得很好。

似乎如果它不是一个struct数组,那就更容易了。有a few SO questionsherehereherehere)关闭对于这个主题,但它要么只是转换一个结构,只是一个数组,或者一些接近但从未完全像我这样的东西,而且我没有成功地适应这些解决方案,所以也许有一种方法可以基于此解决问题,但是无论如何我都是耳朵。

1 个答案:

答案 0 :(得分:1)

我们几乎遇到相同的问题,但就我而言,我使用的是CUDA DLL,因此我的编译器为nvcc。但是我相信这也可以通过普通的g++编译器来完成。无论如何,这是我为了将结构数组从CPP文件转换为可用的Python列表/数组而执行的步骤。我不会检查您的代码;相反,我仅向您提供示例,可在此处找到:https://github.com/jcbacong/python-cpp.git

但是重要的步骤总结如下:

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

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

  3. 在您的.py文件中:

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

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

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

我希望这会有所帮助!