【问题标题】:Create an array of pointers in Python using ctypes使用 ctypes 在 Python 中创建一个指针数组
【发布时间】:2022-06-23 00:52:03
【问题描述】:

我想使用与 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”

但是,我真的很难根据字符串列表创建这种数据类型。

我的第一次尝试(基于How do I create a Python ctypes pointer to an array of pointers)如下所示:

# 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,这表示“创建流不成功”。我仔细检查了函数调用的所有其他参数,这两个属性是唯一可能以某种方式出错的属性。

无论出于何种原因,我都无法弄清楚我到底在哪里犯了错误,但希望这里有人能指出我正确的方向。

【问题讨论】:

    标签: python pointers ctypes


    【解决方案1】:

    要将某个类型的列表转换为该类型的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;
    }
    

    test.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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-24
      • 1970-01-01
      • 2018-03-12
      • 1970-01-01
      • 2014-01-23
      • 2016-11-04
      • 2021-03-29
      相关资源
      最近更新 更多