2012-07-18 74 views
2

我用SWIG为Java中的Spotify library生成了一个包装。 头文件包含在生成的接口中暴露空指针

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

应该让我在Java中设置应用程序键:

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

C中的关键声明如下

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

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

我试图在.i文件添加:

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

,以便能够在Java中创建C数组具有以下功能:

new_uint8Array 
uint8Array_setitem 

没有成功。

我该如何解决我的问题?一个简单的例子会受到欢迎。

回答

1

你与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; 

所以问题是,我们不能称之为setApplication_key()与array_class/array_function产生uint8_t阵列,因为类型唐不匹配。

有很多可能的解决方案。一种方法可能是告诉SWIG忽略中的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并在接口中生成代码以使用包装的uint8_t*而不是void*

另一种可能的方法是简单地要求痛饮该成员一个uint8_t*,通过提供的接口文件中的struct的特殊定义:

%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更改时,您都必须确保更新界面文件以匹配该界面文件,否则您可能不会暴露,甚至不正确地暴露struct

一个忠告:小心这些示例中的所有权 - 在Java端阵列保留所有权,所以你需要确保:

  1. C库不会尝试免费它(如果有的话,你会看到一个双重的free()
  2. Java中数组的生命期超出了C库中的使用寿命。

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

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

+0

非常感谢! – fazerty 2012-07-25 19:59:28