3

I wrote a C library to interface with a hardware device over USB, and have used the library successfully with other C code, so I know it works. I want to call this library from Python, and in order to do so I need to pass a struct by reference so that the library can modify it. The structure is defined:

typedef struct eventNode eventNode;
struct eventNode{
    int channels;
    int samples;
    uint16_t** event;
    eventNode* next;
    eventNode* prev;
}

and in Python I have defined:

class eventNode(Structure):
    pass

eventNode._fields_ = [("channels", c_int),
            ("samples", c_int),
            ("event",POINTER(POINTER(c_uint16))),
            ("prev",POINTER(eventNode)),
            ("next",POINTER(eventNode))]

which I believe is correct. Essentially I used this to implement a linked list, which my library can modify by calling a method with signature void getFromCAEN(int, char*, eventNode**, eventNode**). The last two arguments are pointers to pointers to the list head and tail, respectively. My python code creates these pointers by calling:

queueHead = POINTER(eventNode)()
queueTail = POINTER(eventNODE)()

which should initially be NULL (None in ctypes) so that the library knows the list is empty. When I call this function with the line

caenlib.getFromCAEN(handle,buff,byref(queueHead),byref(queueTail))

a linked list should be formed by the library, which I then attempt to index through with the following block:

cur = queueHead
while cur:
    i+=1
    print("event!",i)
    print(addressof(cur))
    print(cur.contents.channels,cur.contents.samples)
    print(cur.contents.event)
    print(cur.contents.next) #print next object in line
    cur = cur.contents.next
    print(cur)               #this prints a different address for some reason

And the output of all those print statements is this:

event! 1
140486871191712
4 150
<__main__.LP_LP_c_ushort object at 0x7fc5a60d9200>
<__main__.LP_eventNode object at 0x7fc5a60d9170>
<__main__.LP_eventNode object at 0x7fc5a60d9290>

But the problem is, that even though I know there should be many events in the list, it only iterates once and then stops. The problem seems to be with those last few lines, that even though cur gets assigned to cur.contents.next, the address seems to change 0x...170 becomes 0x...290 after the assignment. Attempting to dereference cur after this point raises a NULL poitner exception. I think I'm missing something about pointers and assignment in Python, but I'm not sure what to do.

PS the access to cur.contents.channels and cur.contents.samples returns exactly what I expect it to for the one object that I am able to access, indicating that it is working at least partially.

1 Answer 1

2

You have next and prev flipped around in the ctypes definition of eventNode. So when you use next you're actually getting the NULL value of prev.

The changing LP_eventNode addresses are unrelated. That's the address of the Python object created each time you access the Structure field.

from ctypes import *

class eventNode(Structure):
    pass

eventNode._fields_ = [
    ("channels", c_int),
    ("samples", c_int),
    ("event", POINTER(POINTER(c_uint16))),
    ("next", POINTER(eventNode)),
    ("prev", POINTER(eventNode)),
]

_fields_ is defined as a tuple or list such that the order is preserved for calculating the offset of each field. The CField descriptors in the type specify the ctypes type, the size, and the offset of each field.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you! I had no idea that order was important when defining _fields_. If that was in the documentation I must have missed it somehow. And I actually did define the argtypes I just left it out for the sake of brevity.
Now that I see it it makes sense why that should be, the original declaration of the 'struct' is invisible to Python. Thanks again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.