Как передать значение null во внешнюю библиотеку, используя ctypes, с аргументом, объявленным ctypeslib.ndpointer?

Вдохновленный другим answer здесь, у меня есть функция ctypes, которую я вызываю, используя ctypeslib.ndpointer:

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

Внешняя функция объявляется следующим образом:

void foo(cmplx_float *array, int *length)

Моя проблема в том, что я хочу дважды вызвать эту функцию. В первый раз я хочу передать nullptr в аргумент array, чтобы я мог узнать нужную длину. Затем во второй раз я перейду в массив numpy.

Итак, я делаю так вот так:

lib.foo(None, length)

Это не выполняется со следующей ошибкой:

ctypes.ArgumentError: аргумент 1:: аргумент должен быть ndarray

Можно ли передать nullptr?

Ответ 1

Если (как и я) вы имеете дело с несколькими типами массивов, немного более удобное обходное решение - обернуть ndpointer себя:

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')

Ответ 2

Следуя отличным советам eryksun в комментарии, я подклассифицировал тип, возвращаемый ctypeslib.ndpointer, и реализовал переопределенный from_param. Это немного сложно, так как это нужно сделать с помощью методов класса factory, потому что класс, возвращаемый ctypeslib.ndpointer, выполняется с помощью factory.

Код, с которым я закончил:

_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)}
)

Большое спасибо eryksun за его (как всегда) совет экспертов.