I've defined a ctypes
class and an associated convenience function like so:
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),)
I then define a simple convenience function:
def f(a, b):
return [(i.a, i.b) for i in iter(convert(a, b))]
Most of this works perfectly, but I have two issues:
- It's not flexible enough; I'd like to be able to instantiate a
BNG_FFITuple
usingc_float
instead ofc_uint32
(so the fields arec_float
), and vice versa, so theBNG_FFIArray
data_type
isc_uint32
. I'm not clear on how to do this, though. - I'd like to free the memory which is now owned by Python, by sending a
POINTER(BNG_FFIArray)
back to my dylib (seedrop_array
– I've already defined a function in my dylib), but I'm not sure at what point I should call it.
Is there a way of encapsulating all this in a neater, more Pythonic way, which is also safer? I'm concerned that without the memory cleanup being defined in a robust way (on __exit__
? __del__
?) That anything that goes wrong will lead to unfreed memory