簡體   English   中英

使用Python的ctypes傳遞/讀取一個聲明為“ struct_name *** param_name”的參數?

[英]Using Python's ctypes to pass/read a parameter declared as “struct_name *** param_name”?

我正在嘗試使用Python的ctypes庫訪問掃描庫SANE中的某些方法。 這是我第一次使用ctypes,也是我一年來第一次處理C數據類型,因此這里有一個公平的學習曲線,但是我認為即使沒有這個特定的聲明也會很麻煩:

extern SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only);

首先,我已經成功處理了SANE_Status (一個枚舉)和SANE_Bool (一個對c_int的typedef)。 那些都很簡單。 另一方面,第一個參數正在引起我各種各樣的悲傷。 我不熟悉“ *** ”表示法,到目前為止,我的跟蹤器項目符號只產生了垃圾數據。 如何格式化此函數的輸入,以便可以讀回我的Python結構對象列表? 作為參考,被引用的C結構為:

typedef struct
{
    SANE_String_Const name; /* unique device name */
    SANE_String_Const vendor;   /* device vendor string */
    SANE_String_Const model;    /* device model name */
    SANE_String_Const type; /* device type (e.g., "flatbed scanner") */
 }
SANE_Device;

其中SANE_String_Const定義為c_char_p

我的Python / ctypes版本是:

class SANE_Device(Structure):
    _fields_ = [
        ("name", c_char_p),
        ("vendor", c_char_p),
        ("model", c_char_p),
        ("type", c_char_p)]

關於我應該傳遞什么的建議,這樣我才能從中獲得預期的行為(結構對象的列表)? 所有答復表示贊賞。

更新1:

使用以下代碼,我能夠檢索到正確的SANE_Device Python結構:

devices = pointer(pointer(pointer(SANE_Device())))
status = libsane.sane_get_devices(devices, c_int(0))
print status, devices, devices.contents.contents.contents.name

但是,1)uck逼和2)似乎只有在有一個結果的情況下它才起作用。 我無法在devices.contents.contentsdevices.contents.contents.contents上使用len()。 如何確定結果數? SANE文檔指定“如果函數成功執行,它將在* device_list中存儲指向NULL終止的指向SANE_Device結構的指針的數組的指針”。 有什么建議嗎?

更新2:

我能夠傳遞一個十項數組,然后使用以下命令訪問第一個元素:

devices = pointer(pointer(pointer((SANE_Device * 10)())))
status = libsane.sane_get_devices(devices, c_int(0))
print status, devices, devices.contents.contents.contents[0].name

但是,十顯然是一個任意數字,我無法確定結果的實際數目。 僅連接一個設備時嘗試訪問devices.contents.contents.contents[1].name會導致分段錯誤。 必須有適當的方法來處理ctype中的此類可變長度構造。

const SANE_Device ***是三級指針:它是指向常量SANE_Device的指針。 您可以使用程序cdecl解密復雜的C / C ++類型定義。

根據SANE文檔 ,如果成功, SANE_get_devices()將存儲一個指向以NULL結尾的指向SANE設備的指針的指針。 因此,調用它的正確方法是聲明一個類型為const SANE_Device **的變量(即,指向const SANE_Device **的指針的指針),並傳入該指針的地址:

const SANE_Device **device_list;
SANE_get_devices(&device_list, local_only);  // check return value
// Now, device_list[0] points to the first device,
// device_list[1] points to the second device, etc.
// Once you hit a NULL pointer, that's the end of the list:
int num_devices = 0;
while(device_list[num_devices] != NULL)
    num_devices++;
// num_devices now stores the total number of devices

現在,這就是從C代碼調用它的方式。 我已經瀏覽了有關ctypes的文檔,看來您想使用byref函數通過引用傳遞參數,並且您傳遞的值應該是POINTER到POINTER到SANE_Device。 注意pointerPOINTER之間的區別:前者創建指向實例的指針,而后者創建指向類型的指針。 因此,我猜測以下代碼將起作用:

// SANE_Device declared as you had it
devices = POINTER(POINTER(SANE_Device))()  // devices is a NULL pointer to a pointer to a SANE_Device
status = libsane.sane_get_devices(byref(devices), c_int(0))
if status != successful:   // replace this by whatever success is
    print error
else:
    num_devices = 0
    // Convert NULL-terminated C list into Python list
    device_list = []
    while devices[num_devices]:
        device_list.append(devices[num_devices].contents)  // use .contents here since each entry in the C list is itself a pointer
        num_devices += 1
    print device_list

[編輯]我已經使用非常簡單的SANE_get_devices占位符測試了上面的代碼,並且可以正常工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM