3

I'm trying to execute a C function using Python ctypes, but I'm doing something wrong.

The C function was originally converted from MATLAB to C code using MATLAB coder. The function gives as output an array of undefined length, which depends on the input.

This is the MATLAB code:

function a = array_output(n)
%$codegen

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

end

And this is the obtained C code:

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;
};

Please, find at the end of my question an example implementation they provide.

I am trying to execute it using Python's ctype using the following code:

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)

The output data_struct contains the right size field (data_struct.size[1] is 50), but when I try to access data_struct.data[0], I get the following error:

ValueError: NULL pointer access

Can anyone help me understanding what I'm doing wrong here?

--

Example implementation (code snippets):

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;
  }
}
3
  • Your example is not reproducible. The definition of emxEnsureCapacity_real_T is missing. Commented Nov 16, 2020 at 0:58
  • If you have a working emxInitArray_real_T function written in C, why are you trying to rewrite it in Python instead of just calling it? Commented Nov 16, 2020 at 1:07
  • @JosephSible-ReinstateMonica I have edited my post. If you want I can share the whole output code Regarding your question of not using emxInitArray_real_T I honestly only want to use the main function array_output and do the remaining in Python. Commented Nov 16, 2020 at 1:19

1 Answer 1

1

The second argument of array_output is a emxArray_real_T *, but you're trying to pass the structure by value as if it were just a emxArray_real_T. To fix that, change dll.array_output.argtypes = [ctypes.c_double, DataStruct] to dll.array_output.argtypes = [ctypes.c_double, ctypes.POINTER(DataStruct)] and dll.array_output(50, data_struct) to dll.array_output(50, ctypes.byref(data_struct)).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.