2017-05-09 34 views
1

假设我有一个外部的库如下功能:是否有可能使用ctypes有效地将字节和bytearray对象传递到外部库?

void foo(const unsigned char *buf, const int len); 

我希望能够从我的Python代码调用这个函数,使用​​,未做缓冲区的副本。缓冲区可能相当大,因此避免复制具有明显的性能优势。为了方便我的代码的使用者,我希望能够以bytesbytearray的形式提供此缓冲区。

目前我在argtypes声明中声明bufctypes.POINTER(ctypes.c_char)

lib.foo.argtypes = [ctypes.POINTER(ctypes.c_char), ctypes.c_int] 
buf = bytes(...) 
lib.foo(buf, len(buf)) 

这工作正常,我可以通过一个bytes对象。但是,如果我通过bytearray对象,然后我会遇到以下错误:

ctypes.ArgumentError: argument 1: : wrong type

有我的方式,让bytearray传递,最好交替使用bytes

+0

@eryksun感谢您的回复。我没有真正关心如何实现这一目标。我只想让外部代码接收一个'const unsigned char *'而不复制缓冲区的内容。如果可能的话。 –

回答

1

您可以创建一个覆盖from_param以修改bytearray的指针类型的子类。例如:

class Pchar(ctypes.POINTER(ctypes.c_char)): 
    _type_ = ctypes.c_char 
    @classmethod 
    def from_param(cls, param, array_t=ctypes.c_char * 0): 
     if isinstance(param, bytearray): 
      param = array_t.from_buffer(param) 
     return super(Pchar, cls).from_param(param) 

lib.foo.argtypes = [Pchar, ctypes.c_int] 

其对所述bytearray创建的c_char阵列只需要得到在对象的经由Python的缓冲协议内部缓冲器。数组大小并不重要,所以我们可以避免为每个可能的长度bytearray创建一个数组子类。只需使用缓存在from_param参数列表中的长度为0的数组类型。

+0

再次感谢Eryk,我真的欠你一杯啤酒!缓存在from_param参数列表*中的 –

+0

*。我对此感兴趣。在'ctypes._CData'中定义的classmethod没有'array_t'参数,对吧?它只有一个参数,你在这里命名参数。但是你已经添加了一个名为'array_t'的参数,这将永远不会被任何调用者提供。您这样做是为了在模块加载时评估默认值,这是一种潜在的耗时行为。这样你只需要支付一次这样的费用。那是对的吗?我从来没有遇到过这种技术! –

+1

@DavidHeffernan,我使用参数列表来略微优化查找。我可以使用稍微慢一点的class属性。我也可以简单地依靠ctypes如何缓存'ctypes.c_char * 0'创建的类型。对于后者,它每次仍然会执行字节码,调用'sq_repeat'函数,并最终调用'PyCArrayType_from_ctype',它返回基于元组键'(c_char,0)'的缓存类型。这可能看起来很多,但与实际创建新类型对象的成本相比,它相对便宜,这就是为什么ctypes具有类型缓存的原因。 – eryksun

相关问题