2017-05-09 33 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

相關問題