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?
-
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)
Jason Sundram : Thanks -- I'd prefer to use SWIG if I can, but if it doesn't work out, I may consider ctypes. -
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 allocatedvoid*
:%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 thevoid*
for GetFieldValue() and Cleanup():%typemap (in) void* { $1 = PyCObject_AsVoidPtr($input); }
Jason Sundram : 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?Jorenko : Weird. I added a typemap for that to my answer, just in case, though I haven't had a chance to test it...Jason Sundram : 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.Jorenko : 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.Jason Sundram : 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.Jorenko : How are you calling gvf()?Jason Sundram : I've updated the question with the calling code (at the bottom). Basically, I'm calling gvf and GetFieldValue() the same way.
0 comments:
Post a Comment