Ctypes: Mapping a returned pointer to a structure

Question:

I have tried reading all the questions/answers on this topic but I could not get anything to work. All I am trying to do is send a structure to a shared object, and then have it returned and be accessible.

I have managed to create a structure OK, and I can pass it fine to the shared object. I know this as it works fine if returning a specific value from the structure. The structure definition in Python can be seen below:

class ROW(ctypes.Structure):
    _fields_ = [("Address",ctypes.c_int16),
                ("FirstRegister",ctypes.c_int16),
                ("NumberOfRegisters",ctypes.c_int16),
                ("Sampling", ctypes.c_int16)]

class CONNECTOR(ctypes.Structure):
    _fields_ = [("NumberOfRows", ctypes.c_int16),
                ("Rows", ROW * 200)]

# Initialise an array of connectors (CON1, CON2, CON11, CON12)
ConArray = CONNECTOR * 4
con = ConArray()

# Initialise an array of ROW struct
RowArray = ROW * 200
row = RowArray()

I then populate the structure with data from an SQLite database and can access the specific data using con[n].Rows[m].Address etc.

I am currently trying to just send one connector at a time, and return the exact same structure.

The relevant code can be seen below:

testlib = ctypes.cdll.LoadLibrary('_example.so')
x = testlib.square_array
x.argtype = ctypes.POINTER(CONNECTOR)
x.restype = ctypes.POINTER(CONNECTOR)

y = x(ctypes.byref(con[0]))

I have tried many different methods of calling the function but this one seems to be the most promising. The problem is when I try and then access specific values with y.Rows[0].Address[0], an error occurs: AttributeError: 'LP_CONNECTOR' object has no attribute 'Rows'.

If instead, I just call the function directly:

x = testlib.square_array(ctypes.byref(con[0]))

I receive an int which I assume represents a memory address, eg: 35664848.

I have tried all sorts of options but I am quite unfamiliar with C (a colleague will be handling all the C side of code). Is there any proposed solutions? I feel like I am just missing one small thing, but it has took me many days just to reach this point.

Update: Added C code

The C code can be seen below:

example.c:

 #include "example.h"

 CONNECTOR* square_array(CONNECTOR* con)
 {
    printf("Value of Row 0 Address%in",con->Rows[0].Address);
    return (con);
 }

example.h:

struct ROW;
struct CONNECTOR;

typedef struct {
    short int Address;
    short int FirstRegister;
    short int NumberOfRegisters;
    short int Sampling; 
}ROW;

typedef struct {
    short int NumberOfRows;
    ROW Rows[200];
}CONNECTOR;

CONNECTOR Connectors[4];
Asked By: J.Greenslade

||

Answers:

I already created a small example of my own. After taking a closer look at the Python code, I could figure out the function header (and I also added some dummy body).

The problem is simple, the function returns a CONNECTOR pointer, in order to access its members, you need to "dereference" it first, otherwise you’ll get the AttributeError. Here’s your code (a little bit modified):

testlib = ctypes.cdll.LoadLibrary("_example.so")
testfunc = testlib.square_array
testfunc.argtypes = (ctypes.POINTER(CONNECTOR),)
testfunc.restype = ctypes.POINTER(CONNECTOR)

pconnector0 = testfunc(ctypes.byref(con[0]))
connector0 = pconnector0.contents
print(connector0.NumberOfRows)
print(connector0.Rows[0].Address)

The "secret" lies in pconnector0.contents which performs the "dereferencing".

As a side note the line of code y.Rows[0].Address[0] will trigger a TypeError because of the [0] at the end: Address is an int and can’t be indexed (doesn’t define __getitem__).

Regarding the second approach (calling the function directly), it’s the same problem. Here’s some working code:

pconnector1 = testlib.square_array(ctypes.byref(con[0]))
connector1 = pconnector1.contents
print(type(connector1))

Notes:

Answered By: CristiFati
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.