SWIG接口通过函数参数在Java中接收不透明的结构引用

Bee*_*eeo 7 java swig spotify

我正在尝试使用SWIG以便为Android使用Spotify API(libspotify):https: //developer.spotify.com/technologies/libspotify/

我无法定义SWIG接口文件以便能够成功调用以下本机C函数:

sp_error sp_session_create(const sp_session_config * config, sp_session ** sess);
Run Code Online (Sandbox Code Playgroud)

在C中将被称为这样:

//config struct defined previously
sp_session *sess;
sp_session_create(&config, &sess);
Run Code Online (Sandbox Code Playgroud)

但在Java中我需要这样称呼它:

//config object defined previously
sp_session javaSess = new sp_session();
sp_session_create(config, javaSess);
Run Code Online (Sandbox Code Playgroud)

sp_session是一个不透明的结构,只在libspotify的API.h文件中定义为:

typedef struct sp_session sp_session;
Run Code Online (Sandbox Code Playgroud)

我期待libspotify库创建它并给我一个引用.那时我唯一需要引用的是传递给API中的其他函数.

我相信答案在于SWIG界面和类型地图,但我尝试应用我在文档中找到示例时未能成功.

Fle*_*exo 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) {}
Run Code Online (Sandbox Code Playgroud)

你可以用它包装:

%module spotify

%{
#include "test.h"
%}

%include "test.h"

%include <cpointer.i>

%pointer_functions(sp_session *, SessionHandle)
Run Code Online (Sandbox Code Playgroud)

然后,我们可以写下这样的内容:

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));
  }
}
Run Code Online (Sandbox Code Playgroud)

在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"
Run Code Online (Sandbox Code Playgroud)

编号的评论对应于以下几点:

  1. 我们希望sp_sessionopaque类型映射到"nice"Java类型,但不允许直接在Java中创建/删除类型.(如果有一个sp_session_destroy函数可以安排在Java对象被销毁时自动调用,如果需要使用javadestruct类型映射).该假的,空的定义与结合%nodefaultctor,并%nodefaultdtor安排这一点.
  2. 对于我们正在进行返回的输入参数,我们需要将其从Java接口(使用numinputs=0)中隐藏,然后提供一些内容以在接口的生成的C部分中取代它.
  3. 要返回sp_session代替错误代码,我们需要调整函数返回的类型映射 - 最简单的方法是将它们替换为在函数声明为返回sp_sessionusing时使用的类型映射$typemap.
  4. 就输出而言,我们不想做通常被封送的任何事情,但我们确实希望返回我们用作2中额外输入参数的占位符的指针.
  5. 最后,我们希望将整个调用包含sp_session_create在一些代码中,这些代码将检查实际的返回值,并在指示失败时将其映射到Java异常.我也手动编写了以下异常类:

    public class SpotifyException extends Exception {
      public SpotifyException(String reason) {
        super(reason);
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)

完成所有这些工作后,我们现在可以在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);    
  }
}
Run Code Online (Sandbox Code Playgroud)

这比原始的更简单,更直观,但编写界面更简单.我倾向于使用高级重命名功能使类型名称"看起来更像Java".