2010-04-29 5 views
7

Próbuję zawinąć stary kod do użycia w Javie i byłem bardzo szczęśliwy widząc, że Swig był w stanie obsłużyć plik nagłówkowy i wygenerował świetne opakowanie, które prawie działa. Teraz szukam głębokiej magii, która sprawi, że zadziała.Jak sprawić, by Swig poprawnie opakował bufor char *, który został zmodyfikowany w C jako Java Coś-lub-coś innego?

W C Mam funkcji, który wygląda tak

DLL_IMPORT int DustyVoodoo(char *buff, int len, char *curse); 

Ta całkowita zwracana przez tę funkcję jest kod błędu w przypadku, gdy nie powiedzie się. Argumenty są

  • buff jest bufor charakter
  • len jest długość danych w buforze
  • curse bufor inny znak, który zawiera wynik wywołania DustyVoodoo

Więc, możesz zobaczyć, gdzie to się dzieje, wynik wraca przez trzeci argument. Również len jest mylące, ponieważ może być długością obu buforów, są one zawsze przydzielane jako te same wielkości w kodzie wywołującym, ale biorąc pod uwagę to, co DustyVoodoo, nie uważam, że muszą być takie same. Aby być bezpiecznym, oba bufory powinny mieć taki sam rozmiar, na przykład 512 znaków.

Kod C generowane przez wiązanie się następująco:

SWIGEXPORT jint JNICALL Java_pemapiJNI_DustyVoodoo(JNIEnv *jenv, jclass jcls, jstring 

jarg1, jint jarg2, jstring jarg3) { 
    jint jresult = 0 ; 
    char *arg1 = (char *) 0 ; 
    int arg2 ; 
    char *arg3 = (char *) 0 ; 
    int result; 

    (void)jenv; 
    (void)jcls; 
    arg1 = 0; 
    if (jarg1) { 
    arg1 = (char *)(*jenv)->GetStringUTFChars(jenv, jarg1, 0); 
    if (!arg1) return 0; 
    } 
    arg2 = (int)jarg2; 
    arg3 = 0; 
    if (jarg3) { 
    arg3 = (char *)(*jenv)->GetStringUTFChars(jenv, jarg3, 0); 
    if (!arg3) return 0; 
    } 
    result = (int)PemnEncrypt(arg1,arg2,arg3); 
    jresult = (jint)result; 
    if (arg1) (*jenv)->ReleaseStringUTFChars(jenv, jarg1, (const char *)arg1); 
    if (arg3) (*jenv)->ReleaseStringUTFChars(jenv, jarg3, (const char *)arg3); 
    return jresult; 
} 

Prawdą jest za to, co robi; brakuje jednak faktu, że cursed to nie tylko dane wejściowe, jest ono zmieniane przez funkcję i powinno być zwracane jako dane wyjściowe. Nie wie również, że łańcuchy java są naprawdę buforami i powinny być wspierane przez tablicę o odpowiedniej wielkości.

Myślę, że Swig może zrobić to, co właściwe, nie mogę po prostu dowiedzieć się z dokumentacji, jak powiedzieć Swigowi, co powinien wiedzieć. Jakieś mezmy w domu?

+1

Po prostu na marginesie, sam googlę i był tak szczęśliwy, że zobaczyłem artykuł przepełnienia stosu, który brzmiał dokładnie tak, jakbym potrzebował. To było moje własne, ale bez odpowiedzi pytanie. 30 minut do początku strony, nieźle. – Ukko

+1

30 minut? Zwykle jest to szybsze;) – Thomas

Odpowiedz

7

Dziękuję Thomasowi za szturchanie we właściwym kierunku. Rozwiązaniem tego problemu było utworzenie niestandardowej mapy typów, która używa StringBuffer, aby uzyskać wynik z powrotem. Znalazłem kod w katalogu examples/java/typemap instalacji SWIG. Musiałem przeoczyć to wcześniej, kiedy szukałem.

Dołączyłem przykładowy kod poniżej, obecnie używam sugerowanego alternatywnego podejścia. Jednak pierwsze podejście do korzystania z BYTE typemap będzie wymagać pewnych zmian w moim kodzie Java, ale może na dłuższą metę mieć więcej sensu.

Dzięki za pomoc, a teraz mogę zobaczyć, czy mogę przyjąć własną odpowiedź ...

/* File : example.i */ 
%module example 
%{ 
/* 
    example of a function that returns a value in the char * argument 
    normally used like: 

    char buf[bigenough]; 
    f1(buf); 
*/ 

void f1(char *s) { 
    if(s != NULL) { 
    sprintf(s, "hello world"); 
    } 
} 

void f2(char *s) { 
    f1(s); 
} 

void f3(char *s) { 
    f1(s); 
} 

%} 

