2015-08-20 36 views
10

Zainspirowany innym answer tutaj, mam funkcję ctypes że wzywam użyciu ctypeslib.ndpointer:Jak mogę przekazać wartość null do biblioteki zewnętrznej, używając ctypes, z argumentem zadeklarowanym przy pomocy ctypeslib.ndpointer?

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

Funkcja zewnętrzna jest zadeklarowany tak:

void foo(cmplx_float *array, int *length) 

Mój problem jest, że chcę zadzwonić funkcja dwa razy. Po raz pierwszy chcę przekazać nullptr do argumentu array, dzięki czemu mogę znaleźć wymaganą długość. Za drugim razem przejdę w tablicy numpy.

więc zrobić tak:

lib.foo(None, length) 

ten nie powiedzie się z powodu następującego błędu:

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

Czy jest możliwe dla mnie przechodzą nullptr?

+0

jestem trochę zardzewiały na 'ctypes', ale wygląda na to, czy ty [utwórz wskaźnik bez żadnych argumentów] (https://docs.python.org/2/library/ctypes.html#pointers), powinien utworzyć wskaźnik zerowy. –

+0

Prawdopodobnie najbardziej czystą opcją jest podklasy typ zwracany przez 'ctypeslib.ndpointer', który sam jest podklasą [' ctypeslib._ndptr'] (https://github.com/numpy/numpy/blob/v1.9.2/numpy/ctypeslib.py#L150). Zastąp swoją metodę klasy 'from_param', aby zwrócić' obj', jeśli jest 'Brak', w przeciwnym razie zwróci wynik wywołania metody rodzica' from_param'. – eryksun

+0

Alternatywnie możesz tymczasowo zmodyfikować 'argtypes', ale nie podoba mi się to, ponieważ nie jest bezpieczne dla wątków. Można również utworzyć drugą instancję wskaźnika funkcji, która używa 'argtypes = [c_void_p, POINTER (c_int)]', ale wymaganie dwóch wskaźników funkcji jest niezdarne. – eryksun

Odpowiedz

4

Jeśli (jak ja) masz do czynienia z wieloma różnymi typami macierzy, nieco ładniejszy obejście jest owinąć ndpointer się:

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

Wygląda całkiem sprytnie. Wypróbuję to, ponieważ robię dokładnie to samo co ty! –

1

Po doskonałej porady z eryksun w komentarzu podklasowałem typ zwrócony przez ctypeslib.ndpointer i zaimplementowałem nadpisany from_param. Jest to trochę skomplikowane, ponieważ musi być wykonane przy użyciu technik klasowych, ponieważ klasa zwrócona przez ctypeslib.ndpointer jest wytwarzana przez fabrykę.

Kod że skończyło się z:

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

Wiele dzięki eryksun za (jak zawsze) porad ekspertów.

+0

Ten dwuprzebiegowy wzorzec wywołania jest tak powszechny, że '_ndptr.from_param' powinien już to zrobić. Rozważ sugerowanie tej małej zmiany programistom NumPy. – eryksun

+0

@eryksun done: https://github.com/numpy/numpy/issues/6239 –