C на Python через SWIG: невозможно получить void ** параметры для хранения их значения - PullRequest
7 голосов
/ 12 февраля 2009

У меня есть интерфейс 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, а очистка освобождает p. Я не имею никакого контроля над интерфейсом C - этот код широко используется в других местах.

Я бы хотел вызвать этот код из Python через SWIG , но мне не удалось найти хороших примеров того, как передать указатель на указатель и получить его значение.

Я думаю, что правильный способ сделать это - использовать карты типов, поэтому я определил интерфейс, который будет автоматически разыменовывать p для меня на стороне 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 всегда сохранял свое начальное значение None.

Буду признателен за любую помощь в поиске правильного способа сделать это в SWIG. В противном случае, я, скорее всего, просто напишу оболочку C ++ вокруг кода C, которая мешает Python иметь дело с указателем. А затем оберните эту оболочку SWIG. Кто-нибудь остановит меня!

Edit:

Благодаря 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);
    }
}

Код 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) возвращает правильное значение. Я вставляю код отладки в карту типов для void *, и оба имеют одинаковое значение p! Звонок Есть идеи по этому поводу?

Обновление: Я решил использовать ctypes. НАМНОГО проще.

Ответы [ 2 ]

7 голосов
/ 12 февраля 2009

Я согласен с 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);
}

Тогда ваш питон выглядит так:

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

Edit:

Я бы ожидал, что swig будет обрабатывать простое значение-значение void*, аргументированное изящное само по себе, но на всякий случай вот код swig для переноса void* для GetFieldValue () и Cleanup ():

%typemap (in) void*
{
    $1 = PyCObject_AsVoidPtr($input);
}
4 голосов
/ 12 февраля 2009

Хотели бы вы использовать 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)
...