【问题标题】:Python ctypes pointer to struct as identifier without member accessPython ctypes指向结构的指针作为没有成员访问的标识符
【发布时间】:2020-09-28 07:43:45
【问题描述】:

我有两个不同的 C 函数,我想在 Python 中将它们与 ctypes 一起使用。

一个函数正在建立一个连接并返回一个指向结构的指针。该指针应用作第二个函数中的参数以重用已建立的连接。

C 代码:

customStruct * connect()
{
    customStruct *obj = connection_helper();
    return obj;
}

void foo(customStruct * obj)
{
    foo_helper(obj);
}

Python 代码:

from ctypes import *
lib = CDLL("./test.dll")

obj = lib.connect()
lib.foo(obj)

不幸的是,当我调用lib.foo() 时,我检索到访问冲突错误。我可以使用具有_fields_ 属性的类在Python 中重新创建customStruct 结构,但是由于该结构由许多其他结构组成,并且由于我不想访问Python 本身中的结构成员,所以我正在考虑另一种如何创建可重复使用的标识符。

我可以随意更改connect()foo() 的定义。如果我不必在 python 中重新创建结构,我还可以创建另一个“标识符”结构。

更新: 看来我必须使用函数byref() 来实现我想要的。 https://docs.python.org/3/library/ctypes.html#ctypes.byref

文档声明“返回的对象只能用作外部函数调用参数”,但我不确定在 connect() 中返回什么。

【问题讨论】:

  • 你需要在你的函数上设置argtypesrestype
  • 谢谢。将 argtypes 设置为 [c_void_p] 有效。

标签: python c struct ctypes


【解决方案1】:

就像在 cmets 中提到的那样,您需要在 Python 端为 connect 函数设置 restype 并为 foo 函数设置 argtypes。

在代码中它看起来像这样:

from ctypes import *

lib = cdll.LoadLibrary("some.dll")
lib.connect.restype = c_void_p
lib.foo.argtypes = c_void_p,

obj = lib.connect()
lib.foo(obj)

测试

一个简短的测试应该验证这在 C 端的连接和 foo 函数中提供了相同的指针。

稍作修改的代码版本可能如下所示:

#include <stdlib.h>
#include <stdio.h>

typedef struct  {
    int x;
} customStruct;

static customStruct *connection_helper() {
    return malloc(sizeof(customStruct));
}

customStruct *connect()
{
    customStruct *obj = connection_helper();
    printf("connect: %p\n", obj);
    return obj;
}

void foo(customStruct * obj)
{
    printf("foo: %p\n", obj);
    //do something
}

如果你运行它,你会得到类似的东西:

connect: 0x7fa219e094a0
foo: 0x7fa219e094a0

【讨论】:

  • 感谢您的回答。现在可以了。我只是想知道为什么它使用c_void_p 作为restypeargtypes。为什么我不必创建一个指向 customStruct 的自定义指针?
  • 您必须确保 Python 知道 connect 的调用传递了一个指针,并在您使用该指针作为参数调用 foo 时正确地原样返回它。当然,如果你想在 Python 端访问结构的字段,情况会有所不同。
  • @brnk 所有指针都具有相同的宽度(在给定的机器上)。指针的类型不会改变这一点。指针的类型只是帮助阻止您传递字符串指针,就好像它是 customStruct 指针一样。理想情况下,您应该在 python 中定义 customStruct,以便 ctypes 可以检测您是否将错误的类型传递到 C 库中。
【解决方案2】:

如果你有一个不透明的结构(你不知道它的成员,或者不想知道它的成员),你仍然应该在 python 中创建一个类来表示该结构。然后,您可以使用此类正确键入您的函数。这将有助于防止您不小心将错误的对象作为“CustomStruct”指针传递的错误。

例如:

from ctypes import cdll, c_int, c_void_p

mylib = cdll.LoadLibrary('mylib')

class CustomStructP(c_void_p):
    # subclassing c_void_p creates an opaque pointer type that is distinct
    # from c_void_p, and can only be instantiated as a pointer
    pass

create = mylib.create
create.argtypes = [c_int]
create.restype = CustomStructP

display = mylib.display
display.argtypes = [CustomStructP]
display.restype = None

delete = mylib.delete
delete.argtypes = [CustomStructP]
delete.restype = None

obj = create(10)
display(obj)
delete(obj)

display(CustomStructP())  # passing a null pointer

现在,如果你尝试类似:display(c_void_p()),你会得到:

Traceback (most recent call last):
  File "C:\Users\User\Documents\python\src\main.py", line 31, in <module>
    display(c_void_p())
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type

我使用的 C 代码是:

#include <stdio.h>
#include <stdlib.h>

struct customStruct {
    int val;
};

struct customStruct *
create(int val) {
    struct customStruct *obj = malloc(sizeof(struct customStruct));
    obj->val = val;
    return obj;
}

void
display(struct customStruct *obj) {
    if (obj) {
        printf("customStruct(%d) @ %p\n", obj->val, obj);
    }
    else {
        puts("customStruct is NULL");
    }
}

void
delete(struct customStruct *obj) {
    free(obj);
}

【讨论】:

  • 非常感谢您提供结构良好且富有启发性的答案。我用它在我的C 库中为Python 模拟OOP。不过我有一个问题。有很多方法可以为Python (intermediate-and-advanced-software-carpentry.readthedocs.io/en/…) 包装Ccode。使用另一种方法来解决上述 OOP 问题有什么好处吗?也许其他一些方法在 Python 中需要较少的初始化?
  • 我认为这取决于您的用例。 ctypes 是一个很好的包装库(即您不能或不想访问底层源代码),而且它位于标准库中。用 CPython 编写模块可以为您提供大量控制,但更容易出错(即造成内存泄漏)。 Cython 非常适合在同一个源文件中混合伪 C/C++ 代码和 python,此外它还为您进行内存管理。我在 SWIG 方面的经验是有限的,但它确实允许用最少的代码行包装一个库。见:stackoverflow.com/questions/32053325
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-01
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 2019-08-30
  • 1970-01-01
  • 2014-06-09
相关资源
最近更新 更多