Python ctypes pointer to struct as identifier without member access

 ̄綄美尐妖づ 提交于 2021-01-28 03:44:16

问题


I have two different C functions and I would like to use them with ctypes in Python.

One function is establishing a connection and returns a pointer to an truct. The pointer shall be used as an argument in the second function to reuse the established connection.

C Code:

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

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

Python code:

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

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

Unfortunately, I retrieve access violation errors when I call lib.foo(). I could recreate the customStruct struct in Python using a class with the _fields_ attribute, but since the struct consists of many other structs and since I don't want to access the struct members in Python itself, I'm thinking about an alternative how to create an identifier that can be reused.

I can change the definition of connect() as well as foo() as I'd like. I could also create another "identifier" struct if that would allow me to not have to recreate the struct in python.

Update: It looks like I have to use the function byref() to achieve what I want. https://docs.python.org/3/library/ctypes.html#ctypes.byref

The documentation states "The returned object can only be used as a foreign function call parameter" but I am not sure what to return in connect() then.


回答1:


If you have an opaque structure (you do not know its members, or do not want to know its members), you should still create a class to represent that struct in python. You can then use this class to properly type your functions. This will help prevent bugs where you accidentally pass the wrong object as a "CustomStruct" pointer.

For example:

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

Now, if you tried something like: display(c_void_p()), you would get:

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

The C code I used was:

#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);
}



回答2:


Like mentioned in comments already you need to set restype for the connect function and argtypes for the foo function on Python side.

In code it would look like this:

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)

Test

A short test should verify that this gives the same pointer in your connection and foo function on the C side.

A slightly modified version of your code might look like this:

#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
}

If you run this you get something like:

connect: 0x7fa219e094a0
foo: 0x7fa219e094a0


来源:https://stackoverflow.com/questions/62277376/python-ctypes-pointer-to-struct-as-identifier-without-member-access

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!