Python 当 C 函数 returns 动态数组时 ctypes 行为异常
Python ctypes misbehaves when a C function returns a dynamic array
我正在为 Matlab 的动态库开发 Python 包装器 类 以读取 Python 中的 Matlab MAT 文件,我遇到了一个我无法解释的奇怪行为ctypes 接口。
C 函数签名如下所示:
const mwSize *mxGetDimensions(const mxArray *);
这里,mwSize
是重命名的size_t
,mxArray*
是不透明的指针。这个函数returns Matlab 数组的"shape"。返回的指针指向 size_t 数组,该数组存储在 mxArray 对象内部并且 不是 空终止(它的大小是通过另一个函数获得的)。
要从 Python 调用此函数,我按如下方式设置库:
libmx = ctypes.cdll.LoadLibrary('libmx.dll')
libmx.mxGetDimensions.restype = ctypes.POINTER(ctypes.c_size_t)
libmx.mxGetDimensions.argtypes = [ctypes.c_void_p]
在VAR
得到mxArray*
后,我调用:
dims = libmx.mxGetDimensions(VAR)
print(dims[0],dims[1])
VAR
已知是二维的并且具有 (1, 13)
的形状(使用 C 程序验证)但是我的 Python 代码 returns (55834574849 0)
在 c_ulonglong... 存储在测试 MAT 文件中的所有变量的结果始终是垃圾。
我做错了什么?使用 VAR 的其他库调用似乎工作正常,因此 VAR 指向有效对象。如上所述,在 C 程序中调用的 mxGetDimensions() 按预期工作。
如有任何意见,我们将不胜感激!谢谢
@Neitsa 在 OP 下的评论中解决了我的直接问题,对 libmx.dll 的进一步调查解决了 Python 和 C 版本之间的剩余差异。
因为 Matlab 的 libmx.dll 可以追溯到很久以前,因为它最初是在 32 位时代编写的,所以 DLL 包含其函数的多个版本以实现向后兼容。事实证明,const mwSize *mxGetDimensions(const mxArray *);
是该函数的最旧版本,关联的 C 头文件 (matrix.h) 包含行 #define mxGetDimensions mxGetDimensions_800
以使用其最新版本覆盖该函数。显然,Python 的 ctypes 不检查 C 头文件;因此,我需要筛选头文件以确定要使用的函数版本。
最后,当我将代码更改为:
时,获得了 POINTER(c_size_t)
的正确行为
libmx = ctypes.cdll.LoadLibrary('libmx.dll')
libmx.mxGetDimensions_800.restype = ctypes.POINTER(ctypes.c_size_t)
libmx.mxGetDimensions_800.argtypes = [ctypes.c_void_p]
dims = libmx.mxGetDimensions_800(VAR)
所以,你知道了:如果你要包装第 3 方 dynamic/shared 库,请彻底研究相关的头文件。
我正在为 Matlab 的动态库开发 Python 包装器 类 以读取 Python 中的 Matlab MAT 文件,我遇到了一个我无法解释的奇怪行为ctypes 接口。
C 函数签名如下所示:
const mwSize *mxGetDimensions(const mxArray *);
这里,mwSize
是重命名的size_t
,mxArray*
是不透明的指针。这个函数returns Matlab 数组的"shape"。返回的指针指向 size_t 数组,该数组存储在 mxArray 对象内部并且 不是 空终止(它的大小是通过另一个函数获得的)。
要从 Python 调用此函数,我按如下方式设置库:
libmx = ctypes.cdll.LoadLibrary('libmx.dll')
libmx.mxGetDimensions.restype = ctypes.POINTER(ctypes.c_size_t)
libmx.mxGetDimensions.argtypes = [ctypes.c_void_p]
在VAR
得到mxArray*
后,我调用:
dims = libmx.mxGetDimensions(VAR)
print(dims[0],dims[1])
VAR
已知是二维的并且具有 (1, 13)
的形状(使用 C 程序验证)但是我的 Python 代码 returns (55834574849 0)
在 c_ulonglong... 存储在测试 MAT 文件中的所有变量的结果始终是垃圾。
我做错了什么?使用 VAR 的其他库调用似乎工作正常,因此 VAR 指向有效对象。如上所述,在 C 程序中调用的 mxGetDimensions() 按预期工作。
如有任何意见,我们将不胜感激!谢谢
@Neitsa 在 OP 下的评论中解决了我的直接问题,对 libmx.dll 的进一步调查解决了 Python 和 C 版本之间的剩余差异。
因为 Matlab 的 libmx.dll 可以追溯到很久以前,因为它最初是在 32 位时代编写的,所以 DLL 包含其函数的多个版本以实现向后兼容。事实证明,const mwSize *mxGetDimensions(const mxArray *);
是该函数的最旧版本,关联的 C 头文件 (matrix.h) 包含行 #define mxGetDimensions mxGetDimensions_800
以使用其最新版本覆盖该函数。显然,Python 的 ctypes 不检查 C 头文件;因此,我需要筛选头文件以确定要使用的函数版本。
最后,当我将代码更改为:
时,获得了POINTER(c_size_t)
的正确行为
libmx = ctypes.cdll.LoadLibrary('libmx.dll')
libmx.mxGetDimensions_800.restype = ctypes.POINTER(ctypes.c_size_t)
libmx.mxGetDimensions_800.argtypes = [ctypes.c_void_p]
dims = libmx.mxGetDimensions_800(VAR)
所以,你知道了:如果你要包装第 3 方 dynamic/shared 库,请彻底研究相关的头文件。