当前位置: 首页 > 知识库问答 >
问题:

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

高钱青
2023-03-14

我试图使用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_会话是一个不透明的结构,仅在libspotify的API中定义。h文件格式为:

typedef struct sp_session sp_session;

我期待libspotify库创建它,并给我一个参考。那时我唯一需要的就是将该引用传递给API中的其他函数。

我相信答案就在SWIG界面和打字地图中,但我一直没有成功地尝试应用我在留档中找到的示例。

共有2个答案

仉洲
2023-03-14

您应该将您的typedef声明告知swig。为了做到这一点,您应该使用以下工具编辑界面文件

typedef struct sp_session sp_session;

但是要注意在SWIG看到任何sp_session之前通知你的typedef(我的意思是在包括API. h之前)。我在typedef识别方面也遇到了同样的问题。也许这个链接会有所帮助。

徐学潞
2023-03-14

在最基本的层面上,您可以使用cpointer编写代码。我是SWIG库的一部分,允许在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程序员希望新的会话句柄对象将从creator函数返回,并使用异常来指示失败。通过稍微更改接口文件,我们可以让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不透明类型映射到“nice”Java类型,但不允许直接在Java中创建/删除该类型。(如果有一个sp_session_destroy函数,可以安排在Java对象被销毁时自动调用该函数,如果需要的话,可以使用javadestruct类型映射)。伪空定义与%nodefaultctor%nodefaultdtor相结合,可以实现这一点
  • 对于输入参数,我们需要在Java接口中隐藏它(使用numinput=0),然后在接口的生成C部分中提供它的位置
  • 要返回sp_会话而不是错误代码,我们需要调整从函数返回的类型映射-最简单的方法是将它们替换为如果函数使用$typemap声明为返回sp_会话时将使用的类型映射
  • 就输出而言,我们不希望在通常封送输出的地方执行任何操作,但我们确实希望返回在2中用作额外输入参数占位符的指针
  • 最后,我们希望将对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”。

  •  类似资料:
    • 问题内容: 我很难理解如何设置已作为指针传递的接口值。我正在努力实现以下目标: 我要做什么才能使程序输出为 编辑:是否有可能使用所有解决方案? 问题答案: 您可以使用来模拟AppEngine数据存储接口。通常,我说的是最小化反射,但是您(以及AppEngine和其他ORM)在这里没有其他很好的选择来展示您想要的接口。对于模仿您的内容: 得到与 获得您想要创建的事物的类型 用它创建 (可选)使用,填

    • 问题内容: 我有以下代码: 我一直期望这样做能够成功,因为MyType实现了MyInterface,但是我得到了: 不能将对象(类型 MyType)用作类型 MyInterface作为AcceptInterface的参数:* MyInterface是指向接口的指针,而不是接口 我尝试做类型断言:object。(MyInterface),但这也不起作用。 我该怎么做? 问题答案: 如错误所示, 不能

    • 问题内容: 在Java中,如果有接口: 然后实现是: 所以我的意思是,如果用户想用构造函数声明实例: 那不可能吗? 问题答案: 那不可能吗? 那就对了。你永远做不到 之后你必须选择一个 类 实现了接口(*) ,如: 为什么? 您可以将接口视为类的属性。比喻是形容词,例如“红色”。例如,创建一个红色的球()或一辆红色的汽车()是很有意义的,但是仅创建“红色”()没有任何意义(“红色代表 什么 ?”)

    • 问题内容: 我知道不可能在接口中定义构造函数。但是我想知道为什么,因为我认为这可能非常有用。 因此,您可以确定为该接口的每种实现定义了类中的某些字段。 例如,考虑以下消息类: 如果为该类定义一个接口,以便我可以有更多实现消息接口的类,则只能定义send方法,而不能定义构造函数。那么,如何确保此类的每个实现都确实有一个接收者集?如果我使用类似的方法,则不能确定是否真的调用了该方法。在构造函数中,我可

    • 问题内容: 关于接口和类,这让我感到困扰。 我正在尝试通过名为IPAddressString的类对名为IPAddress的接口进行实施。Ipadress包含四个部分。 我正在编写一个名为mask的方法,该方法用给定的地址屏蔽当前地址。掩码操作是对地址的所有四个部分进行按位“与”操作。您可以通过我编写的名为getOc​​tet的方法来获得所有四个部分。(您可以在下面看到)。 好的,所以我需要掩盖我的

    • 我从我正在使用的库中收到以下接口: 如何创建该类的实现?(我需要一个测试模拟)一个自然的实现,构造函数定义为: