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];
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:
-
I used Python 2.7.10 on Win 10 pc064, but nothing here should be Python version or platform specific
-
There’s a typo with potential disastrous effects in your code. Check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati’s answer) for more details
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];
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:
-
I used Python 2.7.10 on Win 10 pc064, but nothing here should be Python version or platform specific
-
There’s a typo with potential disastrous effects in your code. Check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati’s answer) for more details