所以,我正在尝试使用SWIG将C库(libnfc)移植到Java。
我已经到了编译共享库的地步,并且基本的“nfc_version()”方法调用将起作用。但是,调用“nfc_init()”进行设置会导致SIGSEGV错误。直接调用nfc库就可以了。
我用来生成共享库的命令:
swig -java -I../libnfc/include nfclib.i
gcc -c -I/usr/lib/jvm/java-7-openjdk-i386/include/ -I/usr/lib/jvm/java-7-openjdk-i386/include/linux nfclib_wrap.c
gcc -shared nfclib_wrap.o ../build/libnfc/libnfc.so libnfc_wrap.so
libnfc.i文件:
%module nfc
%{
#include <nfc/nfc.h>
#include <nfc/nfc-types.h>
#include <nfc/nfc-emulation.h>
%}
%include <nfc/nfc.h>
%include <nfc/nfc-types.h>
%include <nfc/nfc-emulation.h>
即。它应该包括libnfc提供的所有方法。
以下是我收到的错误日志:http://openetherpad.org/AyVDsO4XTg
显然,可能无法从我提供的信息中获得特定的解决方案。但任何有关尝试的事情的建议都会非常感激(我的知识就在这里)。
答案 0 :(得分:5)
要始终将相同的指针自动传递给函数,它在SWIG中相当简单。例如,给定“header”文件test.h,它捕获问题的核心部分:
struct context; // only used for pointers
void init_context(struct context **ctx) { *ctx=malloc(1); printf("Init: %p\n", *ctx); }
void release_context(struct context *ctx) { printf("Delete: %p\n", ctx); free(ctx); }
void foo(struct context *ctx) { printf("foo: %p\n", ctx); }
我们可以包装它并自动使全局上下文在任何预期的地方传递,例如:
%module test
%{
#include "test.h"
// this code gets put in the generated C output from SWIG, but not wrapped:
static struct context *get_global_ctx() {
static struct context *ctx = NULL;
if (!ctx)
init_context(&ctx);
return ctx;
}
%}
%typemap(in,numinputs=0) struct context *ctx "$1=get_global_ctx();"
%ignore init_context; // redundant since we call it automatically
%include "test.h"
这为struct context *ctx
设置了一个类型映射,而不是从Java获取输入,而是在匹配的任何地方自动调用get_global_ctx()
。
这可能足以为Java开发人员提供一个理智的界面,但它不太理想:它强制上下文成为全局,意味着没有Java应用程序可以同时使用多个上下文。 / p>
考虑到Java是一种OO语言,一个更好的解决方案是使上下文成为第一类Object。我们也可以让SWIG为我们生成这样的界面,尽管它有点复杂。我们的SWIG模块文件变为:
%module test
%{
#include "test.h"
%}
// These get called automatically, no need to expose:
%ignore init_context;
%ignore delete_context;
// Fake struct to convince SWIG it should be an object:
struct context {
%extend {
context() {
// Constructor that gets called when this object is created from Java:
struct context *ret = NULL;
init_context(&ret);
return ret;
}
~context() {
release_context($self);
}
}
};
%include "test.h"
我们可以成功运用此代码:
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
context ctx = new context();
// You can't count on the finalizer if it exits:
ctx.delete();
ctx = null;
// System.gc() might also do the trick and in a longer
// running app it would happen at some point probably.
}
}
给出:
Init: 0xb66dab40
Delete: 0xb66dab40
在动态类型语言中,这将是艰难的部分 - 我们可以使用一种或另一种形式的元编程来根据需要插入成员函数。因此,我们可以完全按照预期说出new context().foo();
之类的内容。 Java是静态类型的,所以我们需要更多东西。我们可以通过多种方式在SWIG中做到这一点:
接受我们现在可以非常愉快地调用test.foo(new context());
- 它看起来很像Java中的C仍然如此,我建议如果你最终编写了大量的Java,那可能是代码味道看起来像C。
使用%extend
(手动)将方法添加到上下文类中,test.i中的%extend
变为:
%extend {
context() {
// Constructor that gets called when this object is created from Java:
struct context *ret = NULL;
init_context(&ret);
return ret;
}
~context() {
release_context($self);
}
void foo() {
foo($self);
}
}
与%extend
一样,但是使用类型映射在Java端编写粘合剂:
%typemap(javacode) struct context %{
public void foo() {
$module.foo(this);
}
%}
(注意:这需要在接口文件中足够早才能工作)
请注意,我在这里没有向SWIG显示我的上下文结构的真实定义 - 总是按照我的“库”来处理需要实际定义的任何内容,因此不透明指针保持完全不透明
使用双指针包装init_context
的更简单的解决方案是使用%inline
来提供仅在包装器中使用的额外函数:
%module test
%{
#include "test.h"
%}
%inline %{
struct context* make_context() {
struct context *ctx;
init_context(&ctx);
return ctx;
}
%}
%ignore init_context;
%include "test.h"
足以让我们编写以下Java:
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
// This object behaves exactly like an opaque pointer in C:
SWIGTYPE_p_context ctx = test.make_context();
test.foo(ctx);
// Important otherwise it will leak, exactly like C
test.release_context(ctx);
}
}
替代方案,但类似的方法包括使用cpointer.i library:
%module test
%{
#include "test.h"
%}
%include <cpointer.i>
%pointer_functions(struct context *,context_ptr);
%include "test.h"
然后您可以将其用作:
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
SWIGTYPE_p_p_context ctx_ptr = test.new_context_ptr();
test.init_context(ctx_ptr);
SWIGTYPE_p_context ctx = test.context_ptr_value(ctx_ptr);
// Don't leak the pointer to pointer, the thing it points at is untouched
test.delete_context_ptr(ctx_ptr);
test.foo(ctx);
// Important otherwise it will leak, exactly like C
test.release_context(ctx);
}
}
还有一个pointer_class
宏比OO略多,可能值得使用。关键是你要提供工具来处理SWIG用来表示它一无所知的指针的不透明指针对象,但避免了本质上颠覆类型系统的getCPtr()
调用。
答案 1 :(得分:0)
所以Flexo的答案是解决这个问题的正确方法,但SWIG还提供了“cpointer.i”模块(在此描述:http://www.swig.org/Doc1.3/SWIGDocumentation.html#Library_nn3),它允许我将一个快速解决方案组合在一起,以便我可以测试我让基本库工作。我想我会在这个答案中提出完整性,并为遇到这个问题的人提供另一种选择。
对此的需求是因为我将其描述为SWIG生成的不对称。基类型对象有一个属性swigCPtr,它是该对象的内存地址(“自指针”)。然后要创建一个指针,只需从基类型中获取swigCptr,并将其传递给swig生成的指针类型(SWIGTYPE_p_X)的构造函数。但指针类型仅存在于Java中,只保存swigCptr值。持有该指针的内存没有'c'块。因此,指针类型中没有等效的swigCPtr。即,指针类型中没有存储自指针,就像存在于基类型中的自指针一样。
所以你不能指向一个指针(SWIGTYPE_p_p_X),因为你没有指针的地址在构造它时传递它。
我的新'.i'文件如下:
%module nfc
%{
#include <nfc/nfc.h>
#include <nfc/nfc-types.h>
#include <nfc/nfc-emulation.h>
%}
%include <nfc/nfc.h>
%include <nfc/nfc-types.h>
%include <nfc/nfc-emulation.h>
%include "cpointer.i"
%pointer_functions(nfc_context*, SWIGTYPE_p_p_nfc_context)
最后一个宏的作用是提供4/5函数来制作指针。这些函数都接受并返回swig应该已经生成的类型。新的Java用法使nfc_init和nfc_open命令起作用:
SWIGTYPE_p_p_nfc_context context_p_p = nfc.new_SWIGTYPE_p_p_nfc_context();
nfc.nfc_init(context_p_p);
//get the context pointer after init has set it up (the java doesn't represent what's happening in the c)
SWIGTYPE_p_nfc_context context_p = nfc.SWIGTYPE_p_p_nfc_context_value(context_p_p);
SWIGTYPE_p_nfc_device pnd = nfc.nfc_open(context_p, null);
注意我必须在 init命令完成后从双指针获取指针,因为存储在java指针对象中的信息与C'world'分开。因此,检索context_p_p的'value'将为context_p提供其指针的正确值。