2016-05-19 13 views
0

小さなJavaプロジェクトでは、Cで書かれた既存のコード( )とやりとりする必要があったため、残念なことにC/C++プログラマーではありません。私はswigを使うことにしました。swig戻り値の型はstructの変数からjavaの文字列配列

最初の問題は、私が遭遇した: NULLで区切られた文字列を返したC関数が最初の値のみ が3可能な解決策のフレキソで提供することによって解決して含む、文字列を返さ包まれたコードが生じました(!): SWIG get returntype from String as String array in java

このプロジェクトでの開発を継続しながら、私は私を困惑(似た?)のパターンの第2の問題が発生しました:(順番に)含まれている(はずです)という変数itemListのが含まれていることを HEADERFILEは、構造体「PROJECTDETAILS」が含まをNULL区切り文字列 itemListのswigで生成されたgetterは、元のリンクされた質問 (最初の結果を含むString)のGetProjects関数と同様に、最初の結果をStringとして返します。 私はこの問題に提供された答えFlexoをwel

typedef struct _PROJECT 
{ 
    int version; 
    unsigned char vM; 
    unsigned char fM; 
} * PROJECT; 

typedef struct _PROJECTDETAILS 
{ 
    int infoType; 
    union 
    { 
     char *itemList;   /* Returns a NULL-delimited string */ 
     char *projectName; 
    } info; 
} PROJECTDETAILS; 

DllImport PROJECT OpenProject dsproto((int, char *)); 
DllImport int GetProjectDetails dsproto((PROJECT, int, PROJECTDETAILS *)); 

答えて

1

私はのセマンティクスを前提と何のダミー実装を書いて開始するには、しかし、私は「タイプマップ」itemListの変数

Cヘッダファイルの状態で機能することはできませんよ参照した2つの関数は、

です。

これは、私がmy previous answerで与えた答えと組み合わせて、(Javaの開発者の視点では)やっかいではあるが、itemListのメンバーを働かせるには十分である。

前の回答のタイプマップを正しく使用するには、何を呼び出すべきか(または%applyのために何を書くべきか)を調べるだけです。 SWIGはこれを把握するのに役立つ便利な方法を持っています。コマンドライン引数-debug-tmsearchには、何も入力されなかったために考慮され無視されたすべての候補が出力されます。だから私は走った:

swig3.0 -java -Wall -debug-tmsearch test.i 

それは私たちが前の答えからのタイプマップとそれをマッチさせる方法を示している。 char *_PROJECTDETAILS::_PROJECTDETAILS_info::itemListは、私たちが私たちの前のタイプマップを適用したいPROJECTDETAILS::info::itemListメンバーのための最も厳しい試合であることを示している

test.h:16: Searching for a suitable 'out' typemap for: char *_PROJECTDETAILS::_PROJECTDETAILS_info::itemList 
    Looking for: char *_PROJECTDETAILS::_PROJECTDETAILS_info::itemList 
    Looking for: char *itemList 
    Looking for: char * 
    Using: %typemap(out) char * 

。だから、私たちは(複数の用途にそれらを一致させるために%applyを使用してもまたは)異なっこの回答や試合の前のタイプマップを使用することができ、何かのように:

%module test 

%{ 
#include "test.h" 
#include <assert.h> 
%} 

// See part 2 for discusson of these 
%rename("%(strip:[_])s") ""; 
%immutable _PROJECTDETAILS::infoType; 

%typemap(jni) char * _PROJECTDETAILS::_PROJECTDETAILS_info::itemList "jobjectArray"; 
%typemap(jtype) char * _PROJECTDETAILS::_PROJECTDETAILS_info::itemList "String[]"; 
%typemap(jstype) char * _PROJECTDETAILS::_PROJECTDETAILS_info::itemList "String[]"; 
%typemap(javaout) char * _PROJECTDETAILS::_PROJECTDETAILS_info::itemList { 
    return $jnicall; 
} 
%typemap(out) char * _PROJECTDETAILS::_PROJECTDETAILS_info::itemList { 
    size_t count = 0; 
    const char *pos = $1; 
    while (*pos) { 
    while (*pos++); // SKIP 
    ++count; 
    } 
    $result = JCALL3(NewObjectArray, jenv, count, JCALL1(FindClass, jenv, "java/lang/String"), NULL); 
    pos = $1; 
    size_t idx = 0; 
    while (*pos) { 
    jobject str = JCALL1(NewStringUTF, jenv, pos); 
    assert(idx<count); 
    JCALL3(SetObjectArrayElement, jenv, $result, idx++, str); 
    while (*pos++); // SKIP 
    } 
    //free($1); // Iff you need to free the C function's return value 
} 

