JNI包装器使用痛饮C函数 - 应该是什么类型映射? [英] JNI wrapper for C function using SWIG - what should be the typemap?

查看:120
本文介绍了JNI包装器使用痛饮C函数 - 应该是什么类型映射?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建JNI封装在C以下功能:

  INT ERR = new_instance(为const char *名称,instance_t *实例);

名称 - 输入,实例 - 输出

  INT ERR =的get_value(常量instance_t例如,为int * VAL);

实例 - 输入, VAL - 输出

其中, instance_t 的定义是:

 的typedef无效* instance_t;

我都失去了在Java的SWIG手册,因为它不只是支持输入参数的输出类型。我用Python包装(如下图所示),不具任何问题。

什么是Java中的情况下,使用类型映射的正确方法?

  // instance_t [argout]
%类型映射(在,numinputs = 0)instance_t *实例(instance_t临时= 0){
    $ 1 =&安培;温度;
}
%类型映射(argout)instance_t *实例{
    %append_output(PyLong_FromLongLong((久长)* $ 1));
}
// instance_t [输入]
%类型映射(中)instance_t例如{
    $ 1 =(instance_t)PyLong_AsLongLong($输入);
}


解决方案

您可以通过多种不同的方式使用痛饮和Java做到这一点。我创建了下面的标题来说明我的所有的例子,根据您的问题表明了什么:

 的typedef无效* instance_t;INT new_instance(为const char *名称,instance_t *实例);
INT的get_value(常量instance_t例如,为int * VAL);

在接口编写一些Java:

我们可以从SWIG库使用 cpointer.i 给我们的功能,我们需要(因为它变成一个实现细节,我们做私人)编写一个Java超载调用 new_instance 的默认版本。

%模块的测试%{
#包括test.h
%}%包括< cpointer.i>
//有无SWIG创建一个辅助类的指针指向式手柄
%pointer_class(instance_t,inst_ptr);
// new_instance的隐藏默认版本
%javamethodmodifiers new_instance私人;// new_instance供应Java版本现在有用的方法签名
%附注(JAVA)模块code = {%
  公共静态SWIGTYPE_p_void new_instance(字符串名称){
    inst_ptr PTR =新inst_ptr();
    最终诠释ERR = new_instance(姓名,ptr.cast());
    如果(0!= ERR){
      //抛出或什么
    }
    返回ptr.value();
  }
%}%包括test.h

请注意,这个例子可能泄漏的是,因为 ptr.value()非拥有默认情况下。

编写一些C的界面:

在接下来的例子中,我们写一个过载(但因为我假定你正在编写C,而不是C ++,我们不得不使用%重命名,使这项工作)在单独的C,专为SWIG接口。该函数的原始版本被完全忽略,因为它是pretty对我们没用。

 %模块的测试%{
#包括test.h
%}//隐藏默认new_instance
%无视new_instance;
%包括test.h
// pretend我们的封装具体的超载是一直以来被称为new_instance
重命名%(new_instance)new_instance_overload;
//不要泄露我们的新实例
%NEWOBJECT new_instance;//声明,定义和包装new_instance一个特别版本
%一致 %{
    instance_t new_instance_overload(为const char *名称){
        instance_t结果= NULL;
        const int的ERR = new_instance(姓名,&安培;结果);
        如果(ERR){
            //在后面看到/其他Q值跨语言异常的例子
        }
        返回结果;
    }
%}

使用typemaps

我们实际上可以做非常类似于使用Java typemaps你的Python例子东西,虽然过程是比较绕口,因为Java有很强的类型,我们需要尊重这一点。

此解决方案也基本上类似于<一href=\"http://stackoverflow.com/questions/12739331/swig-interface-to-receive-an-opaque-struct-reference-in-java-through-function-ar\">my在同一个根本的问题年长的答案,用另外的复杂性越来越强类型Java中(而不仅仅是 SWIGTYPE_p_void )这里是棘手当基础的typedef是工作到无效* ,而不是一个结构的正向声明。

