SWIG接口通过函数参数接收Java中的不透明结构引用 [英] SWIG interface to receive an opaque struct reference in Java through function argument

查看:156
本文介绍了SWIG接口通过函数参数接收Java中的不透明结构引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to use SWIG in order to use the Spotify API (libspotify) for Android: https://developer.spotify.com/technologies/libspotify/

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

I am having trouble defining the SWIG interface file to be able to successfully call the following native C function:

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中,我需要这样称呼它:

But in Java I would need to call it like this:

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

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

sp_session is an opaque struct and is only defined in libspotify's API.h file as:

typedef struct sp_session sp_session;

我期望libspotify库能够创建它并给我一个参考.我唯一需要的参考是传递给API中的其他函数.

I'm expecting the libspotify library to create it and give me a reference to it. The only thing I need that reference for then is to pass to other functions in the API.

我相信答案就在SWIG界面和类型映射之内,但是我一直未能尝试应用示例我在以下内容中找到文档.

I believe the answer lies within the SWIG interface and typemaps, but I have been unsuccessful in trying to apply the examples I found in the documentation.

推荐答案

在最基本的级别上,您可以使用

At the most basic level you can make code that works using the cpointer.i part of the SWIG library to allow a direct "pointer to pointer" object to be created in 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)

然后,我们可以这样写:

Which then allows us to write something like:

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()为我们创建双指针对象. (还有其他用于处理双指针对象的功能.)

in Java. We use SessionHandle_value() to derference the double pointer and new_SessionHandle() to create a double pointer object for us. (There are other functions for working with the double pointer object).

上面的方法很有效,并且包装起来很简单,但是对于Java程序员来说这并不是直观"的,理想情况下,我们将整个库公开为看起来更像Java的东西.

The above works and is very simple to wrap, but it's hardly "intuitive" for a Java programmer and ideally we'd expose the whole library in something that looks more like Java.

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

A Java programmer would expect that the new session handle object would be returned from the creator function and that an exception would be used to indicate failures. We can make SWIG generate that interface with a few typemaps and some careful use of %exception, by changing the interface file somewhat:

%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"

编号注释与以下几点相对应:

The numbered comments correspond with these points:

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

  1. We want the sp_session opaque type to map to a "nice" Java type but not allow creation/deletion of the type directly within Java. (If there is a sp_session_destroy function to could arrange for that to get automatically called when the Java object is destroyed if that's desirable using the javadestruct typemap). The fake, empty definition combined with %nodefaultctor and %nodefaultdtor arranges for this.
  2. For the input parameter which we're making into a return instead we need to hide it from the Java interface (using numinputs=0) and then supply something to take its place in the generated C part of the interface.
  3. To return the sp_session instead of the error code we need to adjust the typemaps for the return from the function - the simplest way to do that is to substitute them for the typemaps that would have been used if the function was declared as returning a sp_session using $typemap.
  4. As far as the output is concerned we don't want to do anything where it would usually be marshaled, but we do want to return the pointer we used as a placeholder for the extra input parameter in 2.
  5. Finally we want to enclose the whole call to sp_session_create in some code that will check the real return value and map that to a Java exception should it indicate failure. I wrote the following exception class by hand for that as well:

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

已经完成了所有这些工作,我们现在可以在Java代码中按如下方式使用它:

Having done all this work we are now in a position to use that in Java code as follows:

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);    
  }
}

与原始界面相比,它比原始界面更简单,更直观.我的倾向是使用高级重命名功能来创建类型还命名为"look more Java".

Which is vastly simpler and more intuitive than the original but simpler to write interface. My inclination would be to use the advanced renaming feature to make the type names "look more Java" also.

这篇关于SWIG接口通过函数参数接收Java中的不透明结构引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