Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass null to an external library, using ctypes, with an argument declared with ctypeslib.ndpointer?

Inspired by another answer here, I have a ctypes function that I am calling using ctypeslib.ndpointer:

lib.foo.argtypes = [ctypeslib.ndpointer(np.complex64, ndim=1, flags='C'), POINTER(c_int)]

The external function is declared like so:

void foo(cmplx_float *array, int *length)

My problem is that I want to call the function twice. The first time I want to pass nullptr to the array argument, so that I can find out the required length. Then the second time, I will pass in a numpy array.

So I do so like this:

lib.foo(None, length)

This fails with the following error:

ctypes.ArgumentError: argument 1: : argument must be an ndarray

Is it possible for me to pass nullptr?

like image 872
David Heffernan Avatar asked Aug 20 '15 13:08

David Heffernan


2 Answers

If (like me) you are dealing with multiple different array types, a slightly nicer workaround is to wrap ndpointer itself:

import numpy as np
from numpy.ctypeslib import ndpointer

def wrapped_ndptr(*args, **kwargs):
  base = ndpointer(*args, **kwargs)
  def from_param(cls, obj):
    if obj is None:
      return obj
    return base.from_param(obj)
  return type(base.__name__, (base,), {'from_param': classmethod(from_param)})

ComplexArrayType = wrapped_ndptr(dtype=np.complex128, ndim=1, flags='C_CONTIGUOUS')
DoubleArrayType = wrapped_ndptr(dtype=np.float64, ndim=1, flags='C_CONTIGUOUS')
like image 135
ali_m Avatar answered Oct 01 '22 08:10

ali_m


Following the excellent advice from eryksun in a comment I subclassed the type returned by ctypeslib.ndpointer and implemented an overridden from_param. This is a little tricky as it has to be done using class factory techniques, because the class returned by ctypeslib.ndpointer is made by a factory.

The code that I ended up with:

_ComplexArrayTypeBase = numpy.ctypeslib.ndpointer(dtype=numpy.complex128, ndim=1,
    flags='C_CONTIGUOUS')

def _from_param(cls, obj):
    if obj is None:
        return obj
    return _ComplexArrayTypeBase.from_param(obj)

ComplexArrayType = type(
    'ComplexArrayType',
    (_ComplexArrayTypeBase,),
    {'from_param': classmethod(_from_param)}
)

Many thanks to eryksun for his (as always) expert advice.

like image 27
David Heffernan Avatar answered Oct 01 '22 10:10

David Heffernan