Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java SWIG对void*C返回类型的支持_Java_Java Native Interface_Swig - Fatal编程技术网

Java SWIG对void*C返回类型的支持

Java SWIG对void*C返回类型的支持,java,java-native-interface,swig,Java,Java Native Interface,Swig,我的SWIG接口文件(包含在头文件中)中有一个C函数,它当前返回一个char*,SWIG使用该char*为fetchFromRow方法生成字符串Java返回类型。我想知道如果我在C端将返回值改为void*,SWIG在Java端会做什么?C fetchFromRow函数返回一个结构(sockaddr_in)、字符串或int。如何设置SWIG接口文件以支持此操作?有没有办法使生成的Java fetchFromRow具有返回类型的对象,这样我就可以在Java端强制转换 C代码: extern char

我的SWIG接口文件(包含在头文件中)中有一个C函数,它当前返回一个char*,SWIG使用该char*为fetchFromRow方法生成字符串Java返回类型。我想知道如果我在C端将返回值改为void*,SWIG在Java端会做什么?C fetchFromRow函数返回一个结构(sockaddr_in)、字符串或int。如何设置SWIG接口文件以支持此操作?有没有办法使生成的Java fetchFromRow具有返回类型的对象,这样我就可以在Java端强制转换

C代码:

extern char *
fetchFromRow(struct row_t *r_row,
        type_t type);

extern void *
fetchFromRow(struct row_t *r_row,
        type_t type);
当我使用头文件(包括在SWIG接口文件中)中的void*生成方法时,我得到了一个带有SWIGTYPE_p_void返回类型的java方法。有没有办法处理这个问题

Swig文件:

%module Example
%include "typemaps.i"
%include "stdint.i"
%include "arrays_java.i"
void setPhy_idx(uint32_t value);
%include "arrays_java.i"
void setId(unsigned char *value);
%{
#include "Example1.h"
#include "Example2.h"
#include "Example3.h"
#include "Example4.h"
%}
%rename setLogFile setLogFileAsString;
%inline %{
void setLogFileAsString(const char *fn) {
  FILE *f = fopen(fn, "w");
  setLogFile(f);
}
%}
%typemap(jstype) void* "java.lang.Object"
%typemap(javaout) void* {
  long cPtr = $jnicall;
  Object result = null;
  if (type == type_t.TYPE_CATEGORY) {
    result = void2int8(cPtr);
  }
  return result;
}

%newobject fetch(struct result_row_t *result_row, type_t type, int32_t *length);
%inline %{

uint8_t void2int8(jlong v) {
  return (intptr_t)v;
}

%}
%apply char * { unsigned char * };
%apply char * { const void * }
%apply int32_t { int32_t * }
int createKey(const void* secret, int secret_len, const sdr_id_t *id, unsigned char key[20], int key_len);
session_t* createSession(const char* addr, int ort, const char *ddr, int rt, const der_id_t *id, unsigned char key[20], int key_len); 
%apply int32_t *OUTPUT { int32_t *length }
%ignore createKey;
%ignore setLogFile;
%ignore ecreateSession;
%include "Example1.h"
%include "Example2.h"
%include "Example3.h"
%include "Example4.h"
%pragma(java) jniclasscode=%{
  static {
    try {
        System.loadLibrary("Example");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. \n" + e);
      System.exit(1);
    }
  }
%}
调用fetch的Java代码:

{
   //only type_t param shown here for simplicity
   Object category = Example.fetch(result_row, type_t.TYPE_CATEGORY, length);
   System.out.println("category=" + aLevel.toString());
   System.out.println("category=" + ((Short)aLevel).intValue());
   System.out.println("category=" + ((Short)aLevel).toString());
   //all 3 Sys outs show same value but when called again each show same value but different than the first execution
}
我试图用SWIG替换的C代码包装器。这通常是从Java调用的,但现在我尝试直接调用fetch(伪代码):


如果您为返回类型定义了类型层次结构,并将基类用作
fetchFromRow
的返回类型,这可能会更容易。(事实证明,解决方案并不像我最初认为的那么简单,甚至还有!)尽管按照你的要求去做是可能的,但我举了一个简单的例子来说明要点

我在这里使用的示例有一个简单的接口,位于
test.h
(这里的声明和定义都使事情更简单):

这里我们有四种不同的类型,与
enum
值配对。这些都是为了简单而挑选的。它们可以是任何你想要的,只要你有一个合适的类型图可以应用。(如果您特别想要
sockaddr_in
,请特别应用about wrapping
sockaddr_in
中的类型映射)

还有一个函数
fetch
,它创建一个类型并通过
void*
返回它。这说明了你在问题中所问的问题。
fetch
的棘手之处在于,SWIG无法在返回
void*
指针之前直接推断其背后的内容。我们需要为SWIG提供一种更具体地了解类型的方法,使用我们关于
type\t
参数含义的更高层次的知识

为了包装它,我们需要一个SWIG接口文件,
test.i
,它以通常的模块内容开始:

%module test
%{
#include "test.h"
%}
为了包装我们的
fetch
函数,我们需要找到一种合理的方法,在Java端将最接近的东西暴露给
void*
。在本例中,我认为
java.lang.Object
是返回类型的一个好选择,它相当接近
void*

