Оболочка JNI для функции C с использованием SWIG - какова должна быть типовая карта? - программирование

Оболочка JNI для функции C с использованием SWIG - какова должна быть типовая карта?

Я пытаюсь создать оболочку JNI для следующих функций в C:

int err = new_instance(const char* name, instance_t* instance);

name - ввод, instance - вывод

int err = get_value(const instance_t instance, int *val);

instance - ввод, val - вывод

где instance_t определяется как:

typedef void* instance_t;

Я все потерял в руководстве SWIG для Java, поскольку он не просто поддерживает входные параметры в качестве типа вывода. У меня не было никаких проблем с оболочкой Python (показано ниже).

Каков правильный способ использования typemap в Java?

// instance_t [argout]
%typemap(in, numinputs=0) instance_t* instance (instance_t temp = 0) {
    $1 = &temp;
}
%typemap(argout) instance_t *instance {
    %append_output(PyLong_FromLongLong((long long)* $1));
}
// instance_t [in]
%typemap(in) instance_t instance {
    $1 = (instance_t) PyLong_AsLongLong($input);
}
4b9b3361

Ответ 1

Вы можете сделать это с помощью SWIG и Java несколькими способами. Я создал следующий заголовок, чтобы проиллюстрировать все мои примеры, основываясь на том, что вы показали в вопросе:

typedef void* instance_t;

int new_instance(const char* name, instance_t * instance);
int get_value(const instance_t instance, int *val);

Написание некоторых Java в интерфейсе:

Мы можем использовать cpointer.i из библиотеки SWIG, чтобы предоставить нам функции, которые нам нужны, чтобы написать перегрузку Java, которая вызывает значение по умолчанию версии new_instance (которую мы делаем приватной, поскольку она становится деталью реализации).

%module test

%{
#include "test.h"
%}

%include <cpointer.i>
// Have SWIG create a helper class for "pointer to pointer" type of handle
%pointer_class(instance_t, inst_ptr);
// Hide default version of new_instance
%javamethodmodifiers new_instance "private";

// Supply  Java version of new_instance now with useful method signature
%pragma(java) modulecode=%{
  public static SWIGTYPE_p_void new_instance(String name) {
    inst_ptr ptr = new inst_ptr();
    final int err = new_instance(name, ptr.cast());
    if (0!=err) {
      // throw or whatever
    }
    return ptr.value();
  }
%}

%include "test.h"

Обратите внимание, что этот пример, вероятно, протекает как есть, поскольку ptr.value() по умолчанию не владеет.

Написание некоторых C в интерфейсе:

В следующем примере мы пишем "перегрузку" (но поскольку я предполагал, что вы пишете C, а не С++, нам пришлось использовать %rename для выполнения этой работы) только на C, особенно для интерфейса SWIG. Оригинальная версия функции полностью игнорируется, так как она нам совершенно бесполезна.

%module test

%{
#include "test.h"
%}

// Hide the default new_instance
%ignore new_instance;
%include "test.h"
// Pretend our wrapper specific "overload" was called new_instance all along
%rename(new_instance) new_instance_overload;
// Don't leak our new instance
%newobject new_instance;

// Declare, define and wrap a special version of new_instance
%inline %{
    instance_t new_instance_overload(const char* name) {
        instance_t result = NULL;
        const int err = new_instance(name, &result);
        if (err) {
            // See later on/other Q for cross language exception example
        }
        return result;
    }
%}

Использование typemaps

На самом деле мы можем сделать что-то очень похожее на ваш пример Python, используя шаблоны типов Java, хотя процесс более запутан, поскольку Java имеет сильную типизацию, и мы должны это уважать.

Это решение также по существу похоже на мой более старый ответ по одной и той же основной проблеме, с дополнительной сложностью, заключающейся в том, что сильная типизация работает на Java (вместо SWIGTYPE_p_void) здесь сложнее, когда базовый тип typedef равен void* вместо прямого объявления структуры.

%module test

%{
#include "test.h"
%}

// Provide 'instance' class for strong typing (instead of void* semantics)
%rename(Instance) instance;
%nodefaultctor;
struct instance {};
typedef instance * instance_t;

// Don't leak (not that we have a destructor yet, but...)
%newobject new_instance;

// Change new_instance to return instance of Instance

%typemap(jstype) int new_instance "$typemap(jstype,instance_t)";
%typemap(jni) int new_instance "$typemap(jni,instance_t)";
%typemap(jtype) int new_instance "$typemap(jtype,instance_t)";

// Hide the instance_t argument and use a temporary instead under the hood
%typemap(in,numinputs=0) instance_t * ($1_basetype tmp) %{
  $1 = &tmp;
%}
// After the call copy the result back
%typemap(argout) instance_t * %{
  *($1_ltype)&$result = *$1;
%}
// Inside Java construct the proxy with the correct long pointer
%typemap(javaout) int new_instance {
  return new $typemap(jstype,int new_instance)($jnicall, $owner);
}

// Some error handling
%javaexception("Exception") new_instance {
  $action
  if (!result) {
    // JNI code to raise exception, untested in this form
    jclass clazz = JCALL1(FindClass, jenv, "Exception");
    JCALL2(ThrowNew, jenv, clazz, "Failure creating thing");
    return $null;
  }
}

%include "test.h"

Я бы посоветовал вам просмотреть сгенерированный код вокруг вызова new_instance(), чтобы полностью понять, что делают эти typemaps.

Что касается вызова get_value, то instance_t автоматически обрабатывается из интерфейса выше, а аргумент int* arg должен быть обработан либо аналогичным приведенному выше примеру, либо с помощью трюка с массивом содержащий только один элемент:

%include <typemaps.i>
%apply int *OUTPUT { int *val };

%include "test.h"

который вы бы назвали следующим:

int outarr[] = new int[1];
final int err = test.get_value(instance, outarr);
// outarr[0] then constains the value 

Конечно, вы могли бы воспользоваться этим трюком и использовать что-то вроде %pragma(java) modulecode моего первого примера в этом ответе, чтобы обеспечить еще одну перегрузку, которая ведет себя более естественно:

%javamethodmodifiers get_value "private";

%pragma(java) modulecode=%{
  public static int get_value(Instance instance) {
    int outarr[] = new int[1];
    final int err = test.get_value(instance, outarr);
    if (0!=err) {
      // throw, blah blah
    }
    return outarr[0];
  }
%}

(Обратите внимание, что этот трюк с массивами также работал бы на четвертое решение проблемы instance_t*, но поскольку он не был примитивным типом, там было гораздо больше работы без реального выигрыша)