%include "test.h" 

は、これはそれがあった主な理由は、前の回答から、方法2を選びました完全に型マップに基づいているので、-debug-tmsearch SWIG引数のより良い例です。我々はとしてそれを使用することができます十分だ

import java.util.Arrays; 

public class run { 
    public static void main(String[] argv) { 
    System.loadLibrary("test"); 
    PROJECT p = test.OpenProject(1,"???"); 
    PROJECTDETAILS pd1 = new PROJECTDETAILS(); 
    test.GetProjectDetails(p, 1, pd1); 
    System.out.println(Arrays.toString(pd1.getInfo().getItemList())); 
    } 
} 

しかし、我々はちょうどGetProjectDetailsに引数として渡すために新しいPROJECTDETAILSオブジェクトを作成し、Javaのユーザーのためのものよりもはるかに良い行うことができますは少し奇妙です。


あなたは char *は珍しいセマンティクスを持つだけのメンバ変数のほかに、やりたいかなりの数のものがあるのJavaにきちんとこれをラップします。

最初に、あなたが持っているいくつかの構造体の名前を変更したいと思います。これは、高度な名前変更ストリップ演算子を使用してSWIG 2.0以降で行うことができます。

次に、メンバー自身をラップする方法を決定する必要があります。 Cの典型的なデザインパターンは、intを使用して、指定されたオブジェクトに対して、どのユニオンのメンバーが正しいタイプであるかを示すことです。 Pythonでは、それぞれのケースで異なるタイプを返し、ダックタイピングに頼っています。

  1. あなたはクラス階層を定義し、右の型にキャストする方法を見つけ出すためにinstanceof(または単にint型)を使用することができますJavaの場合賢明だろうさまざまなオプションがいくつかあります。
  2. そのままの状態でCのセマンティクスを反映させることができます。 (「間違った」メンバーに技術的にアクセスすることは未定義の動作ですが、これはJava開発者にとっては直感的ではありません)。
  3. 「間違った」メンバにアクセスしようとすると、例外を発生させる型を返すか、代わりにNULLを返すことができます。

オプション2は、この回答の以前の部分です。私の考え方では、オプション3はおそらくJavaプログラマにとって最も予測可能な動作なので、ここでやったことがあります。

第3の楽しい決定は、出力関数の引数を処理する方法です。この例では、より多くのJavaコードをC/JNIよりも好むソリューションを選ぶつもりですが、以前の回答と同じトレードオフがここでも適用されます。

私がしたことは、SWIGにPROJECTDETAILS::infoを完全に無視させ、アンダースコアの接頭辞を付けた構造体の名前を変更することです。

次に、ヘッダファイル内のGetProjectDetailsのバージョンをプライベートにし、接尾辞Implを追加して、ラッパーの内部以外の誰かがそれに触れることを意図していないことを示しました。

モジュール自体の中に、別の公開バージョンGetProjectDetailsを追加して、新しいオブジェクトが出力専用引数として構成されていることを隠すと、戻り値の型がこれを返すように変更されます。また、 'intを使用して、' C言語のコーディングスタイルがJavaの '正しくない場合に例外をスローする'ことを示します。 (これを行うよりもSWIGにはもっと多くの方法がありますが、C/JNIの学習曲線を最小限に抑えてそのようにしています)。

次に、ラップされたPROJECTDETAILS構造体に、2つの追加の読み取り専用メンバー変数を追加します。これらは実際にC言語では直接存在しないので、特別なCコードによって中間インタフェースのグルーコードに実装されています。このコードのポイントは、タイプを示すintメンバを使用して、実際にunionがどのケースにあるかをチェックすることです。型が正しくない場合、nullを返します(ただし、CまたはJavaのグルーコードを使用すると例外が発生する可能性があります)。

