英文:
Using Python and ctypes to pass a variable length string inside a structure to c function (generated by Matlab Coder)
问题
I'm here to help with the Chinese translation:
我正在尝试将一个可变大小的字符串传递到一个从Python生成的C代码中的结构体中(或者更确切地说,传递到一个DLL/SO文件中)。C代码是由Matlab Coder生成的,所以我在更改C代码方面受到了一定的限制(尽管可以在Matlab中进行一些调整以更改输出)。
我在Python中使用ctypes来创建具有各种元素的结构体,这样做是有效的,传递具有不同长度的字符串也是有效的。但是,我无法让结构体内部的可变长度字符串工作。
当我在Matlab中创建一个字符串输入时,将字符串传递给由Matlab生成的C代码时,这个方法运行良好。我通过ctypes将字符串传递到C类型定义中,如下所示:
Matlab生成的C代码:
/* 函数声明 */
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
初始化并调用:
libC.test_string_func.argtypes = [ct.c_char_p, np.ctypeslib.ndpointer()]
dbl_out = libC.test_string_func(bb, bb_sz)
但是,当我在Matlab(C和Python中)创建一个结构体时,我无法让调用工作:
C代码结构体定义:
/* 类型定义 /
#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 */
来自头文件的部分:
extern double test_string_func(const struct0_T *apa_struct);
我在Python中尝试的一种方法:
class emxArray_char_T_1x10(ct.Structure):
"""创建一个与emxArray_char_T_1x10匹配的结构体
来自Matlab的C代码:
/* 类型定义 */
#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):
"""创建一个与C文件中的结构体匹配的结构体
来自Matlab的C代码:
/* 类型定义 */
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),
]
初始化结构体数据
input_string = "INPUT!!"
input_str = emxArray_char_T_1x10()
input_str.data = ct.c_char_p(input_string.encode('utf-8'))
传递一个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)
我设法使得相当复杂的结构体工作,其中包括不同类型和数组作为输入。但是在结构体中传递字符串的错误原因让我困惑不已。
**更新:我忘了说,当在Python中调用时,C代码会运行,但会产生一个错误的字符串长度,并在我尝试在C代码内部“fprinf”传递的字符串时退出。**
你知道我可能做错了什么吗?
顺便说一句,这是具有可变长度变量的结构体的标准格式,这些变量很容易处理:
struct emxArray_real_T
{
double *data;
int *size;
int allocatedSize;
int numDimensions;
boolean_T canFreeData;
};
英文:
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;
};
答案1
得分: 2
The change suggested by Chris Luengo works, but impose some additional modifications to get everything to work. It goes as follows:
The emxArray_char_T_1x10 section should be:
```python
class emxArray_char_T_1x10(ct.Structure):
_fields_ = [('data', ct.c_char * 10),
('size', ct.c_int * 2)]]
and the "initialise struct data" section should be reworked as follows:
# initialise struct data
input_string = "INPUT!!"
input_str = emxArray_char_T_1x10()
arr_1 = bytes(input_string, 'utf-8')
input_str.data = arr_1
<details>
<summary>英文:</summary>
The change suggested by Chris Luengo works, but impose some additional modifications to get everything to work. It goes as follows:
The emxArray_char_T_1x10 section should be:
class emxArray_char_T_1x10(ct.Structure):
fields = [('data', ct.c_char * 10),
('size', ct.c_int * 2))]
and the "initialise struct data" section should be reworked as follows:
initialise struct data
input_string = "INPUT!!"
input_str = emxArray_char_T_1x10()
arr_1 = bytes(input_string, 'utf-8')
input_str.data = arr_1
</details>
# 答案2
**得分**: 1
```python
class emxArray_char_T_1x10(ct.Structure):
_fields_ = [('data', ct.c_char * 10),
('size', ct.POINTER(ct.c_int))]
请注意在这个上下文中char* a
和char b[10]
之间的区别。是的,b
可以被看作是指向数组第一个元素的指针,但结构体包含了实际的数组,即10个字符,而不是一个指向字符的指针。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论