I am trying to pass a variable size string inside a structure from Python into C code (or rather into a DLL/SO). The C-code is generated by Matlab Coder, so I am fairly limited to changing the C-code (although some tweaks can be made in Matlab to change the output).
I use ctypes in Python to creating structures with various elements and that works, and so does passing variables with strings of varying length. But, I can't get a varying length string inside a structure to work.
Passing a string works well when create a string input in Matlab the generated C-code. I pass the string via ctypes into the C type definitions as follows:
Matlab generated C-code:
/* Function Declarations */
extern double test_string_func(const char apa_vl_data[],
const int apa_vl_size[2]);
Python:
str2_in = "hyja"
bb = ct.c_char_p(str2_in.encode('utf-8'))
bb_sz = np.array([1, len(str2_in)]).astype(dtype=int)
bb_sz_p = bb_sz.ctypes.data
Initialise and make the call:
libC.test_string_func.argtypes = [ct.c_char_p, np.ctypeslib.ndpointer()]
dbl_out = libC.test_string_func(bb, bb_sz)
BUT, when I create a struct in Matlab (C and Python) I can't get the call to work:
C-code structure definitions:
/* Type Definitions */
#ifndef struct_emxArray_char_T_1x10
#define struct_emxArray_char_T_1x10
struct emxArray_char_T_1x10 {
char data[10];
int size[2];
};
#endif /* struct_emxArray_char_T_1x10 */
#ifndef typedef_emxArray_char_T_1x10
#define typedef_emxArray_char_T_1x10
typedef struct emxArray_char_T_1x10 emxArray_char_T_1x10;
#endif /* typedef_emxArray_char_T_1x10 */
#ifndef typedef_struct0_T
#define typedef_struct0_T
typedef struct {
double val;
emxArray_char_T_1x10 str;
} struct0_T;
#endif /* typedef_struct0_T */
from the header file:
extern double test_string_func(const struct0_T *apa_struct);
One approach of many that I have tried in Python:
class emxArray_char_T_1x10(ct.Structure):
""" creates a struct to match emxArray_char_T_1x10
C-code from Matlab:
/* Type Definitions */
#ifndef struct_emxArray_char_T_1x10
#define struct_emxArray_char_T_1x10
struct emxArray_char_T_1x10 {
char data[10];
int size[2];
}; """
_fields_ = [('data', ct.c_char_p),
('size', ct.POINTER(ct.c_int))]
class testInStruct(ct.Structure):
"""creates a struct to match struct in C file
C-code from Matlab:
/* Type Definitions */
typedef struct {
double val;
emxArray_char_T_1x10 str;
} struct0_T;
"""
_fields_ = [
("struct_input_val", ct.c_double),
("struct_input_str", emxArray_char_T_1x10),
]
# initialise struct data
input_string = "INPUT!!"
input_str = emxArray_char_T_1x10()
input_str.data = ct.c_char_p(input_string.encode('utf-8'))
# Pass a list of int
L = [1, len(input_string)]
arr = (ct.c_int * len(L))()
arr[:] = L
input_str.size = arr
sd = testInStruct()
sd.struct_input_val = 2.1
sd.struct_input_str = input_str
libC.test_string_func.argtypes = [ct.POINTER(testInStruct)]
dbl_out = libC.test_string_func(sd)
I have managed to make fairly complex structures work, with varying types and arrays as input. But the cause of the error passing a string in a struct eludes me.
Update: forgot to say that the C code runs when called in Python, but produce an incorrect length of the string inside the C-code and exit code when I try to "fprinf" the passed string inside the C-code.
Any idea what I might do wrong?
By the way, this is the standard format for structures with variable length variables, and those are easily dealt with:
struct emxArray_real_T
{
double *data;
int *size;
int allocatedSize;
int numDimensions;
boolean_T canFreeData;
};
The C struct
struct emxArray_char_T_1x10 {
char data[10];
int size[2];
};
should be matched with Ctypes in Python as
class emxArray_char_T_1x10(ct.Structure):
_fields_ = [('data', ct.c_char * 10),
('size', ct.POINTER(ct.c_int))]
Note the difference between char* a
and char b[10]
in this context. Yes, b
can be seen as a pointer to the first element of an array, but the structure contains the actual array, 10 chars, not one pointer to a char.