views:

845

answers:

2

I have a C interface that looks like this (simplified):

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

which is used as follows:

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

You'll note that Operation() allocates the buffer p, that GetFieldValue queries p, and that Cleanup frees p. I don't have any control over the C interface -- that code is widely used elsewhere.

I'd like to call this code from Python via SWIG, but I was unable to find any good examples of how to pass a pointer to a pointer -- and retrieve its value.

I think the correct way to do this is by use of typemaps, so I defined an interface that would automatically dereference p for me on the C side:

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

However, I was unable to get the following python code to work:

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

After calling test.Operation(), p always kept its initial value of None.

Any help with figuring out the correct way to do this in SWIG would be much appreciated. Otherwise, I'm likely to just write a C++ wrapper around the C code that stops Python from having to deal with the pointer. And then wrap that wrapper with SWIG. Somebody stop me!

Edit:

Thanks to Jorenko, I now have the following SWIG interface:

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

The python code that uses this SWIG interface is as follows:

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)

Oddly, in the python code, test.GetFieldValue(p) returns gibberish, but test.gfv(p) returns the correct value. I've inserting debugging code into the typemap for void*, and both have the same value of p! The call Any ideas about that?

+2  A: 

Would you be willing to use ctypes? Here is sample code that should work (although it is untested):

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)
theller
Thanks -- I'd prefer to use SWIG if I can, but if it doesn't work out, I may consider ctypes.
Jason Sundram
+4  A: 

I agree with theller, you should use ctypes instead. It's always easier than thinking about typemaps.

But, if you're dead set on using swig, what you need to do is make a typemap for void** that RETURNS the newly allocated void*:

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

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

Then your python looks like:

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

Edit:

I'd expect swig to handle a simple by-value void* arg gracefully on its own, but just in case, here's swig code to wrap the void* for GetFieldValue() and Cleanup():

%typemap (in) void*
{
    $1 = PyCObject_AsVoidPtr($input);
}
Jorenko
Thanks for the swig. test.Operation() now works, but I'm having trouble calling test.GetFieldValue(p) from the python code. Do I need a typemap for void* as well?
Jason Sundram
Weird. I added a typemap for that to my answer, just in case, though I haven't had a chance to test it...
Jorenko
Thanks -- so this is a bit weird. Without the typemap, GetFieldValue claims that p is null. With the typemap, there are no complaints, but I get back a garbage value in theAnswer.
Jason Sundram
Well, keep in mind that the typemaps are actual C snippets that get inserted before/after the wrapped function call. If you like you can put debug prints, etc there to try to work out what the problem is.
Jorenko
Thanks -- Here's the interface (+ your stuff):% module Test%{ extern float GetFieldValue(void *p);}%%inline %{ float gfv(void *p){ return GetFieldValue(p);} %}Oddly, test.GetFieldValue(p) returns gibberish, but test.gfv(p) returns the correct value. But both have the same value of p!
Jason Sundram
Sorry that last comment was really hard to read -- I've updated the question so the interface code is readable.
Jason Sundram
How are you calling gvf()?
Jorenko
I've updated the question with the calling code (at the bottom). Basically, I'm calling gvf and GetFieldValue() the same way.
Jason Sundram