C TO Python через SWIG: не может получить 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 Queries p, и что очистка освобождает p. У меня нет никакого контроля над интерфейсом C - этот код широко используется в другом месте.
Я хотел бы назвать этот код из Python через Сковорода, но я не смог найти какие -либо хорошие примеры того, как передать указатель указателю - и получить его значение.
Я думаю, что правильным способом сделать это является использование Typemaps, поэтому я определил интерфейс, который автоматически вытекал бы для меня на стороне C:
%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 всегда сохранял свое начальное значение.
Любая помощь в выяснении правильного способа сделать это в SWIG была бы очень оценена. В противном случае я, вероятно, просто напишу обертку C ++ вокруг C -кода, которая мешает Python иметь дело с указателем. А затем обернуть что обертка с SWIG. Кто -нибудь остановите меня!
Редактировать:
Благодаря Йоренко, Теперь у меня есть следующий интерфейс 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);
}
}
Код Python, который использует этот интерфейс SWIG, выглядит следующим образом:
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) возвращает правильное значение. Я вставляю код отладки в Typemap для void*, и оба имеют одинаковое значение P! Позвоните идеям об этом?
Обновлять: Я решил использовать ctypes. Намного легче.
Решение
Я согласен с Theller, вместо этого вы должны использовать Ctypes. Это всегда проще, чем думать о Typemaps.
Но если вы мертвы на использовании 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);
}
Тогда ваш питон выглядит как:
import test
success, p = test.Operation()
theAnswer = 0.0f
if success:
theAnswer = test.GetFieldValue(p)
test.Cleanup(p)
Редактировать:
Я ожидаю void*
изящно спорить самостоятельно, но на всякий случай, вот код SWIG, чтобы обернуть void*
для getfieldvalue () и rainup ():
%typemap (in) void*
{
$1 = PyCObject_AsVoidPtr($input);
}
Другие советы
Хотели бы вы использовать CTYPE? Вот пример кода, который должен работать (хотя он не проверен):
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)