Have:
struct STRUCT {
float x;
float y;
float z;
};
struct BIG_STRUCT {
int value;
int array_count;
struct STRUCT *array;
};
struct BIG_STRUCT *allocate_bs(struct BIG_STRUCT *bs);
struct BIG_STRUCT *do_something_bs(struct BIG_STRUCT *bs);
void free_bs(struct BIG_STRUCT *bs);
array_count - number of array elements.
import ctypes
from ctypes import *
class CStruct(ctypes.Structure):
_fields_ = [
('x', ctypes.c_float),
('y', ctypes.c_float),
('z', ctypes.c_float)
]
class CBigStruct(ctypes.Structure):
_fields_ = [
('value', ctypes.c_int),
('array_count', ctypes.c_int),
('array', ctypes.POINTER(ctypes.POINTER(CStruct)))
]
if __name__ == '__main__':
libc = ctypes.CDLL("./library/libTestPython.so.0.0")
c_struct = CStruct
libc.allocate_bs.argtypes = [ctypes.POINTER(ctypes.POINTER(CStruct))]
libc.allocate_bs.restype = ctypes.POINTER(ctypes.POINTER(CStruct))
result = libc.allocate_bs(ctypes.byref(c_struct))
libc.do_something_bs.argtypes = [ctypes.POINTER(ctypes.POINTER(CStruct))]
libc.do_something_bs.restype = ctypes.POINTER(ctypes.POINTER(CStruct))
result = libc.do_something_bs(c_struct)
libc.free_bs.argtypes = [ctypes.POINTER(ctypes.POINTER(CStruct))]
libc.free_bs.restype = ctypes.c_int
libc.free_bs(c_struct)
Error
Traceback (most recent call last):
File "./example_struct.py", line 39, in <module>
result = libc.allocate_bs(ctypes.byref(c_struct))
TypeError: byref() argument must be a ctypes instance, not '_ctypes.PyCStructType'
without ctypes.bref() in string result = libc.allocate_bs(c_struct) I have an error
Traceback (most recent call last):
File "./example_struct.py", line 39, in <module>
result = libc.allocate_bs(c_struct)
ctypes.ArgumentError: argument 1: <class 'TypeError'>: expected LP_LP_CStruct instance instead of _ctypes.PyCStructType
What is the problem? How to solve it?
POINTER(POINTER(CStruct)
should be POINTER(CBigStruct)
. The function interfaces have BIG_STRUCT*
not STRUCT**
.c_struct = CStruct
doesn't create an instance, but just gives a new name to the type. Use c_struct = CStruct()
to create an instance, but this isn't really needed if you return the allocated pointer (see #3).struct BIG_STRUCT *allocate_bs(struct BIG_STRUCT *bs);
doesn't make sense, it should be struct BIG_STRUCT *allocate_bs()
and return the allocated pointer.Here's something that works:
// test.c -> test.dll
// Compiled with Microsoft compiler: cl /LD /W4 test.c
#include <stdlib.h>
#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif
struct STRUCT {
float x;
float y;
float z;
};
struct BIG_STRUCT {
int value;
int array_count;
struct STRUCT *array;
};
// Allocate and return an intialized structure
API struct BIG_STRUCT *allocate_bs() {
struct BIG_STRUCT* bs = malloc(sizeof(struct BIG_STRUCT));
bs->value = 0;
bs->array_count = 0;
bs->array = NULL;
return bs;
}
// fill out the structure with an allocated array and some data
API void do_something_bs(struct BIG_STRUCT *bs) {
bs->value = 7;
bs->array_count = 2;
bs->array = malloc(2 * sizeof(struct STRUCT));
for(int i = 0; i < 2; ++i) {
bs->array[i].x = (float)i;
bs->array[i].y = (float)(i+1);
bs->array[i].z = (float)(i+2);
}
}
API void free_bs(struct BIG_STRUCT *bs) {
if(bs != NULL) {
if(bs->array != NULL)
free(bs->array);
free(bs);
}
}
# test.py
import ctypes as ct
class CStruct(ct.Structure):
_fields_ = (('x', ct.c_float),
('y', ct.c_float),
('z', ct.c_float))
# So the structure can display itself when printed
def __repr__(self):
return f'CStruct(x={self.x}, y={self.y}, z={self.z})'
class CBigStruct(ct.Structure):
_fields_ = (('value', ct.c_int),
('array_count', ct.c_int),
('array', ct.POINTER(CStruct)))
# slicing the pointer with the correct length will return a Python list
def __repr__(self):
return f'CBigStruct(value={self.value}, array={self.array[:self.array_count]})'
libc = ct.CDLL('./test')
libc.allocate_bs.argtypes = ()
libc.allocate_bs.restype = ct.POINTER(CBigStruct)
libc.do_something_bs.argtypes = ct.POINTER(CBigStruct),
libc.do_something_bs.restype = None
libc.free_bs.argtypes = ct.POINTER(CBigStruct),
libc.free_bs.restype = None
bs = libc.allocate_bs()
print(bs.contents)
libc.do_something_bs(bs)
print(bs.contents)
libc.free_bs(bs)
Output:
CBigStruct(value=0, array=[])
CBigStruct(value=7, array=[CStruct(x=0.0, y=1.0, z=2.0), CStruct(x=1.0, y=2.0, z=3.0)])