使用哪种ctypes参数类型来处理可变大小的多维数组?

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的数组,我们可以怎样做呢?但是当我们不知道这个数字时,我们该怎么办呢?


1
@CristiFati 我不确定这与问题有什么关系。它可能只是一个空函数void matrix_multiply(size_t n, int A[n][n], int B[n][n], int C[n][n]){}。重点是让Python对任意输入数组运行它。 - Ivor Denham-Dyson
确实是个好问题,我从未考虑过在ctypes中使用vla。 - Antti Haapala -- Слава Україні
1
@CristiFati 这里有一个在线示例 https://onlinegdb.com/SkxXkTRipL,展示了这样一个函数。 - Ivor Denham-Dyson
1
@CristiFati 看了一下,这个在gcc和clang上似乎没问题,可以参考这里 https://godbolt.org/z/77wc3Z。这里也有关于这种声明的参考 https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html,看起来是gnu的扩展。感谢提供链接。 - Ivor Denham-Dyson
1
澄清以上内容,GNU扩展允许在C90模式和C++中实现此功能。据我所知,VLA是C99标准的一部分。 - Ivor Denham-Dyson
显示剩余3条评论
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

基于此,我得出结论:可变数组参数被传递为指向其第一个元素的指针,因此 matrix_multiply 的适当 ctype 声明为:

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数组和/或留作练习的内存视图。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接