使用SWIG创建java共享库时出现SIGSEGV错误

Sam*_*Sam 3 java swig nfc segmentation-fault

所以,我正在尝试使用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
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

即它应该包括libnfc提供的所有方法.

这是我得到的错误日志:http://openetherpad.org/AyVDsO4XTg

显然,可能无法从我提供的信息中获得特定的解决方案.但任何有关尝试的事情的建议都会非常感激(我的知识就在这里).

Fle*_*exo 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); }
Run Code Online (Sandbox Code Playgroud)

我们可以将它包装起来并自动导致全局上下文在任何预期的位置传递,例如:

%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"
Run Code Online (Sandbox Code Playgroud)

这为struct context *ctx它设置了一个类型映射,而不是从Java中获取输入,自动调用get_global_ctx()它匹配的任何地方.

这可能足以为Java开发人员提供一个理智的界面,但它不太理想:它强制上下文成为全局,并且意味着没有Java应用程序可以同时使用多个上下文.

考虑到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"
Run Code Online (Sandbox Code Playgroud)

我们可以成功运用此代码:

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.
  }
}
Run Code Online (Sandbox Code Playgroud)

得到:

Init: 0xb66dab40
Delete: 0xb66dab40
Run Code Online (Sandbox Code Playgroud)

在动态类型语言中,这将是艰难的部分 - 我们可以使用一种或另一种形式的元编程来根据需要插入成员函数.因此,我们可以new context().foo();完全按照预期说出类似的话.Java是静态类型的,所以我们需要更多东西.我们可以通过多种方式在SWIG中执行此操作:

  1. 接受我们现在可以test.foo(new context());非常愉快地调用它 - 它看起来很像Java中的C仍然如此,我建议如果最终编写大量看起来像C的Java,它可能是代码味道.

  2. 使用%extend至(手动地)添加方法进上下文类,则%extend在test.i变为:

    %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);
        }
      }
    
    Run Code Online (Sandbox Code Playgroud)
  3. %extend使用类型映射一样,但在Java端编写粘合剂:

    %typemap(javacode) struct context %{
      public void foo() {
        $module.foo(this);
      }
    %}
    
    Run Code Online (Sandbox Code Playgroud)

    (注意:这需要在接口文件中足够早才能工作)

请注意,我在这里没有向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"
Run Code Online (Sandbox Code Playgroud)

足以让我们编写以下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);
  }
}
Run Code Online (Sandbox Code Playgroud)

替代方案,但类似的方法将包括使用cpointer.i库:

%module test

%{
#include "test.h"
%}

%include <cpointer.i>

%pointer_functions(struct context *,context_ptr);

%include "test.h"
Run Code Online (Sandbox Code Playgroud)

您可以将其用作:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

还有一个pointer_class宏比OO多一点,可能值得使用.关键是你要提供工具来处理SWIG用来表示它一无所知的指针的不透明指针对象,但避免了getCPtr()本质上颠覆类型系统的调用.