java - 使用 SWIG 的 C 函数的 JNI 包装器 - 类型映射应该是什么?

标签 java c java-native-interface swig

我正在尝试为 C 中的以下函数创建 JNI 包装器:

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

名称 - 输入,实例 - 输出

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

instance - 输入,val - 输出

其中 instance_t 定义为:

typedef void* instance_t;

我完全迷失在 Java 的 SWIG 手册中,因为它不仅仅支持输入参数作为输出类型。我对 Python 包装器(如下所示)没有任何问题。

在Java的情况下,typemap的正确使用方法是什么?

// 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);
}

最佳答案

您可以使用 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 库中为我们提供编写调用 new_instance 的默认版本(我们将其设为私有(private),因为它成为实现细节)的 Java 重载所需的函数。

%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 编写了一个“重载”(但由于我假设您正在编写 C 而不是 C++,我们不得不使用 %rename 来完成这项工作),特别是痛饮接口(interface)。该函数的原始版本被完全忽略,因为它对我们来说毫无用处。

%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;
    }
%}

使用类型映射

我们实际上可以使用 Java 类型映射来做一些与您的 Python 示例非常相似的事情,尽管这个过程更加复杂,因为 Java 具有强类型,我们需要尊重这一点。

此解决方案也与 my older answer 大体相似。在相同的潜在问题上,当底层 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() 生成的代码,以充分理解这些类型映射的作用。

就调用 get_value 而言,instance_t 从上面的接口(interface)自动处理,int* arg out 需要得到类似于上面示例的处理,或者使用仅包含一个元素的数组的技巧:

%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* 问题的第四个解决方案,但因为它不是基本类型,所以涉及更多工作但没有实际 yield )

关于java - 使用 SWIG 的 C 函数的 JNI 包装器 - 类型映射应该是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30308490/

相关文章:

java - 默认情况下在哪里安装本地Java库?

java - LibGDX getBounds 未返回正确的宽度

c - 预期在“=” token 错误之前出现“;”,“,”或“)”。如何纠正?

java - 在C语言中更改变量的值时,是创建新的原语还是当前原语被突变?

java - 生成的 JAR 文件 - 发生 JNI 错误

java - JNI UnsatisfiedLinkError undefined symbol

java - 我可以在不编译的情况下获得 C/C++/Java 代码的 XML AST 吗?

Java/Android : How to update org. apache.http 已弃用

java - 如何停止在数组中打印 null

c - 使用 SDL 播放 mp3 文件时无法识别的音乐格式