Какой тип аргумента ctypes использовать для изменяемых многомерных массивов - PullRequest
3 голосов
/ 20 июня 2020

Используя ctypes в python, я могу свободно передавать массив в функцию C, используя указатель на свой первый элемент, поскольку массив распадается до указателя на свой первый элемент в C. Итак, для сигнатуры функции C

void insert_sort(int A[], size_t length);

я могу реализовать следующую python оболочку.

import ctypes
from typing import List, Callable

def wrap_function(lib: ctypes.CDLL, name: str, restype: object, argtypes: List[object]) -> Callable:
    """Simplify wrapping ctypes functions"""
    func = lib.__getattr__(name)
    func.restype = restype
    func.argtypes = argtypes
    return func


lib = ctypes.CDLL("path/to/library.so")
insert_sort = wrap_function(lib, 'insert_sort', None, [ctypes.POINTER(ctypes.c_int), ctypes.c_size_t])

Но C также позволяет иметь массивы массивов и еще массивы массивов переменного размера. Например, подпись функции C ниже демонстрирует это.

void matrix_multiply(size_t n, int A[n][n], int B[n][n], int C[n][n]);

Как бы обернуть эту функцию? Чтобы систематизировать мой вопрос, что находится в пустом поле ниже?

matrix_multiply = wrap_function(lib, 'matrix_multiply', None, [...What goes here...])

Если бы размер массивов был известен, мы могли бы просто написать

matrix_multiply = wrap_function(lib, 'matrix_multiply', None, [
    ctypes.c_size_t, ((ctypes.c_int * 4)*4), ((ctypes.c_int * 4)*4), ((ctypes.c_int * 4)*4)
])

для массивов размера 4. Но что может делаем когда это число неизвестно?

1 Ответ

0 голосов
/ 22 июня 2020

Этот ответ основан на обратном проектировании ABI. Я скомпилировал эту тестовую программу для сборки ...

#include <stddef.h>
extern void matrix_multiply(size_t n, int A[n][n], int B[n][n], int C[n][n]);

extern int X[64][64];
extern int Y[64][64];
extern int Z[64][64];

void test(void)
{
  matrix_multiply(64, X, Y, Z);
}

... используя G CC 9 на x86_64- linux, и получил этот ассемблер:

test:
    leaq    Z(%rip), %rcx
    leaq    Y(%rip), %rdx
    leaq    X(%rip), %rsi
    movl    $64, %edi
    jmp matrix_multiply@PLT

На основании этого я прихожу к выводу, что переменно изменяемый параметр массива передается как указатель на его первый элемент, поэтому соответствующее объявление ctypes для matrix_multiply будет

c_int_p = ctypes.POINTER(ctypes.c_int)
matrix_multiply = wrap_function(lib, 'matrix_multiply', None, [
    ctypes.c_size_t, c_int_p, c_int_p, c_int_p
])

Wrapper, который принимает NumPy массивов и / или воспоминания, оставленные в качестве упражнения.

...