我的SWIG接口文件(包含在头文件中)中有一个C函数,它当前返回一个char *,SWIG用它为fetchFromRow方法生成String Java返回类型。我想知道我是否将C的返回更改为void *,SWIG会在Java端做什么? C fetchFromRow函数返回一个结构(sockaddr_in),String或int。如何设置SWIG接口文件以支持此功能?有没有办法让生成的Java fetchFromRow具有返回类型的Object,以便我可以在Java端进行转换?
C代码:
extern char *
fetchFromRow(struct row_t *r_row,
type_t type);
extern void *
fetchFromRow(struct row_t *r_row,
type_t type);
当我在头文件中使用void *生成方法(包含在SWIG接口文件中)时,我得到一个带有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(伪代码):
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 :(得分:3)
如果为返回类型定义了类型层次结构,并且使用基类作为fetchFromRow
的返回类型,则这可能会更容易。 (事实证明,解决方案并不像我原先想象的那么容易,甚至还有an example in the documentation!This 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提供更具体地了解类型的方法。
要包装它,我们需要一个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
。
接下来我们需要编写另一个typemap来指定实际调用的结果将如何转换为我们所说的调用将在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对象,或者调用我们很快就会看到的辅助函数。我们通过查看调用中使用的type_t
来决定要使用哪个类型。 (我对于通过名称直接引用此类型并不是100%感到高兴,即直接type
,但似乎没有更好的方法来获取对函数在{{1}内调用的参数的访问权限} typemap)
每个构造函数的第二个参数,在这里看作javaout
对于内存管理很重要,它说明谁拥有分配,我们想将所有权转移到Java以避免泄漏它。其值由$owner
指令确定。
我们需要一个辅助函数来将%newobject
转换为void*
和int32_t
个案例的更有意义的类型。我们提供String
来请求SWIG同时包装和定义它:
%inline
我在这里使用%inline %{
int32_t void2int(jlong v) {
return (intptr_t)v;
}
const char *void2str(jlong v) {
return (const char*)v;
}
%}
,因为在接口的Java端,所有指针都表示为jlong
。它确保函数与JNI调用返回的内容完全兼容,而不会丢失精度(在某些平台上,long
可能是jlong
)。基本上这些函数用于通过尽可能少的手动工作从泛型(long long
)到C侧的特定转换。 SWIG在这里为我们处理所有类型。
最后,我们以头文件void*
结束(我们希望将其全部包装起来,这是最简单的方法)以及一些使库自动加载的代码:
%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);
}
}
%}
并运行以下Java代码来“练习”它。
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
您可以随时从此处显示的代码中重新创建整个示例,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 on my site的副本。
答案 1 :(得分:1)
我喜欢用于这类困难案例的方法是(a)简化C接口以从SWIG获得更好的结果,以及(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返回。