通过 SWIG C 到 Python:无法获取 void** 参数来保存其值
我有一个看起来像这样的 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* 的类型映射中,并且两者都具有相同的 p 值! 电话 有什么想法吗?
更新:我决定使用ctypes。 容易多了。
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?
Update: I've decided to use ctypes. MUCH easier.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我同意 ller 的观点,你应该使用 ctypes 代替。 它总是比考虑类型映射更容易。
但是,如果您执意要使用 swig,那么您需要做的是为
void**
创建一个类型映射,返回新分配的void*
:然后您的 python看起来像:
编辑:
我希望 swig 能够优雅地自行处理一个简单的按值
void*
arg,但为了以防万一,这里有 swig 代码来包装void*:
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*
:Then your python looks like:
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():你愿意使用ctypes吗? 这是应该可以工作的示例代码(尽管未经测试):
Would you be willing to use ctypes? Here is sample code that should work (although it is untested):