3

そこで、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

明らかに、私が提供した情報から特定の解決策が得られない場合があります。しかし、試してみるべきことの提案は本当にありがたいです(私はここで私の知識の終わりにいます)。

4

2 に答える 2

5

常に同じポインタを関数に自動的に渡すことは、SWIG では非常に簡単です。たとえば、問題のコア部分をキャプチャする「ヘッダー」ファイル 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 *ctxJava から入力を取得する代わりに、get_global_ctx()一致するすべての場所で自動的に呼び出しを行うタイプマップを設定します。

Java 開発者が使用する健全なインターフェースを作成するには、おそらくこれで十分ですが、理想的とは言えません。これは、コンテキストをグローバルにすることを強制し、Java アプリケーションが一度に複数のコンテキストを処理できないことを意味します。

Java が OO 言語であることを考えると、より適切な解決策は、コンテキストをファースト クラス オブジェクトにすることです。少し複雑ではありますが、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 では、さまざまな方法でこれを行うことができます。

  1. これで非常に満足して呼び出すことができることを受け入れてくださいtest.foo(new context());- Java の C によく似ていますが、C のように見える Java をたくさん書くことになった場合は、コードのにおいがする可能性があることをお勧めします。

  2. %extend(手動で) メソッドをコンテキスト クラスに追加するために使用します%extend。 in 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);
        }
      }
    
  3. と同様%extendですが、タイプマップを使用して Java 側で接着剤を記述します。

    %typemap(javacode) struct context %{
      public void foo() {
        $module.foo(this);
      }
    %}
    

    (注: これが機能するには、インターフェース ファイルの初期段階にある必要があります)

ここでは、コンテキスト構造体の実際の定義を SWIG に示していないことに注意してください。実際の定義が必要な場合は、常に「ライブラリ」に従います。したがって、不透明なポインターは完全に不透明なままです。


init_contextを double ポインターでラップするより簡単な解決策%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 ライブラリの使用が含まれます。

%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 であり、代わりに使用する価値があるマクロもあります。getCPtr()ポイントは、SWIG が何も知らないポインターを表すために使用する不透明なポインター オブジェクトを操作するためのツールを提供していることですが、本質的に型システムを破壊する呼び出しを回避しているということです。

于 2013-01-20T18:33:05.610 に答える
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 が既に生成しているはずの型を取り、返します。新しい nfc_init および nfc_open コマンドを動作させるための Java での使用法:

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

Java ポインタ オブジェクトに格納されている情報は C の「ワールド」から分離されているため、init コマンドの完了にダブル ポインタからポインタを取得する必要があることに注意してください。そのため、context_p_p の「値」を取得すると、そのポインターの正しい値を持つ context_p が得られます。

于 2013-01-23T22:52:23.960 に答える