Using Python and ctypes to pass a variable length string inside a structure to c function (generated by Matlab Coder)

huangapple go评论84阅读模式
英文:

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 &quot;initialise struct data&quot; 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* achar 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_ = [(&#39;data&#39;, ct.c_char * 10),
                (&#39;size&#39;, 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.

huangapple
  • 本文由 发表于 2023年4月6日 22:41:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75950826.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定