Search code examples
pythonctypes

ArgumentError:expected LP_c_float instance instead of pointer to LP_c_float_Array_50000


I have a function in dll file that takes float pointer as one of argument(argument 9: float *result).

void generate_codebook(int *nodestatus, int *nofnode, int *noftree, int *terminal,  int *nofterminal, int *nofobservations, int *total, int *nofseries,  float *result)

Here is the python code where I am facing issue:

nofseries=c_int(len(nofobservations))
noftree=c_int(terminal.shape[1])
nofnode=c_int(nodestatus.shape[0])
total=c_int(np.sum(nofobservations,dtype=np.int64))
nofentry=ctypes.POINTER(ctypes.c_float *(len(nofobservations)*nofterminal*terminal.shape[1]))()
mydll.generate_codebook.argtypes = [POINTER(c_int), POINTER(c_int), POINTER(c_int), POINTER(c_int),
                                  POINTER(c_int), POINTER(c_int), POINTER(c_int), POINTER(c_int), POINTER(c_float)]
result=mydll.generate_codebook(nodestatus.ctypes.data_as(ctypes.POINTER(ctypes.c_int)),
                               nofnode,noftree,terminal.ctypes.data_as(ctypes.POINTER(ctypes.c_int)),
                               c_int(nofterminal),
                               nofobservations.ctypes.data_as(ctypes.POINTER(ctypes.c_int)),total,
                               nofseries,
                              ctypes.byref(nofentry))

While calling generate_codebook function I am facing argument error in last argument where LP_c_float instance is expected. Below is the error:

<ipython-input-28-f73a7383211e> in generatecodebook(nodestatus, terminal, nofterminal, nofobservations)
 16                                    nofobservations.ctypes.data_as(ctypes.POINTER(ctypes.c_int)),total,
 17                                    nofseries,
---> 18                                   ctypes.byref(nofentry))

ArgumentError: argument 9: <class 'TypeError'>: expected LP_c_float instance instead of pointer to LP_c_float_Array_50000

I went through this question's solution but not able to resolve error. Thank you in advance!


Solution

  • Your nofentry value is a pointer to an array of floats, while generate_codebook expects a pointer to float.
    CTypes can't do such a conversion automatically, so it has to be performed manually (using [Python.Docs]: ctypes.cast(obj, type)).

    Example:

    >>> import ctypes
    >>>
    >>> dim = 100
    >>>
    >>> FloatArr100 = ctypes.c_float * dim
    >>> FloatArr100Ptr = ctypes.POINTER(FloatArr100)
    >>>
    >>> float_arr = FloatArr100(*range(dim))
    >>> float_arr[4], float_arr[38], float_arr[99]
    (4.0, 38.0, 99.0)
    >>>
    >>> float_arr_ptr = ctypes.pointer(float_arr)  # This is the equivalent of your `nofentry`
    >>> float_arr_ptr
    <__main__.LP_c_float_Array_100 object at 0x000001921ED85A48>
    >>> type(float_arr_ptr) is FloatArr100Ptr
    True
    >>>
    >>> float_ptr = ctypes.cast(float_arr, ctypes.POINTER(ctypes.c_float))  # This is what you should do
    >>>
    >>> float_ptr
    <__main__.LP_c_float object at 0x000001921ED859C8>
    >>> float_ptr[4], float_ptr[38], float_ptr[99]
    (4.0, 38.0, 99.0)
    

    Translated to your code:

    1. Change nofentry definition to:

      nofentry = (ctypes.c_float * (len(nofobservations) * nofterminal * terminal.shape[1]))()  # Notice dropping `ctypes.POINTER`
      
    2. When invoking mydll.generate_codebook, replace

      ctypes.byref(nofentry)
      

      with

      ctypes.cast(nofentry, ctypes.POINTER(ctypes.c_float))
      

      so at the end it will look like:

      result = mydll.generate_codebook(
          nodestatus.ctypes.data_as(ctypes.POINTER(ctypes.c_int)),
          nofnode, noftree, terminal.ctypes.data_as(ctypes.POINTER(ctypes.c_int)),
          c_int(nofterminal),
          nofobservations.ctypes.data_as(ctypes.POINTER(ctypes.c_int)),
          total, nofseries,
          ctypes.cast(nofentry, ctypes.POINTER(ctypes.c_float)))
      

    Also mentioning [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer).