SWIG: от C ++ до C #, указатель на указатель маршаллинга - PullRequest
3 голосов
/ 19 марта 2010

У меня есть устаревший код, который я хочу перенести на C #. Я не могу изменить код C ++, я просто должен делать то, что мне дают.

Итак, ситуация. Я использую SwIG, и я наткнулся на эту функцию:

void MarshalMe(int iNum, FooClass** ioFooClassArray);

Если бы я запустил SWIG по этому поводу, он не знал бы, что делать с массивом, поэтому он создаст SWIGTYPE_p_pFooClass. Справедливо! Код C # для этого будет выглядеть как

void MarshalMe(int iNum, SWIGTYPE_p_p_FooClass ioFooClassArray); // Not ideal!

Существуют некоторые методы для правильной сортировки кода такого типа, поэтому я попробовал несколько из них:

%typemap(ctype)   FooClass** "FooClass**"
%typemap(cstype)  FooClass** "FooClass[]"
%typemap(imtype, inattributes="[In, Out, MarshalAs(UnmanagedType.LPArray)]") FooClass** "FooClass[]"
%typemap(csin)    FooClass** "$csinput"
%typemap(in)      FooClass** "$1 = $input;"
%typemap(freearg) FooClass** ""
%typemap(argout)  FooClass** ""

Это эффективно создает более приятную подпись:

void MarshalMe(int iNum, FooClass[] ioFooClassArray); // Looks good! Would it work?

Однако, когда я пытаюсь запустить его, я получаю следующую ошибку:

{"Exception of type 'System.ExecutionEngineException' was thrown."}

Есть какие-нибудь идеи по поводу фактической карты типов?

Ответы [ 4 ]

2 голосов
/ 19 марта 2010

Исключение говорит вам, что функция повредила кучу мусора. Это запись за концом массива. Если это действительно FooClass [], большой вопрос, то вам сначала нужно создать массив:

 FooClass[] array = new FooClass[666];
 MarshalMe(42, array);

Предполагается, что функция заполнит массив объектами FooClass. Размер массива действительно имеет значение здесь, вам нужно иметь какое-то представление, сколько элементов вы получите Это может работать надежно только в том случае, если «iNum» является аргументом, который говорит, как долго массив. Передать массив. Длина.

Это также может означать, что функция создаст сам массив и вернет указатель на него. Вы действительно облажались, если это так, вы не можете освободить память для массива.

1 голос
/ 06 марта 2013

[Swig] Java: еще один способ передачи указателя на указатель

  1. У меня была похожая проблема с C-функцией (API), которая возвращала указатель на указатель в качестве входного аргумента. Я пытался вызвать C-функцию из JAVA, и у меня не было никакого способа изменить API.

Файл заголовка API.h содержал:

extern int ReadMessage(HEADER **hdr);

Оригинальный C-call выглядел так:

HEADER *hdr;
int status;
status = ReadMessage(&hdr);

Функция API заключалась в хранении данных в ячейке памяти, указанной указателем на указатель.

  1. Я пытался использовать SWIG для создания соответствующего файла интерфейса. SWIG.i создал файл SWIGTYPE_p_p_header.java из API.h. Проблема в том, что конструктор SWIGTYPE_p_p_header инициализировал swigCPtr значением 0.

Вызов JAVA выглядел так:

SWIGTYPE_p_p_header hdr = new SWIGTYPE_p_p_header();
status = SWIG.ReadMessage(hdr);

Но когда я вызывал API из JAVA, ptr всегда был 0.

  1. Наконец, я перестал передавать указатель на указатель в качестве входного аргумента. Вместо этого я определил другую C-функцию в SWIG.i, чтобы возвращать указатель на указатель в возвращаемом значении. Я думал, что это Kludge ... но это сработало!

Вы можете попробовать это:

SWIG.i выглядит так:

// return pointer-to-pointer
%inline %{
   HEADER *ReadMessageHelper() {
   HEADER *hdr;
   int returnValue;
   returnValue = ReadMessage(&hdr);
   if (returnValue!= 1) hdr = NULL;
   return hdr;
}%}
  1. Приведенная выше встроенная функция может привести к утечке памяти, поскольку Java не будет владеть памятью, созданной ReadMessageHelper, поскольку экземпляр HEADER создается в куче.

Исправление утечки памяти - определить ReadMessageHelper как новый объект, чтобы Java могла контролировать память.

%newobject ReadMessageHelper();

JAVA call now would look like:
    HEADER hdr;
    hdr = SWIG.ReadMessageHelper();
  1. Если вам, как и мне, повезло, у вас может быть другой доступный API для освобождения буфера сообщений. В этом случае вам не придется выполнять шаг 4.

  2. Уильям Фултон, гуру SWIG, сказал следующее о подходе:

«Я бы не рассматривал вспомогательную функцию как клудж, более простое решение сложной проблемы. Подумайте, какой эквивалентный чистый 100% Java-код был бы для ReadMessage (). Я не думаю, что есть эквивалент, поскольку классы Java передаются по ссылке, и в Java нет такой вещи, как ссылка на ссылку или указатель на указатель. В имеющейся у вас функции C экземпляры HEADER создаются ReadMessage и передаются обратно вызывающей стороне. Я не понимаю, как можно сделать эквивалент в Java без предоставления некоторого класса-обертки вокруг HEADER и передачи обертки в функцию ReadMessage. В конце дня ReadMessage возвращает только что созданный HEADER, и Java-способ возврата вновь созданных объектов - вернуть его в возвращаемом значении, а не через параметр. ”

0 голосов
/ 17 мая 2016

Мне удалось решить эту проблему, используя Swig Managed Arrays and Pinning Документация.

С учетом функции в C ++

void myArrayCopy(int *sourceArray, int *targetArray, int nitems);

Объявить метод небезопасным в C #

%csmethodmodifiers myArrayCopy "public unsafe";

Добавить соответствующие карты типов

%include "arrays_csharp.i"
%apply int FIXED[] {int *sourceArray} 
%apply int FIXED[] {int *targetArray}

В результате мы получаем следующий метод в классе модуля:

public unsafe static void myArrayCopy(int[] sourceArray, int[] targetArray, int nitems) 
{
    fixed ( int *swig_ptrTo_sourceArray = sourceArray ) 
    {
        fixed ( int *swig_ptrTo_targetArray = targetArray ) 
        {
            examplePINVOKE.myArrayCopy((IntPtr)swig_ptrTo_sourceArray,            

                (IntPtr)swig_ptrTo_targetArray,
                nitems);
        }
    }
}

На практике это может немного отличаться от FooClass **, но Swig действительно поддерживает прямой указатель на маршалинг указателя, что также позволяет избежать копирования, поэтому его можно считать лучшей производительностью

0 голосов
/ 06 марта 2013

Использование таблицы типов SWIG для передачи указателя на указатель:

Вот еще один подход, использующий карты типов. Он нацелен на Perl, а не на Java, но концепции совпадают. И, наконец, мне удалось заставить его работать с использованием карт типов и без вспомогательных функций:

Для этой функции:

typedef void *    MyType;
int getblock( int a, int b, MyType *block );

У меня есть 2 карты типов:

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

%typemap(perl5, argout) void ** data
{
    SV* tempsv = sv_newmortal();
    if ( argvi >= items ) EXTEND(sp,1);
    SWIG_MakePtr( tempsv, (void *)*$1, $descriptor(void *), 0);
    $result = tempsv;
    argvi++;
}

И функция определяется как:

int getblock( int a, int b, void ** data ); 

В моем файле .i. Теперь, это возвращает непрозрачный указатель в карте типов argout, потому что это то, что полезно для этой конкретной ситуации, однако, вы можете заменить строку SWIG_MakePtr на что-то, чтобы фактически делать вещи с данными в указателе, если хотите. Кроме того, когда я хочу передать указатель в функцию, у меня есть карта типов, которая выглядит следующим образом:

%typemap(perl5, in) void * data
{
    if ( !(SvROK($input)) croak( "Not a reference...\n" );

    if ( SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor, 0 ) == -1 )
        croak( "Couldn't convert $1 to $1_descriptor\n");
}

И функция определяется как:

int useblock( void * data );

В моем файле .i.

Очевидно, что это все perl, но он должен отображаться довольно напрямую на Java, насколько позволяет архитектура typemap. Надеюсь, это поможет ...

...