2015-08-20 8 views
10

von einem anderen hier answer inspiriert, habe ich eine ctypes Funktion, die ich ctypeslib.ndpointer mit nenne:Wie kann ich mit ctypes null an eine externe Bibliothek übergeben, wobei ein Argument mit ctypeslib.ndpointer deklariert wird?

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

Die externe Funktion ist wie so erklärt:

void foo(cmplx_float *array, int *length) 

Mein Problem ist, dass ich anrufen möchten die Funktion zweimal. Das erste Mal möchte ich nullptr an das array Argument übergeben, damit ich die erforderliche Länge herausfinden kann. Dann das zweite Mal, werde ich in einer Reihe übergeben.

So tue ich so wie folgt aus:

lib.foo(None, length) 

Dies schlägt mit dem folgenden Fehler:

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

Ist es möglich für mich nullptr zu passieren?

+0

Ich bin ein wenig rostig, aber es sieht aus wie wenn Sie [erstellen Sie den Zeiger ohne Argumente] (https://docs.python.org/2/library/ctypes.html#pointers), sollte es einen Nullzeiger erstellen. –

+0

Die wahrscheinlich sauberste Option ist die Unterklasse des von 'ctypeslib.ndpointer' zurückgegebenen Typs, der selbst eine Unterklasse von [' ctypeslib._ndptr'] ist (https://github.com/numpy/numpy/blob/v1.9.2) /numpy/ctypeslib.py#L150). Überschreiben Sie die 'from_param'-Klassenmethode, um' obj' zurückzugeben, wenn es 'None' ist, andernfalls geben Sie das Ergebnis zurück, indem Sie die' from_param'-Methode des Parents aufrufen. – eryksun

+0

Alternativ könnten Sie 'argtypes' vorübergehend ändern, aber das gefällt mir nicht, da es nicht threadsicher ist. Sie könnten auch eine zweite Funktionszeigerinstanz erstellen, die 'argtypes = [c_void_p, POINTER (c_int)]' 'verwendet, aber zwei Funktionszeiger zu benötigen, ist schwerfällig. – eryksun

Antwort

4

Wenn (wie ich), die Sie mit mehreren verschiedenen Array-Typen zu tun hat, eine etwas schönere Abhilfe ndpointer selbst zu wickeln ist:

auf `ctypes`
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

Das sieht ziemlich glatt aus. Ich werde es ausprobieren, weil ich genau das gleiche mache wie du! –

1

Nach dem hervorragenden Rat von eryksun in einem Kommentar ich Unterklasse den Typ von ctypeslib.ndpointer zurückgegeben und implementiert eine überschriebene from_param. Dies ist ein wenig schwierig, da es mit Klassen-Factory-Techniken durchgeführt werden muss, da die von ctypeslib.ndpointer zurückgegebene Klasse von einer Fabrik hergestellt wird.

Der Code, den ich am Ende mit:

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

Vielen Dank für seine (wie immer) kompetente Beratung eryksun.

+0

Dieses Zwei-Pass-Call-Muster ist so üblich, dass '_ndptr.from_param' das eigentlich schon handhaben sollte. Erwägen Sie diese kleine Änderung der NumPy-Entwickler. – eryksun

+0

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

Verwandte Themen