SWIG+JAVA(%extends)
JNAは楽だが、どうも遅いらしいのでSWIG+JAVA(JNI)を利用。
情報が不足気味。公式の英語ドキュメントをじっくり読んで試すしかない。
このドキュメントの問題点は、どうJNIのC/C++コードに反映されるのか、Javaにどう反映されるのかわからない点にある。
特に%extendsの動作がよくわからなかったため、試してみた。
試してみた結果、以下の結果であることがわかった。
- swigの設定ファイルは、C++のクラスで記述
- 変換後には、%extendsのC++のメソッドの宣言がJavaコードに変換されクラスへ反映されるが、C/C++側は、C++でもクラス構成がないフラットなCの関数呼び出しとなり、クラス構成が反映されない。よって、C++ではクラス変数の設定ができない。(%typemap(javacode)を併用するとJava側ではクラス変数、メソッドの定義が可能)
下手に継承したりすると、メモリサイズとかが変わり不都合が生じる可能性があるため、このような実装になっていると思われる。
サンプル例
たとえば、epoll_eventのようにstructで配列が必要なものがある。
struct epoll_event { uint32_t events; /* epoll イベント */ epoll_data_t data; /* ユーザデータ変数 */ };
単純に変換しただけだと、epoll_eventはポインタにしかならない。
このため、carrays.iを使って、epoll_eventをラップして配列用のクラスを作る。(なお、arrays_java.iは、プリミティブ型のみ使えて、そもそもコピーが生じる)
%include "carrays.i" %array_class(struct epoll_event, epoll_event_array);
なお、carrays.iの%array_classは、
%extend NAME { #if __cplusplus NAME(int nelements) { return new TYPE[nelements]; } ~NAME() { delete [] self; } #else NAME(int nelements) { return (TYPE *) calloc(nelements,sizeof(TYPE)); } ~NAME() { free(self); } #endif TYPE getitem(int index) { return self[index]; } void setitem(int index, TYPE value) { self[index] = value; } TYPE * cast() { return self; } static NAME *frompointer(TYPE *t) { return (NAME *) t; } };
SWIGでの変換後
この場合、Cのコードとして、
typedef struct epoll_event epoll_event_array; SWIGINTERN epoll_event_array *new_epoll_event_array(int nelements){ return (struct epoll_event *) calloc(nelements,sizeof(struct epoll_event)); } SWIGINTERN void delete_epoll_event_array(epoll_event_array *self){ free(self); } SWIGINTERN struct epoll_event epoll_event_array_getitem(epoll_event_array *self,int index){ return self[index]; } SWIGINTERN void epoll_event_array_setitem(epoll_event_array *self,int index,struct epoll_event value){ self[index] = value; } SWIGINTERN struct epoll_event *epoll_event_array_cast(epoll_event_array *self){ return self; } SWIGINTERN epoll_event_array *epoll_event_array_frompointer(struct epoll_event *t){ return (epoll_event_array *) t; }
が生成される。C++オプションを使ってもほぼ同じ。
対応するJavaのコードは、
... //swigが自動生成するコンストラクタ //protectedであることに注意。すなわちextendしないと、Java側からはオブジェクトを生成できない protected epoll_event(long cPtr, boolean cMemoryOwn) { swigCMemOwn = cMemoryOwn; swigCPtr = cPtr; } ... //JavaのGCのタイミングにあわせて、解放されていなければ解放する。 //よって、Ownerでない領域は、OwnerがGCで回収されると、存在しなくなる。 protected void finalize() { delete(); } //publicなのでユーザが明示的に解放してもよい。 public synchronized void delete() { if(swigCPtr != 0 && swigCMemOwn) { swigCMemOwn = false; epollJNI.delete_epoll_event_array(swigCPtr); } swigCPtr = 0; } //carrays.iによって生成されるコンストラクタ //publicなのでJavaから生成可能 public epoll_event_array(int nelements) { this(epollJNI.new_epoll_event_array(nelements), true); } //配列からepoll_eventを取得 public epoll_event getitem(int index) { return new epoll_event(epollJNI.epoll_event_array_getitem(swigCPtr, this, index), true); } //配列へepoll_eventを設定 public void setitem(int index, epoll_event value) { epollJNI.epoll_event_array_setitem(swigCPtr, this, index, epoll_event.getCPtr(value), value); } //epoll_eventへのキャスト public epoll_event cast() { long cPtr = epollJNI.epoll_event_array_cast(swigCPtr, this); return (cPtr == 0) ? null : new epoll_event(cPtr, false); } //epoll_eventからのキャスト public static epoll_event_array frompointer(epoll_event t) { long cPtr = epollJNI.epoll_event_array_frompointer(epoll_event.getCPtr(t), t); return (cPtr == 0) ? null : new epoll_event_array(cPtr, false); }
となり、きちんとラップされていることがわかる。
epoll_eventとのJavaコードでの変換は、
epoll_event ee = array.cast(); epoll_event_array eea = epoll_event_array.frompointer(event);
で相互変換できる。