我想使用與 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,這表示“創建流不成功”。我仔細檢查了函式呼叫的所有其他引數,這兩個屬性是唯一可能以某種方式出錯的屬性。
無論出于何種原因,我都無法弄清楚我在哪里犯了錯誤,但希望這里有人能指出我正確的方向。
uj5u.com熱心網友回復:
要將某個型別的串列轉換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
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/496576.html