我定义了一个ctypes
类和一个相关的便利函数,如下所示:
class BNG_FFITuple(Structure):
_fields_ = [("a", c_uint32),
("b", c_uint32)]
class BNG_FFIArray(Structure):
_fields_ = [("data", c_void_p),
("len", c_size_t)]
# Allow implicit conversions from a sequence of 32-bit unsigned ints
@classmethod
def from_param(cls, seq):
return seq if isinstance(seq, cls) else cls(seq)
def __init__(self, seq, data_type = c_float):
array_type = data_type * len(seq)
raw_seq = array_type(*seq)
self.data = cast(raw_seq, c_void_p)
self.len = len(seq)
def bng_void_array_to_tuple_list(array, _func, _args):
res = cast(array.data, POINTER(BNG_FFITuple * array.len))[0]
return res
convert = lib.convert_to_bng
convert.argtypes = (BNG_FFIArray, BNG_FFIArray)
convert.restype = BNG_FFIArray
convert.errcheck = bng_void_array_to_tuple_list
drop_array = lib.drop_array
drop_array.argtypes = (POINTER(BNG_FFIArray),)
然后定义一个简单的方便函数:
^{pr2}$大多数方法都很完美,但我有两个问题:
c_float
而不是c_uint32
(因此字段是c_float
),反之亦然,所以BNG_FFIArray
data_type
是{POINTER(BNG_FFIArray)
回我的dylib(参见drop_array
-我已经在dylib中定义了一个函数),但是我不确定在什么时候应该调用它。在有没有一种更简洁、更像Python的方式来封装所有这些,这样也更安全?我担心的是,如果没有以健壮的方式定义内存清理(on __exit__
?__del__
?)任何出错的事情都会导致记忆不清
由于您对rust端有一定的控制权,所以最简单的做法是在调用之前从Python预先分配结果数组,并在单个结构中传递所有内容。在
下面的代码假定进行了此修改,但也指定了如果无法执行此操作,则将执行释放操作的位置。在
请注意,如果您进行这种封装,则不需要为库函数指定参数和结果处理等内容,因为您只从单个位置调用实际函数,并且始终使用完全相同的参数类型。在
我不知道rust(甚至我的C也有点生锈),但下面的代码假设您重新定义了rust,以匹配类似的东西:
这是Python。最后要注意的是,由于参数结构的重用,这不是线程安全的。如果需要的话,这很容易修复。在
^{pr2}$下面是在调用的DLL中分配返回数组的代码的修改版本。因为用纯Python进行测试比较困难,而且我不懂rust,所以我为实际测试构建了一个低俗的C库:
下面是调用它的Python代码:
^{pr2}$下面是我执行的结果:
这是一个32位的Ubuntu14.04系统。我使用Python2.7,用
gcc shared ffitest.c -o testlib.so -Wall
构建了这个库相关问题 更多 >
编程相关推荐