2009-02-12 7 views
7

J'ai une interface C qui ressemble à ceci (simplifié):C à Python via SWIG: ne peut pas obtenir les paramètres ** vides pour maintenir leur valeur

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

qui est utilisé comme suit:

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

Vous remarquerez que Operation() alloue le tampon p, que GetFieldValue interroge p, et que Cleanup libère p. Je n'ai aucun contrôle sur l'interface C - ce code est largement utilisé ailleurs. Je voudrais appeler ce code à partir de Python via SWIG, mais j'ai été incapable de trouver de bons exemples de la façon de passer un pointeur sur un pointeur - et de récupérer sa valeur.

Je pense que la bonne façon de le faire est par l'utilisation de typemaps, donc je défini une interface qui serait automatiquement déréférencer p pour moi du côté C:

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

Cependant, je ne pouvais pas obtenir le suivant le code python pour travailler:

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

Après avoir appelé test.Operation(), p a toujours gardé sa valeur initiale de None.

Toute aide pour trouver la bonne façon de le faire dans SWIG serait très appréciée. Sinon, il est probable que j'écrirai simplement un wrapper C++ autour du code C qui empêchera Python de gérer le pointeur. Et puis enveloppez ce wrapper avec SWIG. Quelqu'un m'arrête!

Edit:

Merci à Jorenko, j'ai maintenant l'interface SWIG suivante:

% 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); 
    } 
} 

Le code python qui utilise cette interface SWIG est la suivante:

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) 

Bizarrement, Dans le code python, test.GetFieldValue (p) renvoie gibberish, mais test.gfv (p) renvoie la valeur correcte. J'ai inséré du code de débogage dans le typemap pour void *, et les deux ont la même valeur de p! L'appel Des idées à ce sujet?

Mise à jour: J'ai décidé d'utiliser ctypes. Beaucoup plus facile.

+0

1. 'p' est en même temps 'vide **' et 'void *' dans votre code Python 2. 'None' est immuable donc' p' (un objet auquel il fait référence) ne changera pas quoi que ce soit (bien que vous puissiez lier 'p' à un objet différent (en utilisant' = ')). – jfs

Répondre

7

Je suis d'accord avec Theller, vous devez utiliser ctypes à la place. C'est toujours plus facile que de penser aux typemaps.

Mais, si vous êtes mort mis sur l'utilisation rasade, ce que vous devez faire est de faire un typemap pour void** qui renvoie le nouveau alloué void*:

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

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

Ensuite, votre python ressemble:

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

Edit:

Je pense rasade gérer un simple en valeur void* arg gracieusement sur son propre, mais juste au cas où, voici le code rasade pour envelopper le void* pour GetFieldValue() et de nettoyage():

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

Merci pour le swig. test.Operation() fonctionne maintenant, mais j'ai du mal à appeler test.GetFieldValue (p) à partir du code python. Ai-je besoin d'une typemap pour void *? –

+0

Bizarre. J'ai ajouté un typemap pour cela à ma réponse, juste au cas où, bien que je n'ai pas eu l'occasion de le tester ... – Jorenko

+0

Merci - donc c'est un peu bizarre. Sans le typemap, GetFieldValue prétend que p est nul. Avec le typemap, il n'y a pas de plaintes, mais je récupère une valeur de déchets dans la réponse. –

4

Seriez-vous prêt à utiliser ctypes? Voici un exemple de code qui devrait fonctionner (bien qu'il soit non testé):

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

Merci - Je préfère utiliser SWIG si je peux, mais si cela ne fonctionne pas, je peux envisager des ctypes. –

Questions connexes