2012-07-18 24 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