【问题标题】:python ctypes, pass double pointer by referencepython ctypes,通过引用传递双指针
【发布时间】:2015-05-19 11:47:09
【问题描述】:

问题

我正在尝试使用具有以下原型的 c 库中的函数:
int glip_get_backends(const char ***name, size_t *count);
这里的name 参数是问题所在。它是一个通过引用传递的 2 维字符数组。在 C 中,该函数的使用如下:

const char** name;
size_t count;
glip_get_backends(&name, &count);
for (size_t i = 0; i < count; i++) {
    printf("- %s\n", name[i]);
}

现在我想使用 ctypes 从 python 中使用这个函数。


我尝试过的

对我来说最合乎逻辑的方法是在 python 中这样做:

lglip = CDLL("libglip.so")
count = c_int(0)
backends = POINTER(POINTER(c_char))
lglip.glip_get_backends(byref(backends), byref(count))

导致错误消息

TypeError: byref() 参数必须是 ctypes 实例,而不是 '_ctypes.PyCPointerType'


下一个方法是使用 POINTER() 函数 3 次并省略 byref(),但这会导致以下错误:

ctypes.ArgumentError: argument 1: : 不知道如何转换参数 1


然后我从this question 中得到灵感,想出了以下几点:

lglip = CDLL("libglip.so")
count = c_int(0)
backends = POINTER(POINTER(c_char))()
lglip.glip_get_backends(byref(backends), byref(count))
for i in range(0, count.value):
    print backends[i]  

由于某种我不完全理解的原因在backends 的定义之后添加() 已修复调用,但我得到的输出是:


似乎我无法在 python 中使用一个迭代器来迭代二维 c 数组,因为它没有正确取消引用它。

【问题讨论】:

  • 您是否尝试过迭代后端[i] 中的对象?这是一个二维数组,因此您可能需要为此使用嵌套循环。我不熟悉 ctypes 的用法,只是凭直觉。
  • 我可以试试,但我不知道循环的范围。这就是为什么在 C 中它只使用一个迭代器。哦,我刚刚在 C 中意识到它使用 %s,因此打印到 \0。正在努力。
  • 您必须将() 添加到backends 因为POINTER(POINTER(c_char)) 是一个类型,类似于c_int vs c_int(0)...first 是类型, 其次是类型的实例。

标签: python c ctypes


【解决方案1】:

在意识到在 C 实现中 %s 用于迭代子字符串后,我想出了以下可行的解决方案:

lglip = CDLL("libglip.so")
count = c_int(0)
backends_c = POINTER(c_char_p)()
lglip.glip_get_backends(byref(backends_c), byref(count))
backends = []
for i in range(0, count.value):
    backends.append(backends_c[i])
print backends

更新:按照eryksun 的建议,将POINTER(POINTER(c_char))() 更改为POINTER(c_char_p)()。这样我可以轻松访问字符串。

【讨论】:

  • c_char_p 是 ctypes 中以 null 结尾的字符串(具体来说,这个简单的 C 类型是 ctypes._SimpleCData 的子类,用 _type_ == 'z' 定义)。使用 count = c_int(); backends = POINTER(c_char_p)() 。这是一个指向空终止字符串的指针。正如您当前所做的那样,通过引用传递这些函数,以便函数填充初始指针和数组计数。 ctypes 实现了指针算术索引,因此backends[i] 获取数组中的第 i 个指针。
  • 谢谢,我明天试试,如果能按预期更新答案。
  • 我实际上已经尝试过首先使用c_char_p,但是最后我仍然没有括号,所以我只得到了错误。
  • 是的,我曾尝试使用backends = POINTER(c_char_p),但这不起作用,因为正如 Mark Tolonen 所解释的那样,它返回的是类型,而不是实例。但我不知道这是问题所在,所以我切换到backends = POINTER(POINTER(c_char)) 并最终切换到backends = POINTER(POINTER(c_char))()。我根本没想过再次使用c_char_p
【解决方案2】:

如果后端成员可以使用index方法,你可以使用这个代码:

lglip = CDLL("libglip.so")
count = c_int(0)
backends = POINTER(POINTER(c_char))()
lglip.glip_get_backends(byref(backends), byref(count))
for i in range(0, count.value):
    print ''.join(backends[i][:backends[i].index("0")])

【讨论】:

  • 不,那只会给AttributeError: 'LP_c_char' object has no attribute 'index'
  • 那么我猜你的代码会很好。我想不出更优雅的方式可以在那里使用简单的类似数组的语法。
猜你喜欢
  • 2016-08-26
  • 1970-01-01
  • 2022-11-15
  • 2014-07-27
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
  • 2019-04-07
相关资源
最近更新 更多