簡體   English   中英

使用 ctypes 在 Python 中創建一個指針數組

[英]Create an array of pointers in Python using ctypes

我想使用與 C 數據類型“const char**”匹配的 ctypes 創建一個 Python 數據類型,它類似於一個指針數組。 但是,我無法在 Python 中對此進行編碼。 簡化的 C 函數標頭如下所示:

int foo(int numOfProp, const char** propName, const char** propValue);

在 C 中,正確的函數調用如下所示:

const char *PropName[2];
PropName[0] = "Prop_Index_1";
PropName[1] = "Prop_Index_2";

const char *PropValue[2];
PropValue[0] = "10";
PropValue[1] = "20";

stream_id = (*foo)(2, PropName, PropValue);

基本上,該函數采用兩個數組(名稱和值對)以及兩個數組的長度,並返回一個流 ID。 加載 DLL 時,我可以看到該函數需要屬性數組的此 ctypes 數據類型:

“LP_c_char_p”

但是,我真的很難根據字符串列表創建這種數據類型。

我的第一次嘗試(基於如何創建指向指針數組的 Python ctypes 指針)如下所示:

# set some dummy values
dummy_prop_values = [
    "10",
    "20"
]

# create property dict
properties = {
    f"Prop_Index_{i}": dummy_prop_values[i] for i in range(len(dummy_prop_values))
}

def first_try():
    # create a dummy ctype string
    ctypes_array = ctypes.c_char_p * 2

    # create empty c-type arrays for the stream properties
    prop_names = ctypes_array()
    prop_values = ctypes_array()

    # fill the empty arrays with their corresponding values
    for i, (prop_name, prop_value) in enumerate(properties.items()):
        prop_names[i] = prop_name.encode()
        prop_values[i] = prop_value.encode()

    # get pointer to properties
    ptr_prop_names = ctypes.pointer(prop_names)
    ptr_prop_values = ctypes.pointer(prop_values)

    return ptr_prop_names, ptr_prop_values

當我將返回的值交給函數 foo 時,它會引發這種錯誤(這實際上是有道理的,因為我明確地創建了一個長度為 2 的數組......問題):

ctypes.ArgumentError: argument 2: <class 'TypeError'>: expected LP_c_char_p instance instead of LP_c_char_p_Array_2

我的第二次嘗試(或多或少基於我自己的想法)如下所示:

def second_try():
    # convert properties to lists
    prop_names = [x for x in properties.keys()]
    prop_values = [x for x in properties.values()]
    
    # concat list elements, zero terminated
    # but I guess this is wrong anyway because it leads to an early string-termination (on byte-level)...?
    prop_names = ctypes.c_char_p("\0".join(prop_names).encode())
    prop_values = ctypes.c_char_p("\0".join(prop_values).encode())
    
    # get pointer to properties
    ptr_prop_names = ctypes.pointer(prop_names)
    ptr_prop_values = ctypes.pointer(prop_values)

    return ptr_prop_names, ptr_prop_values

這實際上不會引發錯誤,而是返回 -1 作為流 ID,這表示“創建流不成功”。 我仔細檢查了函數調用的所有其他參數,這兩個屬性是唯一可能以某種方式出錯的屬性。

無論出於何種原因,我都無法弄清楚我在哪里犯了錯誤,但希望這里有人能指出我正確的方向。

要將某個類型的列表轉換為該類型的ctypes數組,直接的習慣用法是:

(element_type * num_elements)(*list_of_elements)

在這種情況下:

(c_char_p * len(array))(*array)

請注意, (*array)擴展了數組,就好像每個單獨的元素都作為參數傳遞一樣,這是初始化數組所必需的。

完整示例:

test.c - 驗證參數是否按預期傳遞。

#include <stdio.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

API int foo(int numOfProp, const char** propName, const char** propValue) {
    for(int i = 0; i < numOfProp; i++)
        printf("name = %s    value = %s\n", propName[i], propValue[i]);
    return 1;
}

測試.py

import ctypes as ct

dll = ct.CDLL('./test')
# Always define .argtypes and .restype to help ctypes error checking
dll.foo.argtypes = ct.c_int, ct.POINTER(ct.c_char_p), ct.POINTER(ct.c_char_p)
dll.foo.restype = ct.c_int

# helper function to build ctypes arrays
def make_charpp(arr):
    return (ct.c_char_p * len(arr))(*(s.encode() for s in arr))

def foo(arr1, arr2):
    if len(arr1) != len(arr2):
        raise ValueError('arrays must be same length')
    return dll.foo(len(arr1) ,make_charpp(arr1), make_charpp(arr2))

foo(['PropName1', 'PropName2'], ['10', '20'])

輸出:

name = PropName1    value = 10
name = PropName2    value = 20

暫無
暫無

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

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