2012-10-05 7 views
6

Estoy tratando de utilizar el TRAGO con el fin de utilizar la API de Spotify (libspotify) para Android: https://developer.spotify.com/technologies/libspotify/interfaz SWIG para recibir una referencia de estructura opaca en Java a través argumento de la función

Tengo problemas para definir el archivo de interfaz SWIG a ser capaz de llamar con éxito la siguiente función nativa C:

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

lo que en C se llamaría así:

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

Pero en Java I tendría que llamarlo así:

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

sp_session es una estructura opaca y sólo se define en el archivo de API.h libspotify como:

typedef struct sp_session sp_session; 

Estoy esperando la biblioteca libspotify para crear y dame una referencia a eso. Lo único que necesito esa referencia para entonces es pasar a otras funciones en la API.

Creo que la respuesta se encuentra en la interfaz SWIG y en los mapas de tipos, pero no he tenido éxito al intentar aplicar el examples I found en la documentación.

Respuesta

5

En el nivel más básico, puede crear código que funcione utilizando la parte cpointer.i de la biblioteca SWIG para permitir que se cree un objeto directo "puntero a puntero" en Java.

Por ejemplo dado el archivo de cabecera:

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

se puede envolver con:

%module spotify 

%{ 
#include "test.h" 
%} 

%include "test.h" 

%include <cpointer.i> 

%pointer_functions(sp_session *, SessionHandle) 

Que a su vez nos permite escribir algo como:

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

en Java. Usamos SessionHandle_value() para derivar el puntero doble y new_SessionHandle() para crear un objeto de doble puntero para nosotros. (Hay otras funciones para trabajar con el objeto de doble puntero).


Los trabajos anteriores y es muy fácil de ajustar, pero es apenas "intuitiva" para un programador de Java e idealmente nos gustaría exponer a toda la biblioteca en algo que se parece más a Java.

Un programador de Java esperaría que el nuevo objeto de identificador de sesión fuera devuelto por la función de creador y que una excepción se usaría para indicar fallas. Podemos hacer TRAGO generar esa interfaz con algunas typemaps y algún uso cuidadoso de %exception, cambiando el archivo de interfaz un tanto:

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

Los comentarios numeradas corresponden con estos puntos:

  1. Queremos que el sp_session tipo opaco para asignar a un tipo de Java "agradable" pero no permitir la creación/eliminación del tipo directamente dentro de Java.(Si hay una función sp_session_destroy que podría organizar que se llame automáticamente cuando se destruya el objeto Java si eso es deseable utilizando el mapa de tipos javadestruct). El fake, empty definition combined with %nodefaultctor and %nodefaultdtor organiza esto.
  2. Para el parámetro de entrada que estamos haciendo en un retorno, en su lugar, tenemos que ocultarlo de la interfaz Java (usando numinputs=0) y luego suministrar algo para tomar su lugar en la parte C generada de la interfaz.
  3. Para devolver el sp_session en lugar del código de error, necesitamos ajustar los mapas de tipos para el retorno de la función - la forma más sencilla de hacerlo es sustituirlos por los mapas de tipos que se habrían utilizado si la función se hubiera declarado como retornada a sp_session usando $typemap.
  4. En lo que a la salida se refiere a que no queremos hacer nada, donde por lo general se calculan las referencias, pero queremos volver el puntero se utilizó como un marcador de posición para el parámetro de entrada adicional en 2.
  5. Finalmente, queremos encerrar toda la llamada al sp_session_create en algún código que verifique el valor de retorno real y lo asigne a una excepción de Java si indica un error. Escribí la siguiente clase de excepción con la mano para que así:

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

Habiendo hecho todo este trabajo que estamos ahora en condiciones de uso que en el código Java de la siguiente manera:

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

Que es mucho más simple e intuitivo que la interfaz original pero más simple de escribir. Mi inclinación sería usar el advanced renaming feature para hacer que los nombres de tipo "busquen más Java" también.

+0

Tiene un error en su archivo de interfaz, 'pointer_functions (sp_session *, SessionHandle)'. Eliminar el '*'. Obtengo errores de compilación si están presentes. Sin embargo, al eliminarlo, ya no creo un puntero. Cómo resolver esto –

+0

si '% pointer_functions (sp_session *, SessionHandle)' no funciona, entonces es más probable que haya un problema con 'sp_session' u otra cosa en su interfaz. Lo comprobé dos veces y ese bit definitivamente funciona correctamente con SWIG 2.0.11. – Flexo

+0

Gracias. Al editar manualmente la fuente _wrap puedo hacerlo funcionar. Yo uso SWIG 2.0.7.3. Creo que intentaré instalar un SWIG más nuevo. Nunca tuvo éxito con los dobles punteros. Inserta algunos const * y moldes –

0

Debe informar al trago acerca de su declaración typedef. Con el fin de hacer eso debería editar el archivo de interfaz con:

typedef struct sp_session sp_session; 

Pero tenga cuidado para informar acerca de su TRAGO typedef antes de ver cualquier sp_session (me refiero a antes de incluir API.h). Tuve el mismo problema con el reconocimiento typedef. Tal vez esto link ayudará.

Cuestiones relacionadas