2012-10-05 9 views
6

Staram się używać SWIG w celu korzystania z interfejsu API Spotify (libspotify) dla Androida: https://developer.spotify.com/technologies/libspotify/interfejs SWIG otrzymywać nieprzezroczystą odniesienie struct w Javie przez argumentu funkcji

Mam problemy określającą plik interfejsu SWIG do być w stanie skutecznie wywołać następujące macierzystym C funkcję:

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

które w C można nazwać tak:

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

Ale w Java I musiałby nazwać tak:

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

sp_session jest nieprzejrzysta struktura i jest określony tylko w pliku API.h libspotify jako:

typedef struct sp_session sp_session; 

Czekam na libspotify biblioteki, aby go utworzyć i daj mi odniesienie do tego. Jedyne, czego potrzebuję tego odniesienia, to przekazanie do innych funkcji w API.

Wydaje mi się, że odpowiedź leży w interfejsie SWIG i typemaps, ale nie udało mi się zastosować examples I found w dokumentacji.

Odpowiedz

5

Na najbardziej podstawowym poziomie można utworzyć kod, który działa przy użyciu części cpointer.i biblioteki SWIG, aby umożliwić utworzenie bezpośredniego obiektu "wskaźnik do wskaźnika" w Javie.

Na przykład dany plik nagłówka:

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

Można owinąć go z:

%module spotify 

%{ 
#include "test.h" 
%} 

%include "test.h" 

%include <cpointer.i> 

%pointer_functions(sp_session *, SessionHandle) 

Które następnie pozwala nam napisać coś takiego:

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

w Javie. Używamy SessionHandle_value(), aby wykluczyć podwójny wskaźnik i new_SessionHandle(), aby utworzyć dla nas obiekt podwójnego wskaźnika. (Istnieją inne funkcje do pracy z obiektem o podwójnym wskaźniku).


Powyższe prace i jest bardzo prosty do zawijania, ale to ledwie „intuicyjny” dla programisty Java i idealnie chcielibyśmy narazić całą bibliotekę w coś, co wygląda bardziej jak Java.

Programista Java oczekiwałby, że nowy obiekt obsługi sesji zostanie zwrócony z funkcji twórcy, a do wskazania błędów zostanie użyty wyjątek. Możemy dokonać SWIG wygenerować ten interfejs z kilkoma typemaps i niektórych ostrożnego korzystania z %exception, zmieniając plik interfejsu nieco:

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

numerowane komentarze odpowiadają tych punktów:

  1. Chcemy sp_session nieprzezroczysty typ, aby odwzorować "miły" typ Java, ale nie pozwala na tworzenie/usuwanie tego typu bezpośrednio w Javie.(Jeśli istnieje funkcja sp_session_destroy, która mogłaby spowodować automatyczne wywołanie, gdy obiekt Java zostanie zniszczony, jeśli jest to pożądane przy użyciu javadestruct typemap). fake, empty definition combined with %nodefaultctor and %nodefaultdtor organizuje to.
  2. Dla parametru wejściowego, który robimy w zamian, musimy ukryć go przed interfejsem Java (używając numinputs=0), a następnie dostarczyć coś, co zajmie jego miejsce w wygenerowanej części C interfejsu.
  3. Aby zwrócić kod sp_session zamiast kodu błędu, musimy dostosować mapy typów dla zwrotu z funkcji - najprostszym sposobem jest zastąpienie ich mapami typów, które zostałyby zastosowane, gdyby funkcja została zadeklarowana jako zwracająca. a sp_session przy użyciu $typemap.
  4. Jeśli chodzi o wyjście jest zainteresowana nie chcemy robić niczego, gdzie byłoby to zwykle marshaled, ale chcemy, aby powrócić wskaźnik użyliśmy jako zastępczy dla dodatkowego parametru wejściowego w 2.
  5. Na koniec chcemy zawrzeć całe wywołanie sp_session_create w kodzie, który sprawdzi prawdziwą wartość zwracaną i odwzoruje ją na wyjątek Javy, jeśli wskaże awarię. Napisałem następujący klasę wyjątku przez strony, że dobrze:

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

Po wykonaniu wszystkich tych prac jesteśmy teraz w stanie wykorzystać, że w kodzie Java w następujący sposób:

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

Co jest znacznie prostsze i bardziej intuicyjne niż oryginalny, ale prostszy w pisaniu interfejs. Moja skłonność polegałaby na użyciu nazwy advanced renaming feature, aby nazwy typów również wyglądały bardziej "Java".

+0

Masz błąd w pliku interfejsu, 'pointer_functions (sp_session *, SessionHandle)'. Usuń '*'. Dostaję błędy kompilacji, jeśli są obecne. Jednak po jego usunięciu nie tworzę już wskaźnika. Jak rozwiązać ten –

+0

, jeśli '% pointer_functions (sp_session *, SessionHandle)' nie działa, bardziej prawdopodobny jest problem z 'sp_session' lub czymś innym w twoim interfejsie. Właśnie sprawdziłem dwukrotnie i ten bit zdecydowanie działa poprawnie z SWIG 2.0.11. – Flexo

+0

Dzięki. Ręczne edytowanie źródła _wrap może sprawić, że zadziała. Używam SWIG 2.0.7.3. Myślę, że spróbuję zainstalować nowszy SWIG. Nigdy nie udało się osiągnąć podwójnych wskaźników. Wstawia kilka const * i rzutów –

0

Powinieneś poinformować swig o swojej deklaracji typedef. W tym celu należy edytować plik z interfejsu:

typedef struct sp_session sp_session; 

Ale należy uważać, aby poinformować o swojej SWIG typedef przed widzi żadnego sp_session (mam na myśli przed tym API.h). Miałem ten sam problem z rozpoznawaniem typedef. Może to pomoże link.

Powiązane problemy