我正在尝试使用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);
在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中的其他函数。
答案 0 :(得分:5)
在最基本的层面上,您可以使用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程序员会期望从创建者函数返回新的会话句柄对象,并且将使用异常来指示失败。通过稍微更改接口文件,我们可以使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"
编号的注释与这些点相对应:
sp_session
opaque类型映射到“nice”Java类型,但不允许直接在Java中创建/删除类型。 (如果有一个sp_session_destroy
函数可以安排在Java对象被销毁时自动调用,如果需要使用javadestruct类型映射)。 fake, empty definition combined with %nodefaultctor
and %nodefaultdtor
安排此事。numinputs=0
)隐藏,然后提供一些内容以在接口的生成的C部分中取代它。 sp_session
而不是错误代码,我们需要调整函数返回的类型映射 - 最简单的方法是将它们替换为函数时使用的类型映射声明使用sp_session
返回$typemap
。最后,我们希望在一些代码中将整个调用括在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);
}
}
这比原始界面更简单,更直观,但编写界面更简单。我倾向于使用advanced renaming feature使类型名称“看起来更像Java”。
答案 1 :(得分:0)
您应该告知swig您的typedef
声明。为此,您应该使用以下命令编辑接口文件:
typedef struct sp_session sp_session;
但是在看到任何sp_session之前,请小心告知SWIG你的typedef(我的意思是在包含API.h之前)。 我对typedef识别有同样的问题。也许这link会有所帮助。