%typemap(jstype) void* "java.lang.Object"
这将设置Java端的
fetch
返回类型。(我们没有使用
%typemap(jtype)
更改生成的JNI中介体的返回类型,但是,这仍然会像以前一样默认为
long

接下来,我们需要编写另一个类型映射,以指定实际调用的结果将如何转换为我们所说的调用将在Java端返回的类型:

%typemap(javaout) void* {
  long cPtr = $jnicall;
  Object result = null;
  if (type == type_t.TYPE1) {
    result = new type1(cPtr, $owner);
  }
  else if (type == type_t.TYPE2) {
    result = new type2(cPtr, $owner);
  }
  else if (type == type_t.TYPE3) {
    result = void2int(cPtr);
    // could also write "result = new Integer(void2int(cPtr));" explicitly here
  }
  else if (type == type_t.TYPE4) {
    result = void2str(cPtr);
  }

  return result;
}

%newobject fetch(type_t type);
在这里,我们创建一个适当的SWIG代理类型的Java对象,或者调用一个我们将很快看到的helper函数。我们通过查看调用中使用的
type\u t
来决定使用哪种类型。(我不喜欢100%按名称引用此类型,即直接引用
类型
,但似乎没有更好的方法访问
javaout
typemap中调用函数时使用的参数)

每个构造函数的第二个参数,在这里被看作是
$owner
,对于内存管理很重要,它声明谁拥有分配,我们希望将所有权转移到Java以避免泄漏。其值由
%newobject
指令确定

我们需要一个helper函数来将
void*
转换为
int32\u t
String
案例中更有意义的类型。我们提供了
%inline
,要求SWIG同时包装和定义它:

%inline %{
int32_t void2int(jlong v) {
  return (intptr_t)v;
}

const char *void2str(jlong v) {
  return (const char*)v;
}
%}
我在这里使用了
jlong
,因为在接口的Java端,所有指针都表示为
long
。它确保函数与JNI调用返回的函数完全兼容,而不会丢失精度(在某些平台上,
jlong
可能是
long
)。基本上,这些函数的存在是为了在C端实现从泛型(
void*
)到特定类型的转换,而只需尽可能少的手动操作。SWIG在这里为我们处理所有类型

最后,我们结束了头文件的
%include
(我们希望以最简单的方式包装它)和一些使库自动加载的代码:

%include "test.h"

%pragma(java) jniclasscode=%{
  static {
    try {
        System.loadLibrary("test");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. \n" + e);
      System.exit(1);
    }
  }
%}
我通过编译来测试此包装:

swig -Wall -java -c++ test.i
javac *.java 
g++ -Wall -Wextra test_wrap.cxx -shared -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux/ -o libtest.so 
运行下面的Java代码来“练习”一下

public class main {
  public static void main(String[] argv) {
    Object o1 = test.fetch(type_t.TYPE1);
    Object o2 = test.fetch(type_t.TYPE2);
    Object o3 = test.fetch(type_t.TYPE3);
    Object o4 = test.fetch(type_t.TYPE4);

    if (!(o1 instanceof type1)) {
      System.out.println("Wrong type - o1");
    }
    else {
      System.out.println("o1.getFoo(): " + ((type1)o1).getFoo());
    }

    if (!(o2 instanceof type2)) {
      System.out.println("Wrong type - o2");
    }
    else {
      System.out.println("o2.getFoo(): " + ((type2)o2).getBar());
    }

    if (!(o3 instanceof Integer)) {
      System.out.println("Wrong type - o3");
    }
    else {
      System.out.println("o3.intValue(): " + ((Integer)o3).intValue());
    }

    if (!(o4 instanceof String)) {
      System.out.println("Wrong type - o4");
    }
    else {
      System.out.println("o4: " + (String)o4);
    }
  }
}

您总是可以根据此处显示的代码重新创建整个示例,
test.i
按顺序显示和讨论,但为了方便起见,我还提供了一个副本。

对于像这样的困难情况,我喜欢使用的方法是:(a)简化C接口以从SWIG中获得更好的结果,以及(b)将复杂度作为简化C函数的包装带回目标语言

在您的情况下,我将向C API添加两个附加函数:

char* fetchFromRowString(struct row_t *r_row);
int fetchFromRowInt(struct row_t *r_row);
这对斯威格来说是小菜一碟。然后在Java端,您可以重新创建原始的
fetchFromRow()
,其中实现根据类型调用C函数的字符串或Int版本,最后将结果作为对象返回


祝你好运。

我认为这是一个骗局,但有点不同。这是关于支持voidc返回类型的。这很棘手,但我认为确实可行。基本上你需要一个
public class main {
  public static void main(String[] argv) {
    Object o1 = test.fetch(type_t.TYPE1);
    Object o2 = test.fetch(type_t.TYPE2);
    Object o3 = test.fetch(type_t.TYPE3);
    Object o4 = test.fetch(type_t.TYPE4);

    if (!(o1 instanceof type1)) {
      System.out.println("Wrong type - o1");
    }
    else {
      System.out.println("o1.getFoo(): " + ((type1)o1).getFoo());
    }

    if (!(o2 instanceof type2)) {
      System.out.println("Wrong type - o2");
    }
    else {
      System.out.println("o2.getFoo(): " + ((type2)o2).getBar());
    }

    if (!(o3 instanceof Integer)) {
      System.out.println("Wrong type - o3");
    }
    else {
      System.out.println("o3.intValue(): " + ((Integer)o3).intValue());
    }

    if (!(o4 instanceof String)) {
      System.out.println("Wrong type - o4");
    }
    else {
      System.out.println("o4: " + (String)o4);
    }
  }
}
char* fetchFromRowString(struct row_t *r_row);
int fetchFromRowInt(struct row_t *r_row);