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