4

Javaで使用するには、C++ライブラリをSWIGでラップする必要があります。

私はすでにいくつかの方法が機能していますが、それを解決する方法がわからないという状況に遭遇しました。

私はこのようないくつかの方法があります:

void method1(std::string & name, std::string & result);

bool method2(std::string & name, std::string & alias, std::string & resurnValue, std::string & returnType);

注:実際には、これはMyClassという名前のクラスのメンバーメソッドです。

std::string最初のメソッドを変更して、の代わりにを返すことができvoidます。これでうまくいくはずです。しかし、最後の2つのパラメーターが出力パラメーターである2番目のメソッドを処理する方法がわかりません。char *出力パラメータ(複数のパラメータを渡し、Swig / Pythonを使用してCで文字列を割り当てる)を参照するいくつかの質問を見てきましたが、私の場合はaである必要がありstd::string、SWIGのドキュメントにはこの状況が記載されていません。ここにリンクの説明を入力してください。また、おそらく異なるタイプの3つ以上の出力パラメーターを返すメソッドに遭遇する可能性があります。

最後に、インターフェイスについて少し制御できます。ライブラリへのエントリポイントとして機能するクラスも開発していますが、実際の実装に呼び出しを渡すだけです。

たとえば、これによって私はなんとかのようなメソッドを変更するmethod3(std::string & s)ことmethod3(const std::string & s)ができたので、通常の。でJavaからそれを使用することができましたString

したがって、メソッドの署名を少し変更することは可能ですが、ネイティブメソッドがn個の出力パラメーターを返す場合は、それらすべてを返す必要があります(それぞれを返す新しいメソッドを作成することはできません)。

更新: 私はFlexoによって提供されたソリューションを検討しており、うまく機能していますが、std :: stringをラップするクラスを実行し、それを使用して返された文字列を操作することを検討しています。これは、Flexoの2番目のソリューションと非常によく似たアプローチです。 、ただし、Java文字列配列を使用する代わりにこのStringWrapperを使用すると、基本的に次のようになります。

/*
* The MyClass.i file
*/
%module example

%include "std_string.i"

%{
    class StringPtr{

    private:
        stdString str;

    public:
        StringPtr(){

    }

        StringPtr(const stdString & str){
        this->str = stdString(str);
        }

    stdString & getStrRef(){
        return (this->str);
        }

        stdString getStrVal(){
        return stdString(this->str);
        }

        ~StringPtr(){

        }
    };


%}

/////////////////// Export StringPtr to Java

class StringPtr{

    public:
        StringPtr();

    StringPtr(const stdString & str);

    stdString getStrVal();

    ~StringPtr();

};

// I think this is nor necessary
%rename ("$ignore", fullname=1) "StringPtr::getStrRef";

%extend MyClass {

    void method1(cons std::string & name, StringPtr & result){
        $self->method1(name, result.getStrRef());
    }

    bool method2(cons std::string & name, cons std::string & alias, StringPtr & returnValue, StringPtr & returnType){
        $self->method2(name, alias, returnValue.getStrRef(), returnType.getStrRef());
    }

};

%rename ("$ignore", fullname=1) "MyClass::method1";
%rename ("$ignore", fullname=1) "MyClass::method2";

%include "MyClass.h"

したがって、パフォーマンスの観点から、ウィッチの方が優れているのではないかと思います。構造体ソリューション(Flexoによる)、Flexoによる文字列配列、またはこのポインター(メンバーが1つしかない構造体のように)。

4

3 に答える 3

7

既存のヘッダーファイルを変更せずにこれをラップしたい場合、2つの方法が思い浮かびます。テストに使用したヘッダーファイルを考えると、次のようになります。

#include <string>

inline bool method2(const std::string & name, const std::string & alias, std::string & resurnValue, std::string & returnType) {
  resurnValue = name;
  returnType = alias;
  return true;
}

それをラップする最も簡単な方法は、%inlineすべての出力を1つのタイプにラップするオーバーロードを作成するために使用することです。

%module test

%include <std_string.i>