%模块的测试%{
#包括test.h
%}//提供实例类(而不是无效*语义)的强类型
重命名%(实例)实例;
%nodefaultctor;
结构实例{};
例如typedef的* instance_t;//不要泄露(我不是说我们有一个析构函数还,但...)
%NEWOBJECT new_instance;//改变new_instance返回实例实例%类型映射(jstype)INT new_instance$类型映射(jstype,instance_t);
%类型映射(JNI)I​​NT new_instance$类型映射(JNI,instance_t);
%类型映射(jtype的)INT new_instance$类型映射(jtype的,instance_t);//隐藏instance_t参数,并使用一个临时代替下引擎盖
%类型映射(在,numinputs = 0)instance_t *($ 1_basetype TMP)%{
  $ 1 =&安培; TMP;
%}
//调用拷贝后的结果返回
%类型映射(argout)instance_t *%{
  *($ 1_ltype)及$结果= * $ 1;
%}
// Java里面建设有正确的长指针代理
%类型映射(javaout)INT new_instance {
  返回新类型映射$(jstype,INT new_instance)($ JNICALL,$所有者);
}//一些错误处理
%javaexception(异常)new_instance {
  $行动
  如果(!结果){
    // JNI code引发异常,未经测试这种形式
    JCLASS clazz所= JCALL1(FindClass后面,jenv,异常);
    JCALL2(ThrowNew,jenv,clazz中,失败创造物);
    返回$空;
  }
}%包括test.h

我建议你在待命,产生code望向 new_instance()来充分了解这些typemaps正在做的。

至于调用的get_value 来讲 instance_t 就会自动从接口处理上述及为int * ARG出需要得到处理,或者类似地,以上面的例子中,或使用一个技巧只包含一个元素的数组:

%包括&LT; typemaps.i&GT;
%应用为int * OUTPUT {INT * VAL};%包括test.h

,你会再调用为:

INT outarr [] =新INT [1];
最终诠释ERR = test.get_value(例如,outa​​rr);
// outarr [0],然后constains值

你当然可以然后采取招和使用类似%附注(JAVA)模块code 我的第一个例子中的这个答案提供另一个重载其行为更自然:

%javamethodmodifiers的get_value私人;%附注(JAVA)模块code = {%
  公共静态INT的get_value(实例实例){
    诠释outarr [] =新INT [1];
    最终诠释ERR = test.get_value(例如,outa​​rr);
    如果(0!= ERR){
      //扔,等等等等
    }
    返回outarr [0];
  }
%}

(注意,这招对数组也会有第四个解决 instance_t * 问题的工作,但因为它不是一个原始类型有涉及更多的工作没有真正的增益)

I am trying to create the JNI wrapper for the following functions in C:

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

name - input, instance - output

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

instance - input, val - output

where instance_t is defined as:

typedef void* instance_t;

I am all lost in the SWIG manual for Java, since it doesn't simply support input parameters as the output type. I had no problems whatsoever with the Python wrapper (shown below).

What is the correct way of using typemap in the case of 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);
}

解决方案

You can do this using SWIG and Java in several different ways. I've created the following header to illustrate all my examples, based on what you showed in the question:

typedef void* instance_t;

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

Writing some Java in the interface:

We can use the cpointer.i from the SWIG library to give us the functions we need to write a Java overload that calls the default version of new_instance (which we make private since it becomes an implementation detail).

%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"

Note that this example probably leaks as is since ptr.value() is non-owning by default.

Writing some C in the interface:

In this next example we write an "overload" (but since I assumed you're writing C and not C++ we had to use %rename to make this work) in C alone, specifically for the SWIG interface. The original version of the function gets ignored completely since it's pretty useless to us.

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

Using typemaps

We can actually do something very similar to your Python example using Java typemaps, although the process is more convoluted since Java has strong typing and we need to respect that.

This solution is also substantially similar to my older answer on the same underlying issue, with the additional complexity that getting strong typing working in Java (instead of just SWIGTYPE_p_void) is trickier here when the underlying typedef is to void* instead of a forward declaration of a struct.

%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"

I would encourage you to look at the generated code around the call to new_instance() to fully understand what these typemaps are doing.

As far as the call to get_value is concerned the instance_t gets handled automatically from the interface above and the int* arg out needs to get handled either similary to the above example, or using a trick with an array containing only one element:

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

%include "test.h"

Which you would then call as:

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

Of course you could then take that trick and use something like the %pragma(java) modulecode of my first example in this answer to supply another overload that behaves more naturally:

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

(Note that this trick with arrays would also have worked for a fourth solution to the instance_t* problem, but because it's not a primitive type there's a lot more work involved for no real gain)

这篇关于JNI包装器使用痛饮C函数 - 应该是什么类型映射?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