7

Android 用の Spotify API (libspotify) を使用するために SWIG を使用しようとしています: https://developer.spotify.com/technologies/libspotify/

次のネイティブ C 関数を正常に呼び出せるように SWIG インターフェイス ファイルを定義するのに問題があります。

sp_error sp_session_create(const sp_session_config * config, sp_session ** sess);

C では次のように呼び出されます。

//config struct defined previously
sp_session *sess;
sp_session_create(&config, &sess);

しかし、Java では次のように呼び出す必要があります。

//config object defined previously
sp_session javaSess = new sp_session();
sp_session_create(config, javaSess);

sp_session は不透明な構造体であり、libspotify の API.h ファイルでのみ次のように定義されています。

typedef struct sp_session sp_session;

libspotify ライブラリがそれを作成し、参照してくれることを期待しています。そのためにその参照が必要なのは、API の他の関数に渡すことだけです。

答えは SWIG インターフェイスとタイプマップにあると思いますが、ドキュメントで見つけたを適用しようとして失敗しました。

4

2 に答える 2

6

最も基本的なレベルでは、SWIG ライブラリのcpointer.i部分を使用して動作するコードを作成し、Java で直接「ポインタからポインタへ」オブジェクトを作成できるようにします。

たとえば、ヘッダー ファイルが与えられた場合:

#include <stdlib.h>

typedef struct sp_session sp_session;

typedef struct {} sp_session_config;

typedef int sp_error;

inline sp_error sp_session_create(const sp_session_config *config, sp_session **sess) {
  // Just for testing, would most likely be internal to the library somewhere
  *sess = malloc(1);
  (void)config;
  return sess != NULL;
}

// Another thing that takes just a pointer
inline void do_something(sp_session *sess) {}

あなたはそれをラップすることができます:

%module spotify

%{
#include "test.h"
%}

%include "test.h"

%include <cpointer.i>

%pointer_functions(sp_session *, SessionHandle)

次に、次のようなものを書くことができます。

public class run {
  public static void main(String[] argv) {
    System.loadLibrary("test");
    SWIGTYPE_p_p_sp_session session = spotify.new_SessionHandle();
    spotify.sp_session_create(new sp_session_config(), session);
    spotify.do_something(spotify.SessionHandle_value(session));
  }
}

Javaで。を使用SessionHandle_value()して、ダブル ポインターを参照new_SessionHandle()し、ダブル ポインター オブジェクトを作成します。(ダブル ポインター オブジェクトを操作する関数は他にもあります)。


上記は機能し、ラップするのは非常に簡単ですが、Java プログラマーにとって「直感的」とは言い難いので、ライブラリ全体を Java に似た形で公開するのが理想的です。

Java プログラマーは、作成者関数から新しいセッション ハンドル オブジェクトが返され、失敗を示すために例外が使用されることを期待します。%exceptionインターフェイス ファイルを多少変更することで、SWIG にそのインターフェイスを生成させることができます。

%module spotify

%{
#include "test.h"
%}

// 1:
%nodefaultctor sp_session;
%nodefaultdtor sp_session;
struct sp_session {};

// 2:
%typemap(in,numinputs=0) sp_session ** (sp_session *tptr) {
  $1 = &tptr;
}

// 3:
%typemap(jstype) sp_error sp_session_create "$typemap(jstype,sp_session*)"
%typemap(jtype) sp_error sp_session_create "$typemap(jtype,sp_session*)"
%typemap(jni) sp_error sp_session_create "$typemap(jni,sp_session*)";
%typemap(javaout) sp_error sp_session_create "$typemap(javaout,sp_session*)";

// 4:
%typemap(out) sp_error sp_session_create ""
%typemap(argout) sp_session ** {
  *(sp_session **)&$result = *$1;
}

// 5:
%javaexception("SpotifyException") sp_session_create {
  $action
  if (!result) {
    jclass clazz = JCALL1(FindClass, jenv, "SpotifyException");
    JCALL2(ThrowNew, jenv, clazz, "Failure creating session");
    return $null;
  }
}

%include "test.h"

番号付きのコメントは、次の点に対応しています。

  1. sp_sessionopaque 型を「適切な」Java 型にマップする必要がありますが、Java 内で型を直接作成/削除することはできません。( sp_session_destroyjavadestruct typemap を使用して望ましい場合、Java オブジェクトが破棄されたときに自動的に呼び出されるように調整できる関数がある場合)。偽の空の定義は、これと組み合わされ%nodefaultctor%nodefaultdtor配置されます。
  2. 代わりに、戻り値にする入力パラメータについては、( を使用してnuminputs=0) Java インターフェースから非表示にし、インターフェースの生成された C 部分で代わりになるものを提供する必要があります。
  3. エラー コードの代わりに を返すには、関数からの戻り値に合わせて型マップを調整する必要があります。これを行う最も簡単な方法は、関数がusingsp_sessionを返すように宣言されている場合に使用される型マップをそれらに置き換えることです。sp_session$typemap
  4. 出力に関する限り、通常はマーシャリングされる場所では何もしたくありませんが、2 で追加の入力パラメーターのプレースホルダーとして使用したポインターを返したいと考えています。
  5. 最後に、呼び出し全体を何らかのコードで囲み、sp_session_create実際の戻り値をチェックして、それが失敗を示している場合にそれを Java 例外にマップします。そのために、次の例外クラスも手動で作成しました。

    public class SpotifyException extends Exception {
      public SpotifyException(String reason) {
        super(reason);
      }
    }
    

このすべての作業が完了したので、次のように Java コードでそれを使用できるようになりました。

public class run {
  public static void main(String[] argv) throws SpotifyException {
    System.loadLibrary("test");
    sp_session handle = spotify.sp_session_create(new sp_session_config());
    spotify.do_something(handle);    
  }
}

これは、元のインターフェースよりもはるかにシンプルで直感的ですが、インターフェースを書くのが簡単です。私の傾向は、高度な名前変更機能を使用して、タイプ名を「より Java に見える」ようにすることです。

于 2012-10-07T09:33:54.400 に答える
0

あなたのtypedef宣言について swig に通知する必要があります。これを行うには、インターフェース ファイルを次のように編集する必要があります。

typedef struct sp_session sp_session;

ただし、sp_session を確認する前に (API.h をインクルードする前に)、SWIG に typedef を通知するように注意してください。typedef 認識にも同じ問題がありました。たぶん、このリンクが役立ちます。

于 2012-10-07T09:35:24.803 に答える