2009-02-12 4 views
7

tengo una interfaz de C que tiene este aspecto (simplificado):C a través del pitón del TRAGO: no se puede obtener parámetros void ** para mantener su valor

extern bool Operation(void ** ppData); 
extern float GetFieldValue(void* pData); 
extern void Cleanup(p); 

que se utiliza de la siguiente manera:

void * p = NULL; 
float theAnswer = 0.0f; 
if (Operation(&p)) 
{ 
    theAnswer = GetFieldValue(p); 
    Cleanup(p); 
} 

Observará que Operation() asigna el buffer p, que GetFieldValue consulta p, y que Cleanup libera p. No tengo ningún control sobre la interfaz C, ese código es ampliamente utilizado en otros lugares.

Me gustaría llamar a este código desde Python a través de SWIG, pero no pude encontrar ningún buen ejemplo de cómo pasar un puntero a un puntero y recuperar su valor.

creo que la forma correcta de hacerlo es mediante el uso de typemaps, por lo que define una interfaz que lo haría automáticamente eliminar la referencia de p para mí en el lado C:

%typemap(in) void** { 
    $1 = (void**)&($input); 
} 

Sin embargo, no he podido conseguir el siguiente código Python para trabajar:

import test 
p = None 
theAnswer = 0.0f 
if test.Operation(p): 
    theAnswer = test.GetFieldValue(p) 
    test.Cleanup(p) 

Después de llamar test.Operation(), p siempre mantuvo su valor inicial de Nada.

Cualquier ayuda para averiguar la forma correcta de hacer esto en SWIG sería muy apreciada. De lo contrario, es probable que solo escriba un contenedor C++ alrededor del código C que impida que Python tenga que lidiar con el puntero. Y luego envuelva esa envoltura con SWIG. ¡Que alguien me pare!

Editar:

Gracias a Jorenko, ahora tengo la siguiente interfaz SWIG:

% module Test 
%typemap (in,numinputs=0) void** (void *temp) 
{ 
    $1 = &temp; 
} 

%typemap (argout) void** 
{ 
    PyObject *obj = PyCObject_FromVoidPtr(*$1, Cleanup); 
    $result = PyTuple_Pack(2, $result, obj); 
} 
%{ 
extern bool Operation(void ** ppData); 
extern float GetFieldValue(void *p); 
extern void Cleanup(void *p); 
%} 
%inline 
%{ 
    float gfv(void *p){ return GetFieldValue(p);} 
%} 

%typemap (in) void* 
{ 
    if (PyCObject_Check($input)) 
    { 
     $1 = PyCObject_AsVoidPtr($input); 
    } 
} 

El código Python que utiliza esta interfaz SWIG es el siguiente:

import test 
success, p = test.Operation() 
if success: 
    f = test.GetFieldValue(p) # This doesn't work 
    f = test.gvp(p) # This works! 
    test.Cleanup(p) 

Curiosamente, en el código python, test.GetFieldValue (p) devuelve galimatías, pero test.gfv (p) devuelve el valor correcto. He insertado código de depuración en el mapa de tipos para void *, y ambos tienen el mismo valor de p! La llamada ¿Alguna idea sobre eso?

Actualización: He decidido usar ctypes. Más fácil.

+0

1. 'p 'es al mismo tiempo 'vacío **' y 'void *' en el código Python 2. 'Ninguno' es inmutable, por lo tanto' p' (un objeto al que se refiere) no cambiará sin importar qué (aunque puedes unir 'p' a un objeto diferente (usando' = ')). – jfs

Respuesta

7

Estoy de acuerdo con Theller, se debe utilizar ctypes lugar. Siempre es más fácil que pensar en mapas de tipos.

Pero, si usted está empeñado en el uso de trago, lo que hay que hacer es hacer una typemap para void** que devuelve el recién asignado void*:

%typemap (in,numinputs=0) void** (void *temp) 
{ 
    $1 = &temp; 
} 

%typemap (argout) void** 
{ 
    PyObject *obj = PyCObject_FromVoidPtr(*$1); 
    $result = PyTuple_Pack(2, $result, obj); 
} 

A continuación, el pitón se ve como:

import test 
success, p = test.Operation() 
theAnswer = 0.0f 
if success: 
    theAnswer = test.GetFieldValue(p) 
    test.Cleanup(p) 

Editar:

yo esperaría trago para manejar un sencillo por valor void* arg con gracia en su propia, pero por si acaso, aquí está el código trago para envolver el void* para GetFieldValue() y Limpieza():

%typemap (in) void* 
{ 
    $1 = PyCObject_AsVoidPtr($input); 
} 
+0

Gracias por el trago. test.Operation() ahora funciona, pero tengo problemas para llamar a test.GetFieldValue (p) desde el código python. ¿Necesito también un mapa de tipos para void *? –

+0

Extraño. Agregué un mapa de tipo para eso a mi respuesta, por si acaso, aunque no he tenido la oportunidad de probarlo ... – Jorenko

+0

Gracias - así que esto es un poco raro. Sin el mapa de tipos, GetFieldValue afirma que p es nulo. Con el mapa de tipos, no hay quejas, pero obtengo un valor de basura en la respuesta. –

4

¿Estaría dispuesto a usar ctypes? Aquí es código de ejemplo que deben trabajar (aunque no se ha probado):

from ctypes import * 

test = cdll("mydll") 

test.Operation.restype = c_bool 
test.Operation.argtypes = [POINTER(c_void_p)] 

test.GetFieldValue.restype = c_float 
test.GetFieldValue.argtypes = [c_void_p] 

test.Cleanup.restype = None 
test.Cleanup.argtypes = [c_void_p] 

if __name__ == "__main__": 
    p = c_void_p() 
    if test.Operation(byref(p)): 
     theAnswer = test.GetFieldValue(p) 
     test.Cleanup(p) 
+0

Gracias - Prefiero usar SWIG si puedo, pero si no funciona, puedo considerar ctypes. –

Cuestiones relacionadas