java - 在生成的接口(interface)中公开空指针

标签 java java-native-interface swig

我使用 SWIG 为 Spotify library 生成了一个包装器在 java 。 头文件包含

typedef struct sp_session_config {
   ...
  const void *application_key;           ///< Your application key
  ...

这应该允许我在 Java 中设置应用程序 key :

sp_session_config cfg = new sp_session_config();
cfg.setApplication_key(appkey);

C中的key是这样声明的

const uint8_t g_appkey[] = {0x01, 0xB4, 0xF9, 0x33, .... }

问题是我不知道如何在 Java 中创建 key 。 appkey 的类型必须是 SWIGTYPE_p_void

我试图在 .i 文件中添加:

%include "carrays.i"
%array_functions(uint8_t, uint8Array);

能够使用这些函数在 Java 中创建 C 数组:

new_uint8Array
uint8Array_setitem

没有成功。

我该如何解决我的问题?欢迎提供一个简单的小例子。

最佳答案

你在 carrays.i 的正确路线上,虽然通常我更愿意使用 %array_class 而不是 %array_functions 如果有选择的话。问题是你的 array_functions/array_class 给你一个 SWIGTYPE_p_unsigned_char (即指向 unsigned char 的指针的代理),而你需要一个 void 指针并且有由于 Java 中的强类型,没有简单的方法可以在两者之间进行转换。不过,我们可以解决这个问题。

为了说明这个答案,我创建了一个非常简单的头文件来重现您的问题:

typedef struct sp_session_config {
  const void *application_key;           ///< Your application key
} session_config;

所以问题是我们无法使用 array_class/array_function 生成的 uint8_t 数组调用 setApplication_key(),因为类型不匹配。

对此有很多可能的解决方案。一种方法可能是告诉 SWIG 忽略 sp_session_config 中的 application_key,而是假装它是一个 uint8_t* application_key。为此,您需要在 C 中为完成工作的包装器提供一个 set/get 函数(在这种情况下微不足道)并使用 %ignore 隐藏“真实”成员,% extend 添加“假”成员和 %rename 停止 %ignore 忽略假成员:

%module test

%{
#include "test.h"
#include <stdint.h>

// Internal helpers for our fake memeber
static void session_config_application_key_set(session_config *cfg, const uint8_t *arr) {
  cfg->application_key = arr;
}

static const uint8_t *session_config_application_key_get(const session_config *cfg) {
  return cfg->application_key;
}
%}

%include <carrays.i>
%include <stdint.i>

%array_class(uint8_t, uint8Array);

%ignore sp_session_config::application_key; // ignore the real one when we see it
%include "test.h"
%rename("%s") sp_session_config::application_key; // unignore for extend
%extend session_config {
  uint8_t *application_key;
}

这足以让我们用 Java 编写:

public class run {
  public static void main(String[] argv) {
    session_config cfg = new session_config();
    uint8Array key = new uint8Array(4);
    key.setitem(0, (short)100); key.setitem(1, (short)101); 
    key.setitem(2, (short)102); key.setitem(3, (short)103);    
    cfg.setApplication_key(key.cast());
  }
}

或者,如果您愿意,可以使用类型映射系统将成员公开为 uint8_t*:

%module test

%{
#include "test.h"
%}

%include <carrays.i>
%include <stdint.i>

%array_class(uint8_t, uint8Array);

%typemap(jstype) const void *application_key "$typemap(jstype, const uint8_t*)"
%typemap(javain) const void *application_key "$typemap(jstype, const uint8_t*).getCPtr($javainput)"
%typemap(javaout) const void *application_key {
  long cPtr = $jnicall;
  return (cPtr == 0) ? null : new $typemap(jstype, const uint8_t*)(cPtr, $owner);
}
%include "test.h"

仅匹配 const void *application_key 并在接口(interface)中生成代码以使用包装的 uint8_t* 而不是 void*

另一种可能的方法是通过提供 struct 的特殊定义,简单地向 SWIG 声明成员 uint8_t*在接口(interface)文件中:

%module test

%{
#include "test.h"
%}

%include <carrays.i>
%include <stdint.i>

%array_class(uint8_t, uint8Array);

typedef struct sp_session_config {
  const uint8_t *application_key;
} session_config;

// Ignore the one in the header file, use our special version instead
%ignore sp_session_config;
%include "test.h"

这样做很简单,但维护起来比较棘手 - 每次 sp_session_config 更改时,您都必须确保更新接口(interface)文件以匹配它,否则您将面临不公开甚至不正确的风险公开结构

请注意:注意这些示例中的所有权 - Java 端的数组保留所有权,因此您需要确保:

  1. C 库不会尝试释放它(如果它这样做,您会看到一个双 free())
  2. Java 中数组的生命周期超过了它在 C 库中的使用。

如果你愿意,你可以让 C 库获得内存的所有权,这些例子中最容易做到这一点的是使用类型映射的例子。 (您可以修改 javain 类型映射以也获得所有权)。

最后,对于另一种可能的解决方案,您可以使用 a little bit of JNIyet more other techniques正如我过去回答的那样,使用 Java 数组或为此生成合适的代码。

关于java - 在生成的接口(interface)中公开空指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11535615/

相关文章:

java - Jackson Object Mapper 读取值(以字节为单位)返回一个所有字段都初始化为 null 的对象

python - 使用自定义视频编写器库编写音频的错误

iphone - Phonegap - C++ 库

java-native-interface - 哪个 native 函数导致 JNI 代码中的 EXCEPTION_ACCESS_VIOLATION?

java - 痛饮+Java : namespaces and packages

c# - 为包含 void* 参数的函数创建 SWIG C# 包装器

java - org.springframework.security.access.SecurityConfig : Unsatisfied dependency expressed through constructor

Java过滤器工厂实现

java - 查明 apk 是否以 Beta 版本发布或在运行时稳定运行

java - 我是否保证每个使用 JNI 的进程只有一个 JavaVM?