/* default behaviour is that of input arg, Java cannot return a value in a 
* string argument, so any changes made by f1(char*) will not be seen in the Java 
* string passed to the f1 function. 
*/ 
void f1(char *s); 

%include various.i 

/* use the BYTE argout typemap to get around this. Changes in the string by 
* f2 can be seen in Java. */ 
void f2(char *BYTE); 



/* Alternative approach uses a StringBuffer typemap for argout */ 

/* Define the types to use in the generated JNI C code and Java code */ 
%typemap(jni) char *SBUF "jobject" 
%typemap(jtype) char *SBUF "StringBuffer" 
%typemap(jstype) char *SBUF "StringBuffer" 

/* How to convert Java(JNI) type to requested C type */ 
%typemap(in) char *SBUF { 

    $1 = NULL; 
    if($input != NULL) { 
    /* Get the String from the StringBuffer */ 
    jmethodID setLengthID; 
    jclass sbufClass = (*jenv)->GetObjectClass(jenv, $input); 
    jmethodID toStringID = (*jenv)->GetMethodID(jenv, sbufClass, "toString", "()Ljava/lang/String;"); 
    jstring js = (jstring) (*jenv)->CallObjectMethod(jenv, $input, toStringID); 

    /* Convert the String to a C string */ 
    const char *pCharStr = (*jenv)->GetStringUTFChars(jenv, js, 0); 

    /* Take a copy of the C string as the typemap is for a non const C string */ 
    jmethodID capacityID = (*jenv)->GetMethodID(jenv, sbufClass, "capacity", "()I"); 
    jint capacity = (*jenv)->CallIntMethod(jenv, $input, capacityID); 
    $1 = (char *) malloc(capacity+1); 
    strcpy($1, pCharStr); 

    /* Release the UTF string we obtained with GetStringUTFChars */ 
    (*jenv)->ReleaseStringUTFChars(jenv, js, pCharStr); 

    /* Zero the original StringBuffer, so we can replace it with the result */ 
    setLengthID = (*jenv)->GetMethodID(jenv, sbufClass, "setLength", "(I)V"); 
    (*jenv)->CallVoidMethod(jenv, $input, setLengthID, (jint) 0); 
    } 
} 

/* How to convert the C type to the Java(JNI) type */ 
%typemap(argout) char *SBUF { 

    if($1 != NULL) { 
    /* Append the result to the empty StringBuffer */ 
    jstring newString = (*jenv)->NewStringUTF(jenv, $1); 
    jclass sbufClass = (*jenv)->GetObjectClass(jenv, $input); 
    jmethodID appendStringID = (*jenv)->GetMethodID(jenv, sbufClass, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); 
    (*jenv)->CallObjectMethod(jenv, $input, appendStringID, newString); 

    /* Clean up the string object, no longer needed */ 
    free($1); 
    $1 = NULL; 
    } 
} 
/* Prevent the default freearg typemap from being used */ 
%typemap(freearg) char *SBUF "" 

/* Convert the jstype to jtype typemap type */ 
%typemap(javain) char *SBUF "$javainput" 

/* apply the new typemap to our function */ 
void f3(char *SBUF); 
+0

Nie pozwolę sobie przyjąć mojej własnej odpowiedzi do jutra, no cóż. Teraz wiemy. – Ukko

6

Może this part dokumentacji SWIG jest pomocne:

Częstym problemem w niektórych programach C jest przekazywane jako parametry obsługi prostych wskaźników lub referencji. Na przykład:

void add(int x, int y, int *result) { 
    *result = x + y; 
} 

[...]

Plik biblioteki typemaps.i pomoże w takich sytuacjach. Na przykład:

%module example 
%include "typemaps.i" 

void add(int, int, int *OUTPUT); 

Jest też section on wrapping arrays.

Przykro mi, że nie jest to gotowa, kompletna odpowiedź. SWIG czasami pochyla się nad umysłem.

+0

SWIG jest gięciem umysłu, dam ci znać, jak to się potoczy później w ten weekend. Dzięki. – Ukko

0

Kolejny prosty i głupi obejście jeśli jesteś jak leniwy jak ja, a jeśli masz wolno nieco zmienić prototyp C/C++. Zmień typ swojego parametru (w pliku interfejsu) na niepodpisany znak * zamiast znaku *, a następnie SWIG zamień go na SWIGTYPE_p_unsigned_char :-) zamiast łańcucha znaków w Javie

+0

Niestety SWIGTYPE_p_unsigned_char daje po prostu znak, gdy się dereferencji, więc nie jest dobrze - przepraszam za wprowadzające w błąd. Przy okazji zastanawiam się - jeśli w powyższym przykładzie, jeśli muszę użyć ByteBuffer zamiast StringBuffer, to jakie są zmiany w typemap? Czy ktoś może to komentować? –

Powiązane problemy