%{
#include "test.h"
%}

%inline %{
  struct Method2Result {
    bool b;
    std::string s1;
    std::string s2;
  };

  Method2Result method2(const std::string& in1, const std::string& in2) {
    Method2Result ret;
    ret.b = method2(in1,in2,ret.s1,ret.s2);
    return ret;
  }
%}

// Optional: don't wrap the original form of method2 at all:
%ignore method2;

%include "test.h"

これは以下で機能します:

public class run {
  public static void main(String[] args) {
    System.loadLibrary("test");
    Method2Result ret = test.method2("foo", "bar");
    System.out.println(ret.getB() + " - " + ret.getS1() + ", " + ret.getS2());
  }
}

使用することも、使用することもできますがstd::pair、ラッピングは簡単ではないと思います。このように、ライブラリのユーザーが理解できる適切な名前をメンバーに付けることができます。使用せずに、カスタム構造体を記述するよりも冗長になります。。boost::tuple%templateboost::tuplefirstsecond%rename%inline


または、SWIGは、%apply出力引数を作成するために使用できるOUTPUTタイプマップを提供します。これらは1つの要素の配列としてラップされます-配列を渡すセマンティクスは出力引数のセマンティクスと一致します。残念ながらstd::string、typemaps.iにはforがないため、独自に作成する必要があります。理想的にはOUTPUT_TYPEMAP、そのファイルのマクロを再利用し、argoutタイプマップをわずかに変更しただけですが、それが不可能な場合は、それが実行#undefされます。幸い、この場合、複製して変更するのはかなり簡単です。

%module test

%{
#include "test.h"
%}

%typemap(jstype) std::string& OUTPUT "String[]"
%typemap(jtype) std::string& OUTPUT "String[]"
%typemap(jni) std::string& OUTPUT "jobjectArray"
%typemap(javain)  std::string& OUTPUT "$javainput"
%typemap(in) std::string& OUTPUT (std::string temp) {
  if (!$input) {
    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
    return $null;
  }
  if (JCALL1(GetArrayLength, jenv, $input) == 0) {
    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
  }
  $1 = &temp;
}
%typemap(argout) std::string& OUTPUT {
  jstring jvalue = JCALL1(NewStringUTF, jenv, temp$argnum.c_str()); 
  JCALL3(SetObjectArrayElement, jenv, $input, 0, jvalue);
}

%apply  std::string& OUTPUT { std::string & resurnValue }
%apply  std::string& OUTPUT { std::string & returnType }

%include "test.h"

これは次のように使用できます。

public class run {
  public static void main(String[] args) {
    String[] out1 = new String[1];
    String[] out2 = new String[1];
    boolean retb = test.method2("foo", "bar", out1, out2);
    System.out.println(retb + " - " + out1[0] + ", " + out2[0]);
  }
}

これらは両方ともテストされ、私のシステムで動作しました。この場合、私はこの%inlineアプローチが好きです。(メンバー関数の場合は、%extend代わりに使用します)。通常、OUTPUTタイプマップは、追加のコードを記述せずに適用できます。

于 2012-08-15T10:36:32.327 に答える
3

C ++ 11をサポートしている場合は、、、およびのstd::tupleを返すことができます。boolstd::stringstd::string

それ以外の場合は、ネストされたstd::pairsを作成できます。

于 2012-08-14T15:48:27.433 に答える
3

キックのためだけに、これがJavaCPPでそれを行う方法です。

public static native boolean method2(@StdString String name,
        @StdString @Cast("char*") BytePointer alias,
        @StdString @Cast("char*") BytePointer returnValue,
        @StdString @Cast("char*") BytePointer returnType);

public static void main(String[] args) {
    BytePointer alias = new BytePointer();
    BytePointer returnValue = new BytePointer();
    BytePointer returnType = new BytePointer();
    method2("Unknown", alias, returnValue, returnType);
    alias.getString();
    returnValue.getString();
    returnType.getString();
}

SWIGがどのように優れたソリューションであるかを聞きたいです!

于 2012-09-01T04:56:46.977 に答える