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:
- 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.
- 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.
- 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
.
- 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.
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.
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 –
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
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 –