2011-11-17 35 views
3

我的SWIG接口文件(包含在头文件中)中有一个C函数,它当前返回一个char *,SWIG用来为fetchFromRow方法生成一个String Java返回类型。我想知道,如果我在C方面将返回更改为void *,那么SWIG会在Java方面做什么? C fetchFromRow函数返回一个结构体(sockaddr_in),String或int。我如何设置我的SWIG接口文件来支持这个?有没有办法让生成的Java fetchFromRow具有返回类型的Object,以便我可以在Java端进行投票?SWIG对void * C返回类型的支持

C代码:

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

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

当我生成使用在头文件的空隙*(包含在SWIG接口文件)的方法中,我得到一个SWIGTYPE_p_void返回类型的Java方法。任何想法如何处理?

痛饮文件:

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

的Java代码来调用取:我正在试图与SWIG来代替

{ 
    //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 
} 

C代码包装。这就要用到从Java调用,但现在我想调用获取直接(伪代码):

char * 
wrapFetch(struct row_t *result_row, type_t type) 
{ 
int8_t *int8_valp = NULL; 
switch (attribute) { 
case TYPE_CATEGORY: 

     int8_valp = fetch(
       result_row, attribute, &length); 
     if (length > 0 && int8_valp != NULL) { 
      snprintf(smallbuf, sizeof(smallbuf), "%u", *int8_valp); 
      return strdup(smallbuf); 
     } else { 
      return NULL; 
     } 
} 
+0

我觉得这是 http://stackoverflow.com/questions/2345354/accessing-void-pointers-in-python-using-swig-or-something-else –

+0

这是略有不同的欺骗。它关于支持无效的C返回类型 – c12

+0

这很棘手,但我认为可以。基本上你需要在'type_t's和他们所代表的“真实”类型之间建立一个映射关系,以便使Java在Java上可行。我有一个类似于Python的例子,我希望这个周末能够转换成Java。 – Flexo

回答

3

,如果你定义的返回类型类型层次结构,以及所使用的基类,因为这可能是更容易返回类型为fetchFromRow。 (事实证明,解决方案并不像我原先想象的那样容易,甚至有可能是an example in the documentationThis question also applies to Java+SWIG)尽管做你所问的事情是可能的,但我举了一个更简单的例子来说明重要的一点。

我与在这里工作的例子有一个简单的界面,在test.h(声明和定义都让事情变得更简单的在这里):

struct type1 { 
    type1(int foo) : foo(foo) {} 
    int foo; 
}; 

struct type2 { 
    type2(double bar) : bar(bar) {} 
    double bar; 
}; 

// TYPE3 is int32_t, TYPE4 is const char* 

typedef enum { TYPE1, TYPE2, TYPE3, TYPE4 } type_t; 

void* fetch(type_t type) { 
    switch(type) { 
    case TYPE1: 
    return new type1(101); 
    case TYPE2: 
    return new type2(1.111); 
    case TYPE3: 
    return (void*)(-123); // One way of returning int32_t via void*! 
    case TYPE4: 
    return (void*)("Hello world"); // Cast because not const void* 
    default: 
    return NULL; 
    } 
} 

在这里,我们有四种不同的类型,有配对值为enum。这些是为了简单而挑选的。只要你有一个合适的可应用的类型图,它们可以是你想要的任何东西。 (如果您想要sockaddr_in专门,则应用my answer to your previous question的类型映射关于专门包装sockaddr_in)。

还有一个功能fetch它创建一个类型并通过void *返回它。这说明了你在问题中提出的问题。 fetch棘手的事情是SWIG没有直接的方式来推断返回之前void*指针后面的内容。我们需要使用关于type_t参数含义的更高层次的知识,让SWIG更具体地了解类型是什么。

为了结束这一点,我们需要一个痛饮接口文件,test.i这与通常的模块的东西开始:

%module test 
%{ 
#include "test.h" 
%} 

为了包装我们fetch功能,我们需要找到暴露出最接近的一个明智的办法在Java一侧的void*。在这种情况下,我认为java.lang.Object对于退货类型是一个不错的选择,它在这里相当接近void*

​​

这设置了Java端的fetch的返回类型。 (虽然我们没有改变生成的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); 

这里我们创建一个Java对象,适当的SWIG代理类型,或者调用我们很快会看到的帮助函数。我们通过查看呼叫中使用的type_t来决定要使用哪种类型。 (我不是100%高兴通过名称指的是这种类型的,即type直接,但似乎没有要获得一个函数被调用了javaout类型映射里面的参数访问一个更好的方法)

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

我们需要帮助函数将void*转换为int32_tString个案的更有意义的类型。我们与%inline提供此问痛饮在同一时间来包装和定义它:

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

const char *void2str(jlong v) { 
    return (const char*)v; 
} 
%} 

我以前jlong在这里,因为所有的指针表示为long接口的Java端。它确保函数与JNI调用返回的内容完全兼容,而不会损失精度(在某些平台上,可能jlong可能是long long)。基本上这些功能的存在是为了从通用(void*)到C端的特定转换,尽可能减少手动工作。 SWIG在这里为我们处理所有类型。

最后我们完成了%include的头文件(我们希望把它包起来所有所以这是最简单的方法)和一些代码,以使图书馆都会自动加载:

​​

我测试了这个包裹通过编译:

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所示,依次讨论,但为了方便起见,我也放了test.i on my site副本。

+0

感谢所有帮助awoodland! – c12

+0

我再次查看这个答案,只是想知道是否有无论如何处理无效*无需修改头文件?我假设不......只是想我会问。 – c12

+0

@ c12 - 我给出的使用javaout和jstype类型映射的答案不需要对头文件进行任何修改即可工作。我展示的头文件只是我的猜测,头文件的简化版本可能看起来像什么,并给了我一些测试对象。 'fetch'的类型,'enum'和实现可以是几乎任何你喜欢的东西。 – Flexo

1

我喜欢用强硬的情况下,像这样的是(一)简化C接口得到痛饮更好的结果,和(b)带来的复杂性早在目标语言作为一个包装简化的方法C功能。

在你的情况,我想补充两个附加功能的C API:

char* fetchFromRowString(struct row_t *r_row); 
int fetchFromRowInt(struct row_t *r_row); 

这将是一块蛋糕为SWIG来处理。然后在Java端,您可以重新创建原始fetchFromRow(),其中实现根据类型调用C函数的String或Int版本,最后将结果作为Object返回。

祝你好运。

+1

这是一个很好的建议Miguel,谢谢你的回答 – c12

相关问题