2012-10-05 13 views
6

Tôi cố gắng để sử dụng SWIG để sử dụng API Spotify (libspotify) dành cho Android: https://developer.spotify.com/technologies/libspotify/giao diện SWIG để nhận được một tài liệu tham khảo struct đục trong Java thông qua tham số của hàm

tôi đang gặp khó khăn xác định các tập tin giao diện SWIG để được thể gọi thành công chức năng C có nguồn gốc như sau:

sp_error sp_session_create(const sp_session_config * config, sp_session ** sess); 

nào trong C sẽ được gọi là như thế này:

//config struct defined previously 
sp_session *sess; 
sp_session_create(&config, &sess); 

Nhưng trong Java tôi sẽ cần phải gọi nó là như thế này:

//config object defined previously 
sp_session javaSess = new sp_session(); 
sp_session_create(config, javaSess); 

sp_session là một đục struct và chỉ được định nghĩa trong file API.h libspotify như:

typedef struct sp_session sp_session; 

tôi hy vọng thư viện libspotify để tạo ra nó và cho tôi một tham chiếu đến nó. Điều duy nhất tôi cần tham chiếu đó là chuyển sang các chức năng khác trong API.

Tôi tin câu trả lời nằm trong giao diện SWIG và các bản đồ, nhưng tôi đã không thành công khi cố gắng áp dụng examples I found trong tài liệu.

Trả lời

5

Ở mức cơ bản nhất, bạn có thể tạo mã hoạt động bằng cách sử dụng phần cpointer.i của thư viện SWIG để cho phép tạo đối tượng "con trỏ trỏ tới" trực tiếp trong Java.

Ví dụ cho các tập tin tiêu đề:

#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) {} 

Bạn có thể bọc nó với:

%module spotify 

%{ 
#include "test.h" 
%} 

%include "test.h" 

%include <cpointer.i> 

%pointer_functions(sp_session *, SessionHandle) 

Mà sau đó cho phép chúng tôi viết một cái gì đó như:

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)); 
    } 
} 

trong Java. Chúng tôi sử dụng SessionHandle_value() để tạo ra con trỏ kép và new_SessionHandle() để tạo đối tượng con trỏ kép cho chúng ta. (Có các chức năng khác để làm việc với đối tượng con trỏ kép).


Các công trình trên và rất đơn giản để bọc, nhưng hầu như không "trực quan" đối với một lập trình viên Java và lý tưởng là chúng tôi sẽ trưng ra toàn bộ thư viện trong một cái gì đó trông giống Java hơn.

Một lập trình viên Java dự kiến ​​rằng đối tượng xử lý phiên mới sẽ được trả về từ hàm tạo và rằng một ngoại lệ sẽ được sử dụng để chỉ ra các lỗi. Chúng tôi có thể làm cho SWIG tạo mà giao diện với một vài typemaps và một số sử dụng cẩn thận của %exception, bằng cách thay đổi các tập tin giao diện hơi:

%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" 

Các ý kiến ​​đánh số tương ứng với những điểm sau:

  1. Chúng tôi muốn sp_session loại đục để ánh xạ tới loại Java "đẹp" nhưng không cho phép tạo/xóa loại trực tiếp trong Java.(Nếu có một hàm sp_session_destroy để có thể sắp xếp để có được tự động gọi khi đối tượng Java bị phá hủy nếu đó là mong muốn bằng cách sử dụng kiểu chữ javadestruct). fake, empty definition combined with %nodefaultctor and %nodefaultdtor sắp xếp cho việc này.
  2. Đối với thông số đầu vào mà chúng tôi đang thực hiện trở lại thay vào đó chúng ta cần phải ẩn nó từ giao diện Java (sử dụng numinputs=0) và sau đó cung cấp một cái gì đó để diễn ra của nó trong phần C tạo ra của giao diện.
  3. Để trả về sp_session thay vì mã lỗi, chúng ta cần phải điều chỉnh các kiểu chữ để trả về từ hàm - cách đơn giản nhất để thay thế chúng là các bản đồ đã được sử dụng nếu hàm được khai báo là trả về a sp_session sử dụng $typemap.
  4. Theo như đầu ra có liên quan, chúng tôi không muốn làm bất cứ điều gì mà nó thường được sắp xếp, nhưng chúng tôi muốn trả lại con trỏ mà chúng tôi đã sử dụng làm trình giữ chỗ cho tham số nhập bổ sung trong 2.
  5. Cuối cùng, chúng tôi muốn gửi toàn bộ cuộc gọi đến sp_session_create trong một số mã sẽ kiểm tra giá trị trả lại thực và ánh xạ tới một ngoại lệ Java nếu nó cho biết lỗi. Tôi đã viết những lớp ngoại lệ sau bằng tay cho đó là tốt:

    public class SpotifyException extends Exception { 
        public SpotifyException(String reason) { 
        super(reason); 
        } 
    } 
    

Sau khi thực hiện tất cả các công việc này ngay bây giờ chúng ta đang ở trong một vị trí để sử dụng trong mã Java như sau:

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);  
    } 
} 

Đó là đơn giản hơn và trực quan hơn so với bản gốc nhưng đơn giản hơn để viết giao diện. Độ nghiêng của tôi sẽ sử dụng advanced renaming feature để làm cho các tên kiểu "cũng giống Java hơn".

+0

Bạn có lỗi trong tệp giao diện, 'pointer_functions (sp_session *, SessionHandle)'. Xóa '*'. Tôi nhận được lỗi biên dịch nếu có. Tuy nhiên, khi gỡ bỏ nó, tôi không còn tạo ra một con trỏ nữa. Làm thế nào để giải quyết điều này –

+0

nếu '% pointer_functions (sp_session *, SessionHandle)' không hoạt động thì nhiều khả năng có vấn đề với 'sp_session' hoặc cái gì đó khác trong giao diện của bạn. Tôi chỉ cần kiểm tra gấp đôi và bit đó chắc chắn hoạt động đúng với SWIG 2.0.11. – Flexo

+0

Cảm ơn. Bằng cách chỉnh sửa thủ công nguồn _wrap tôi có thể làm cho nó hoạt động. Tôi sử dụng SWIG 2.0.7.3. Tôi nghĩ rằng tôi sẽ cố gắng cài đặt một SWIG mới hơn. Không bao giờ thành công với con trỏ kép. Nó chèn một số const * & phôi –

0

Bạn nên thông báo cho swig về tuyên bố typedef của mình. Để làm điều đó, bạn nên chỉnh sửa tập tin giao diện của bạn với:

typedef struct sp_session sp_session; 

Nhưng hãy cẩn thận để thông báo cho SWIG về typedef của bạn trước khi nó thấy bất kỳ sp_session (Tôi có nghĩa là trước khi đưa API.h). Tôi đã có cùng một vấn đề với nhận dạng typedef. Có lẽ điều này link sẽ giúp ích.