C到Python通过SWIG:无法获取void **参数来保存它们的值

时间:2009-02-12 08:04:16

标签: python c swig wrapping

我有一个C接口,看起来像这样(简化):

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

使用如下:

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

你会注意到Operation()分配缓冲区p,GetFieldValue查询p,以及Cleanup释放p。我对C接口没有任何控制权 - 该代码在其他地方被广泛使用。

我想通过SWIG从Python调用此代码,但我无法找到如何将指针传递给指针的任何好例子 - 并检索其值。

我认为正确的方法是使用类型地图,所以我定义了一个接口,它会在C端为我自动取消引用p:

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

但是,我无法使用以下python代码:

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

调用test.Operation()后,p始终保持其初始值None。

任何帮助确定在SWIG中执行此操作的正确方法都将非常感激。否则,我可能只是围绕C代码编写一个C ++包装器,阻止Python处理指针。然后使用SWIG包装 包装器。有人阻止我!

编辑:

感谢Jorenko,我现在拥有以下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);
    }
}

使用此SWIG接口的python代码如下:

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) 

奇怪的是,在python代码中,test.GetFieldValue(p)返回乱码,但test.gfv(p)返回正确的值。我将调试代码插入到void *的typemap中,并且两者都具有相同的p值!电话有任何想法吗?

更新:我决定使用ctypes。更容易。

2 个答案:

答案 0 :(得分:7)

我同意theller,你应该使用ctypes。它总是比考虑类型图更容易。

但是,如果您已经决定使用swig,那么您需要做的是为void**制作一个返回新分配的void*

}的

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

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

然后你的python看起来像:

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

编辑:

我希望swig能够自己优雅地处理一个简单的按值void* arg,但为了以防万一,这里的swig代码用于包装GetFieldValue()和Cleanup()的void*

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

答案 1 :(得分:4)

你愿意使用ctypes吗?以下是应该有效的示例代码(尽管未经测试):

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)