【问题标题】:Trouble using Python ctypes to run C dll function (array output with unknown size)使用 Python ctypes 运行 C dll 函数时遇到问题(数组输出大小未知)
【发布时间】:2021-02-27 06:03:13
【问题描述】:

我正在尝试使用 Python ctypes 执行 C 函数,但我做错了。

最初使用 MATLAB coder 将 C 函数从 MATLAB 转换为 C 代码。该函数给出一个未定义长度的数组作为输出,这取决于输入。

这是 MATLAB 代码:

function a = array_output(n)
%$codegen

if n > 2*pi
    a = 1:n;
else
    a = [1,2,3];
end

end

这是获得的C代码:

void array_output(double n, emxArray_real_T *a)
{
  int i;
  int loop_ub;
  if (n > 6.2831853071795862) {
    i = a->size[0] * a->size[1];
    a->size[0] = 1;
    loop_ub = (int)floor(n - 1.0);
    a->size[1] = loop_ub + 1;
    emxEnsureCapacity_real_T(a, i);
    for (i = 0; i <= loop_ub; i++) {
      a->data[i] = (double)i + 1.0;
    }
  } else {
    i = a->size[0] * a->size[1];
    a->size[0] = 1;
    a->size[1] = 3;
    emxEnsureCapacity_real_T(a, i);
    a->data[0] = 1.0;
    a->data[1] = 2.0;
    a->data[2] = 3.0;
  }
}

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

请在我的问题末尾找到他们提供的示例实现。

我正在尝试使用以下代码使用 Python 的 ctype 执行它:

dll = ctypes.cdll.LoadLibrary(dll_path)


class DataStruct(ctypes.Structure):
    _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)
    ]


array = ctypes.POINTER(ctypes.c_double)()
size = numpy.array([0, 0]).ctypes.data_as(ctypes.POINTER(ctypes.c_int))
allocatedSize = 0
numDimensions = 2
canFreeData = True

data_struct = DataStruct(array, size, allocatedSize, numDimensions, canFreeData)

dll.array_output.argtypes = [ctypes.c_double, DataStruct]
dll.array_output.restype = None

dll.array_output(50, data_struct)

输出data_struct 包含正确的大小字段(data_struct.size[1] 为 50),但是当我尝试访问 data_struct.data[0] 时,出现以下错误:

ValueError: NULL pointer access

谁能帮我理解我在这里做错了什么?

--

示例实现(代码sn-ps):

void main(){
    // pseudo-code here
    emxArray_real_T *a;
    emxInitArray_real_T(&a, 2);
    
    /* Initialize function 'array_output' input arguments. */
    /* Call the entry-point 'array_output'. */
    array_output(5.0, a);
}

void emxInitArray_real_T(emxArray_real_T **pEmxArray, int numDimensions)
{
  emxInit_real_T(pEmxArray, numDimensions);
}

void emxInit_real_T(emxArray_real_T **pEmxArray, int numDimensions)
{
  emxArray_real_T *emxArray;
  int i;
  *pEmxArray = (emxArray_real_T *)malloc(sizeof(emxArray_real_T));
  emxArray = *pEmxArray;
  emxArray->data = (double *)NULL;
  emxArray->numDimensions = numDimensions;
  emxArray->size = (int *)malloc(sizeof(int) * numDimensions);
  emxArray->allocatedSize = 0;
  emxArray->canFreeData = true;
  for (i = 0; i < numDimensions; i++) {
    emxArray->size[i] = 0;
  }
}


void emxEnsureCapacity_real_T(emxArray_real_T *emxArray, int oldNumel)
{
  int newNumel;
  int i;
  void *newData;
  if (oldNumel < 0) {
    oldNumel = 0;
  }

  newNumel = 1;
  for (i = 0; i < emxArray->numDimensions; i++) {
    newNumel *= emxArray->size[i];
  }

  if (newNumel > emxArray->allocatedSize) {
    i = emxArray->allocatedSize;
    if (i < 16) {
      i = 16;
    }

    while (i < newNumel) {
      if (i > 1073741823) {
        i = MAX_int32_T;
      } else {
        i *= 2;
      }
    }

    newData = calloc((unsigned int)i, sizeof(double));
    if (emxArray->data != NULL) {
      memcpy(newData, emxArray->data, sizeof(double) * oldNumel);
      if (emxArray->canFreeData) {
        free(emxArray->data);
      }
    }

    emxArray->data = (double *)newData;
    emxArray->allocatedSize = i;
    emxArray->canFreeData = true;
  }
}

【问题讨论】:

  • 您的示例不可重现。缺少emxEnsureCapacity_real_T 的定义。
  • 如果你有一个用 C 编写的 emxInitArray_real_T 函数,你为什么要尝试用 Python 重写它而不是直接调用它?
  • @JosephSible-ReinstateMonica 我已经编辑了我的帖子。如果你愿意,我可以分享整个输出代码关于你不使用emxInitArray_real_T的问题,老实说,我只想使用主函数array_output,其余的在Python中完成。

标签: python c++ c matlab ctypes


【解决方案1】:

array_output 的第二个参数是 emxArray_real_T *,但您试图按值传递结构,就好像它只是一个 emxArray_real_T。要解决此问题,请将 dll.array_output.argtypes = [ctypes.c_double, DataStruct] 更改为 dll.array_output.argtypes = [ctypes.c_double, ctypes.POINTER(DataStruct)] 并将 dll.array_output(50, data_struct) 更改为 dll.array_output(50, ctypes.byref(data_struct))

【讨论】:

    猜你喜欢
    • 2017-05-21
    • 1970-01-01
    • 2010-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-17
    • 2021-11-03
    • 1970-01-01
    相关资源
    最近更新 更多