これで済むのは、以前の回答のtypemapsを再利用して、言語の境界を越えて正しく機能するセマンティクスを取得することだけです。もう一度、ここでメソッド2を使用しました。ヌルを返す関数のマイナーな問題以外は変更されていません。

import java.util.Arrays; 

public class run { 
    public static void main(String[] argv) { 
    System.loadLibrary("test"); 
    PROJECT p = test.OpenProject(1,"???"); 

    System.out.println("PD1"); 
    PROJECTDETAILS pd1 = test.GetProjectDetails(p, 1); 
    System.out.println(Arrays.toString(pd1.getItemList())); 
    System.out.println(pd1.getProjectName()); 

    System.out.println("PD2"); 
    PROJECTDETAILS pd2 = test.GetProjectDetails(p, 2); 
    System.out.println(Arrays.toString(pd2.getItemList())); 
    System.out.println(pd2.getProjectName()); 
    } 
} 

:その後で動作します

%module test 

%{ 
#include "test.h" 
#include <assert.h> 
%} 

%rename("%(strip:[_])s") ""; 
%immutable _PROJECTDETAILS::infoType; 
%ignore info; // Ignore the member 
%ignore _PROJECTDETAILS_info; // Ignore the anonymous type 
%javamethodmodifiers GetProjectDetails "private"; 
%rename(GetProjectDetailsImpl) GetProjectDetails; 

%typemap(jni) char *_PROJECTDETAILS::itemList "jobjectArray"; 
%typemap(jtype) char *_PROJECTDETAILS::itemList "String[]"; 
%typemap(jstype) char *_PROJECTDETAILS::itemList "String[]"; 
%typemap(javaout) char *_PROJECTDETAILS::itemList { 
    return $jnicall; 
} 
%typemap(out) char *_PROJECTDETAILS::itemList { 
    if (!$1) return NULL; // This fixes a possible bug in my previous answer 
    size_t count = 0; 
    const char *pos = $1; 
    while (*pos) { 
    while (*pos++); // SKIP 
    ++count; 
    } 
    $result = JCALL3(NewObjectArray, jenv, count, JCALL1(FindClass, jenv, "java/lang/String"), NULL); 
    pos = $1; 
    size_t idx = 0; 
    while (*pos) { 
    jobject str = JCALL1(NewStringUTF, jenv, pos); 
    assert(idx<count); 
    JCALL3(SetObjectArrayElement, jenv, $result, idx++, str); 
    while (*pos++); // SKIP 
    } 
    //free($1); // Iff you need to free the C function return value 
} 

%pragma(java) modulecode=%{ 
    public static PROJECTDETAILS GetProjectDetails(PROJECT p, int a) { 
    PROJECTDETAILS out = new PROJECTDETAILS(); 
    final int ret = GetProjectDetailsImpl(p,a,out); 
    if (0!=ret) { 
     // assuming this is an error throw something 
    } 
    return out; 
    } 
%} 

%extend _PROJECTDETAILS { 
    const char *itemList const { 
    if ($self->infoType != INFOTYPE_ITEMLIST) { 
    // Throw a Java exception here instead? That is another question... 
    return NULL; 
    } 
    return $self->info.itemList; 
    } 
    const char *projectName const { 
    if ($self->infoType != INFOTYPE_PROJECTNAME) { 
     // Throw exception? 
     return NULL; 
    } 
    return $self->info.projectName; 
    } 
} 

%include "test.h" 

+0

(注_資本文字が続くで始まるものは、おそらくないあなたのせいではなく、正確に素晴らしいではありませんC reserved nameです)遅い返信のための私の謝罪!私は両方の提案された実装を試して、彼らは完全に動作します! 私は本当にあなたが提供した明確で広範な説明を本当に感謝していると言わなければなりません。実際に何が起きているかをよりよく理解するのに役立ちます(単にコピー&ペーストから私の指を横切り、 – c3po

+0

ヘッダーファイル内の別の関数はPROJECTDETAILS構造体を入力パラメータとして取ります。SWIG生成コードでPROJECTDETAILSのinfoTypeを設定できますが、infoTypeに関連付けられた変数の値を設定することはできません。 projectName。あなたがそれを設定できる '%immutable'を削除した場合、これを推測するには%typemap – c3po

+0

@ c3poを追加する必要がありますが、既存の値を自由にするなど何かをする必要がある場合は、 – Flexo

関連する問題