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

2023-12-26

我正在尝试使用 SWIG 来使用适用于 Android 的 Spotify API (libspotify):https://developer.spotify.com/technologies/libspotify/ https://developer.spotify.com/technologies/libspotify/

我在定义 SWIG 接口文件以成功调用以下本机 C 函数时遇到问题:

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 接口和类型映射中,但我在尝试应用examples http://www.swig.org/Doc2.0/SWIGDocumentation.html#Java_struct_pointer_pointer I found http://www.swig.org/Doc2.0/SWIGDocumentation.html#Java_using_typemaps_return_arguments在文档中。


在最基本的层面上,您可以使用以下方式编写可运行的代码指针.i http://www.swig.org/Doc2.0/Library.html#Library_nn4SWIG 库的一部分,允许在 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));
  }
}

在爪哇。我们用SessionHandle_value()引用双指针和new_SessionHandle()为我们创建一个双指针对象。 (还有其他用于处理双指针对象的函数)。


上面的代码可以工作并且包装起来非常简单,但是对于 Java 程序员来说它几乎不“直观”,理想情况下我们会以看起来更像 Java 的方式公开整个库。

Java 程序员期望从创建者函数返回新的会话句柄对象,并且使用异常来指示失败。我们可以让 SWIG 通过一些类型映射和一些谨慎的使用来生成该接口%exception,通过稍微更改接口文件:

%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_session不透明类型映射到“好的”Java 类型,但不允许直接在 Java 中创建/删除该类型。 (如果有一个sp_session_destroy函数可以安排在 Java 对象被销毁时自动调用该函数(如果需要使用 javadestruct 类型映射)。这假的、空的定义结合%nodefaultctor and %nodefaultdtor https://stackoverflow.com/a/10008434/168175为此安排。
  2. 对于我们要返回的输入参数,我们需要将其从 Java 接口中隐藏(使用numinputs=0),然后提供一些东西来代替它在生成的接口的 C 部分中的位置。
  3. 要返回sp_session我们需要调整函数返回的类型映射,而不是错误代码 - 最简单的方法是将它们替换为如果函数被声明为返回 a 时将使用的类型映射sp_session using $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);    
  }
}

这比原来的界面更简单、更直观,但更容易编写。我的倾向是使用高级重命名功能 http://www.swig.org/Doc2.0/SWIG.html#SWIG_advanced_renaming也使类型名称“看起来更像 Java”。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SWIG 接口通过函数参数接收 Java 中的不透明结构引用 的相关文章

随机推荐