3

在 python 中使用ctypes我可以使用指向其第一个元素的指针自由地将数组传递给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 的数组。但是当这个数字未知时我们能做什么?

4

1 回答 1

0

该答案基于对 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);
}

...在 x86_64-linux 上使用 GCC 9,我得到了这种汇编语言:

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

将 NumPy 数组和/或内存视图作为练习的包装器。

于 2020-06-22T13:42:22.967 回答