将二维列表从python传递到DLL

大家好 我决定稍微补充一下Python中C / C ++文章。


传递标准类型(例如int,bool,float等)非常简单,但不是很必要。 Python本身将迅速处理此类数据,并且几乎没有人需要将此类代码的一部分转移到C / C ++库中。


但是传输大型数据数组,甚至更好的二维数据数组,甚至对象的二维数组。


这里的一切都不是那么明显,对于那些想显着加快python解释器难度的代码段的人来说,我认为可以强调很多事情。


剪切下给出的示例并不是很有用,但我认为足以突出此过程的所有细微差别。


立即提供库文件的源代码。


py_list2c_array.h


#ifndef _PY_LIST_2_C_ARRAY_H_ #define _PY_LIST_2_C_ARRAY_H_ #include <stdio.h> typedef struct { int value; wchar_t* name; } Item; extern "C" __declspec(dllexport) int sum_diagonal(Item** field, size_t size); #endif 

py_list2c_array.cpp


除了我注意到使用wprintf()函数来打印诸如wchar_t *之类的行外,所有内容在这里都是标准的。


 // py_list2c_array.cpp:      DLL. // #include "stdafx.h" #include "py_list2c_array.h" extern "C" __declspec(dllexport) int sum_diagonal(Item** field, size_t size) { int result = 0; for(size_t i=0; i<size;++i) { for(size_t j=0; j<size; ++j) { if(i == j) { result += field[i][j].value; wprintf(L"%s\n", field[i][j].name); } } } return result; } 

py_list2c_array.py


现在最重要的是。 我将提供python脚本的源代码,并附有要点的描述。


 import ctypes class PyItem: def __init__(self, value, name): self.value = value self.name = name class CItem(ctypes.Structure): _fields_ = [ ('value', ctypes.c_int), ('name', ctypes.c_wchar_p) ] def create_list(size): return [[PyItem(int(str(i+1)+str(j+1)), 'item{}{}'.format(i+1, j+1)) for j in range(size)] for i in range(size)] def py_list2c_array(py_list, size): rowType = CItem * size resultType = ctypes.POINTER(CItem) * size result = resultType() for i in range(size): row = rowType() for j in range(size): row[j] = CItem() row[j].value = py_list[i][j].value row[j].name = ctypes.c_wchar_p(py_list[i][j].name) result[i] = ctypes.cast(row, ctypes.POINTER(CItem)) return ctypes.cast(result, ctypes.POINTER(ctypes.POINTER(CItem))) if __name__ == '__main__': sLib = ctypes.cdll.LoadLibrary('./py_list2c_array.dll') size = 4 py_list = create_list(size) c_array = py_list2c_array(py_list, size) sLib.sum_diagonal.argtypes = [ctypes.POINTER(ctypes.POINTER(CItem)), ctypes.c_size_t] sLib.sum_diagonal.restype = ctypes.c_int result = sLib.sum_diagonal(c_array, ctypes.c_size_t(size)) print(': {}'.format(result)) 

详细资料


让我们考虑一下创建ctypes数组的一些功能。 为此,我们将仔细研究将列表转换为py_list2c_array数组的功能。 您必须先指定类型。


数组中每一行的类型定义为元素的类型乘以元素的数量。


rowType = CItem *大小


数组类型定义为数组的行类型乘以行数。


我将在下面对ctypes.POINTER()进行一些解释。ResultType = ctypes.POINTER(CItem)* size


接下来,创建结果数组.result = resultType()


在循环中,我们将每一行创建为一维数组.row = rowType()


接下来,在嵌套循环中,创建数组的每个元素,并从对象列表python.row [j] = CItem()向结构分配值


行[j] .value = py_list [i] [j] .value
行[j] .name = ctypes.c_wchar_p(py_list [i] [j] .name)


然后,应将每个创建的带有元素的行转换为指向对象数组的指针的类型,并将其分配给结果数组的单元格。


我将在.result [i] = ctypes.cast(row,ctypes.POINTER(CItem)) 下面写一下ctypes.cast()函数。
当然,将整个数组转换为指针返回ctypes.cast(结果为ctypes.POINTER(ctypes.POINTER(CItem)))


ctypes.POINTER


Ctypes具有ctypes.POINTER() -指示正在使用指针。 例如:ctypes.POINTER(CItem)表示它是指向CItem()结构的指针。
相应地,在ctypes.POINTER(ctypes.POINTER(CItem))行中,我们可以指出这是一个指向CItem结构的指针,或者在C ++ CItem ** A中存在ctypes.pointer() 。 该函数返回一个指向对象的指针。 例如:item = CItem()


指针= ctypes.pointer(项目)


不要混淆它们,因为它们的含义完全不同。


ctypes.cast()


现在考虑非常重要的函数ctypes.cast(),该函数有点类似于C ++中的static_cast()


它使您可以进行非常重要的演员表转换。


创建数组类型时,例如:rowType = CItem * 4


行= rowType()


在这种情况下,行是CItem结构的4个元素的存储区域。


当然,以这种形式,我们将无法使用此数据。 但是,如果我们对它们使用强制转换功能:array_pointer = ctypes.cast(row,ctypes.POINTER(CItem))


在这种情况下,array_pointer已经是指向具有4个CItem结构的存储区域的指针。


第一个参数使用数组元素设置创建的内存区域,第二个参数指示该区域应转换为哪种类型,好吧,这似乎突出显示了使用ctypes传输数据数组时的要点。


我希望本文能帮助您更快,更全面地处理精彩的ctypes库。

Source: https://habr.com/ru/post/zh-CN466575/


All Articles