【问题标题】:Python ctypes definition for c structc 结构的 Python ctypes 定义
【发布时间】:2014-08-29 17:16:15
【问题描述】:

我正在尝试调用一些由 Matlab 编码器生成的 c 代码。 Matlab 使用一个名为 emxArray 的 c 结构来表示矩阵(在此处记录:http://www.mathworks.co.uk/help/fixedpoint/ug/c-code-interface-for-unbounded-arrays-and-structure-fields.html)。

struct emxArray_real_T
{
    double *data;
    int *size;
    int allocatedSize;
    int numDimensions;
    boolean_T canFreeData;
};

我几乎没有 ctypes 的经验,并且正在努力创建一个等效的结构,然后我可以使用它来来回传递向量到 c .so 中定义的函数

这是我迄今为止在 python 中所取得的成就......

class EmxArray(ctypes.Structure):
    """ creates a struct to match emxArray_real_T """

    _fields_ = [('data', ctypes.POINTER(ctypes.c_double)),
                ('size', ctypes.POINTER(ctypes.c_int)),
                ('allocatedSize', ctypes.c_int),
                ('numDimensions', ctypes.c_int),
                ('canFreeData', ctypes.c_bool)]    

但是,如果我这样定义:

data = (1.1, 1.2, 1.3, 1.4)
L = len(data)

x = EmxArray()
x.data = (ctypes.c_double * L)(*data)
x.data = (ctypes.c_int * 1)(L)    

这样就可以了

print len(x.data[:L]) 

for v in x.data[:L]: print v

编辑:我已经整理并采纳了Roland的建议,可以使用

data_out = x.data[:L]

我需要进一步调查,看看我是否可以成功地使用这个结构来传递和接收来自 c 代码的数据。

解决方案

按照 Roland 的建议实现 ctypes 结构不起作用 - 返回的值是垃圾,当我追求基于 python 的 lilbil 答案的实现时,我从来没有弄清楚为什么。我已经接受了这个答案,因为它是最接近的......

我将在这里记录我的解决方案,因为它可能会节省其他人像我一样浪费时间。

首先,我生成了一个简单的 matlab 函数,它将函数的每个元素自身相乘,并使用编码器将其编译为 c .so。这是使用 ctypes 导入到 python 的。代码如下...

import ctypes

LIBTEST = '..../dll/emx_test/'
EMX = ctypes.cdll.LoadLibrary(LIBTEST + 'emx_test.so')
init = EMX.emx_test_initialize()

# Create a data structure to hold the pointer generated by emxCreateWrapper...
class Opaque(ctypes.Structure):
    pass

# make some random data to pass in
data_in = [1., 2., 4., 8., 16.]
L = len(data_in)
# create an empty array of the same size for the output
data_ou = [0] * L

# put this in a ctypes array
ina = (ctypes.c_double * L)(*data_in)
oua = (ctypes.c_double * L)(*data_ou)
# create a pointer for these arrays & set the rows and columns of the matrix
inp = ctypes.pointer(ina)
oup = ctypes.pointer(oua)

nrows = ctypes.c_int(1)
ncols = ctypes.c_int(L)

# use EMX.emxCreateWrapper_real_T(double *data, int rows, int cols) to generate an emx wrapping the data 
# input arg types are a pointer to the data NOTE its not great to have to resize the ctypes.c_double but cant see another way
EMX.emxCreateWrapper_real_T.argtypes = [ctypes.POINTER(ctypes.c_double * L), ctypes.c_int, ctypes.c_int]
# a pointer to the emxArray is returned and stored in Opaque
EMX.emxCreateWrapper_real_T.restype = ctypes.POINTER(Opaque)
# use emxCreateWrapper
in_emx = EMX.emxCreateWrapper_real_T(inp, nrows, ncols)
ou_emx = EMX.emxCreateWrapper_real_T(oup, nrows, ncols)

# so now we have to emx's created and have pointers to them we can run the emx_test
# emx test looks like this in matlab
#
# function res = emx_test ( in )
#     res = in .* in;
# end
#
# so basically it multiplies each element of the matrix by itself
# 
# therefore [1., 2., 4., 8., 16.] should become [1., 4., 8., 64., 256.]

EMX.emx_test(in_emx, ou_emx)

# and voila...that's what we get
print 'In: ', ina[:L]
print 'Out:', oua[:L]

输出:

In: [1.0, 2.0, 4.0, 8.0, 16.0]
Out:[1.0, 4.0, 16.0, 64.0, 256.0]

感谢大家的宝贵时间和建议。

【问题讨论】:

  • 我觉得你可能不需要 sz...*sz))' parts of the EmxArray` 类。
  • 我这样做的原因是数据将是一个数组,但是如果我可以允许一个可变大小的数组,那将是最好的。

