Pasar listas bidimensionales de Python a DLL

Hola a todos Decidí complementar ligeramente el artículo C / C ++ de Python .


Pasar tipos estándar como int, bool, float, etc. es bastante simple, pero no muy necesario. Python sí mismo hará frente rápidamente a dichos datos, y es poco probable que alguien necesite transferir parte de dicho código a la biblioteca C / C ++.


Pero la transferencia de grandes conjuntos de datos, o incluso mejores conjuntos de datos bidimensionales, o incluso conjuntos de objetos bidimensionales.


Todo aquí no es tan obvio, y hay una serie de cosas que creo que pueden destacarse para aquellos que desean acelerar significativamente las secciones de código difíciles para el intérprete de Python.


El ejemplo dado debajo del corte no es muy útil para usar, pero creo que es suficiente para resaltar todos los matices de este procedimiento.


Inmediatamente proporcione el código fuente de los archivos de la biblioteca.


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


Aquí también todo es estándar, excepto que noto el uso de la función wprintf () para imprimir líneas como 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


Y ahora lo más importante. Daré el código fuente de un script de Python con una descripción de puntos importantes.


 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)) 

Detalles


Consideremos algunas características de la creación de matrices ctypes. Para hacer esto, veremos más de cerca la función de convertir la lista a py_list2c_array Debe especificar los tipos primero.


El tipo de cada fila en la matriz se define como el tipo de elemento multiplicado por el número de elementos.


rowType = CItem * tamaño


Un tipo de matriz se define como el tipo de fila de la matriz multiplicado por el número de filas.


Explicaré un poco más abajo sobre ctypes.POINTER (). ResultType = ctypes.POINTER (CItem) * tamaño


A continuación, cree la matriz resultante .result = resultType ()


Y en el bucle creamos cada fila como una matriz unidimensional .row = rowType ()


A continuación, en un bucle anidado, cree cada elemento de la matriz y asigne valores a la estructura desde la lista de objetos python.row [j] = CItem ()


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


Luego, cada línea creada con elementos debe convertirse en un tipo de puntero a una matriz de objetos y asignarse a la celda de la matriz resultante.


Escribiré sobre la función ctypes.cast () a continuación .result [i] = ctypes.cast (row, ctypes.POINTER (CItem))
Y, por supuesto, convierta toda la matriz en un puntero. Devuelva ctypes.cast (result, ctypes.POINTER (ctypes.POINTER (CItem)))


ctypes.PUNTERO


Ctypes tiene ctypes.POINTER () : indica que se está utilizando un puntero. Por ejemplo: ctypes.POINTER (CItem) indica que es un puntero a una estructura CItem ().
En consecuencia, con la línea: ctypes.POINTER (ctypes.POINTER (CItem)) podemos indicar que es un puntero a un puntero a una estructura CItem, o en C ++ CItem ** A hay ctypes.pointer () . Esta función devuelve un puntero a un objeto. Por ejemplo: item = CItem ()


puntero = ctypes.pointer (elemento)


No deben confundirse, ya que su significado es completamente diferente.


ctypes.cast ()


Ahora considere la función muy importante ctypes.cast (). Esta función es algo similar a static_cast () de C ++.


Te permite hacer moldes muy importantes.


Al crear un tipo de matriz, por ejemplo: rowType = CItem * 4


fila = rowType ()


En este caso, la fila es un área de memoria de 4 elementos de estructuras CItem.


Por supuesto, de esta forma, no podremos utilizar estos datos. Pero si usamos la función de conversión en ellos: array_pointer = ctypes.cast (row, ctypes.POINTER (CItem))


En este caso, array_pointer ya es un puntero a un área de memoria con 4 estructuras CItem.


El primer parámetro es el área de memoria creada con elementos de la matriz, y el segundo parámetro es el tipo al que se debe convertir la región dada. Bueno, parece resaltar los puntos principales cuando se transfieren matrices de datos utilizando ctypes.


Espero que este artículo te ayude a lidiar de manera más rápida y completa con la maravillosa biblioteca de tipos.

Source: https://habr.com/ru/post/466575/


All Articles