常に同じポインタを関数に自動的に渡すことは、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 *ctx
Java から入力を取得する代わりに、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 では、さまざまな方法でこれを行うことができます。
これで非常に満足して呼び出すことができることを受け入れてくださいtest.foo(new context());
- Java の C によく似ていますが、C のように見える Java をたくさん書くことになった場合は、コードのにおいがする可能性があることをお勧めします。
%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);
}
}
と同様%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 が何も知らないポインターを表すために使用する不透明なポインター オブジェクトを操作するためのツールを提供していることですが、本質的に型システムを破壊する呼び出しを回避しているということです。