标签: python ctypes matlab-coder


【解决方案1】:

我并不精通 Python-C 接口,所以我的建议可能不太理想。我的猜测是,崩溃可能是因为 x->data 从未初始化,并且它指向的内存未分配。

在存在emxArray 参数的情况下,与 MATLAB Coder 生成的其他语言代码进行交互时,我采用的一种方法是手写一个提供更简单 API 的 C 接口函数。这减轻了需要在其他环境中构建emxArray 的负担(在我的特定情况下是Android Java)。如果生成的函数foo 接受并返回一个二维double 数组,那么类似下面的方法可以工作:

void foo(double *x, int *szx, double **y, int *szy);

此函数将获取一个指向输入数据及其大小的指针,并提供一个指向输出数据及其大小的指针。实现看起来像:

void foo(double *x, int *szx, double **y, int *szy) 
{
  emxArray_real_T *pEmx;
  emxArray_real_T *pEmy;

  /* Create input emxArray assuming 2-dimensional input */
  pEmx = emxCreateWrapper_real_T(x, szx[0], szx[1]);

  /* Create output emxArray (assumes that the output is not */
  /* written before allocation occurs) assuming 2-D output  */
  pEmy = emxCreateWrapper_real_T(NULL, 0, 0);

  /* Call generated code (call foobar_initialize/terminate elsewhere) */
  foobar(pEmx, pEmy);

  /* Unpack result - You may want to MALLOC storage in *y and */
  /* MEMCPY there alternatively                               */
  *y = pEmy->data;
  szy[0] = pEmy->size[0];
  szy[1] = pEmy->size[1];

  /* Clean up any memory allocated in the emxArrays (e.g. the size vectors) */
  emxDestroyArray_real_T(pEmx);
  emxDestroyArray_real_T(pEmy);
}

您应该能够更简单地从 Python 调用此函数,并根据需要传入所需的数据。

My other answer 在文件foobar_emxAPI.h 中提供了有关emxArray_* 函数的更多详细信息。

【讨论】:

  • 感谢您的全面回答。我认为这可能是要走的路,所以我今天将探索这条路线。我目前正在研究一个虚拟 matlab 脚本,它只需要一个 emx_array 平方并将其作为 emx_array 返回。我最终需要使用的库定义了多个函数,因此包装更加麻烦,我想知道是否可以定义一个通用函数,该函数接受一个向量并定义一个可以传递给函数的 emx_array,反之亦然。 ..
  • 这是可能的。库参数的类型和复杂性有何不同?通常,差异可能存在于参数的类型、复杂性或维数上。改变这些需要改变转换程序的行为。您的转换例程可以用 C++ 编写吗?如果是这样,它可以用不同类型的特化模板化,这些类型知道如何调用foo_emxApi.h中的正确函数。
  • 我的另一个想法是,您可以在 Python 中声明一个指向适当 emxArray 类型的指针。然后,您可以调用生成的 emxCreate*emxCreateWrapper* 函数来从 Python 代码初始化该指针。这应该最大限度地减少您需要编写的 C 的数量,并且这些函数具有带有数字参数的简单签名。这也可以让你在 Python 中编写向量 emxArray 转换函数。
【解决方案2】:

只需创建一个指针,然后分配数据;

import ctypes

class EmxArray(ctypes.Structure):
    """ creates a struct to match emxArray_real_T """

    _fields_ = [('data', ctypes.POINTER(ctypes.c_double)),
                ('size', ctypes.POINTER(ctypes.c_int)),
                ('allocatedSize', ctypes.c_int),
                ('numDimensions', ctypes.c_int),
                ('canFreeData', ctypes.c_bool)]

data = (1.3, 3.5, 2.7, 4.1)
L = len(data)

e = EmxArray()
e.data = (ctypes.c_double * L)(*data)
e.size = (ctypes.c_int * 1)(L)
# et cetera

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-17
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多