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