2010-09-20 21 views
9

Sto usando SWIG di accedere al codice C++ da Java.SWIG: come avvolgere std :: string & (std :: string passato per riferimento)

Qual è il modo più semplice per esporre un parametro di std :: string passati per riferimento non-const?

I primitivi sono passati per riferimento esposti come array Java, grazie a typemaps.i e const std::string& s esposti come java.lang.String, grazie a std_string.i. Ma una stringa non-const std :: & viene esposta come tipo di puntatore opaco SWIGTYPE_p_std__string.

attuale:

// C++ method      -> // Java wrapper of C++ method 
void foo(int & i)    -> public void foo(int[] i); // OK 
void bar(const std::string & s) -> public void bar(String s); // OK 
void baz(std::string & s)  -> public void baz(SWIGTYPE_p_std__string s); // :(

desiderata:

void foo(int & i)    -> public void foo(int[] i); // OK 
void bar(const std::string & s) -> public void bar(String s); // OK 
void baz(std::string & s)  -> public void baz(String[] s); // OK 

UPDATE: ho trovato una soluzione, descritta di seguito. Tuttavia, ci sono voluti più sforzi di pochi secondi. Sono ancora interessato a conoscere approcci semplici.

+0

Perché la classe String di Java è immutabile. Quindi, anche se passi una matrice falsa di String, non puoi modificarla come stai implicando passando un riferimento. –

+0

@Martin York - Gli oggetti stringa sono immutabili. I membri di un array String, tuttavia, sono riferimenti mutabili agli oggetti String. –

+0

@Andy Thomas-Cramer: Ma non è come passare per riferimento. Stai passando un oggetto che puoi manipolare. Ma con una serie di stringhe l'oggetto non è ancora modificabile. A meno che tu non stia suggerendo che la funzione chiamata può cambiare la stringa sostituendola! Quale richiederebbe quindi il codice Glue per prendere l'oggetto stringa Java restituito e inserirlo nell'oggetto stringa C++ passato. –

risposta

7

L'approccio migliore che ho trovato è stato quello di scrivere il mio typemap. Speravo in alcune semplici istruzioni SWIG.

Nel caso qualcuno altro ha bisogno questo, ecco come ho fatto. Tieni presente che non sono un esperto SWIG.

In primo luogo, è necessario definire alcuni typemaps da applicare a std :: string & argomenti. Devi solo definirli una volta. (Nota:. Ci sono typemaps aggiuntivi che possono essere richiesti in alcune configurazioni)

modello argomento
%typemap(jni) std::string *INOUT, std::string &INOUT %{jobjectArray%} 
%typemap(jtype) std::string *INOUT, std::string &INOUT "java.lang.String[]" 
%typemap(jstype) std::string *INOUT, std::string &INOUT "java.lang.String[]" 
%typemap(javain) std::string *INOUT, std::string &INOUT "$javainput" 

%typemap(in) std::string *INOUT (std::string strTemp), std::string &INOUT (std::string strTemp) { 
    if (!$input) { 
    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); 
    return $null; 
    } 
    if (JCALL1(GetArrayLength, jenv, $input) == 0) { 
    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); 
    return $null; 
    } 

    jobject oInput = JCALL2(GetObjectArrayElement, jenv, $input, 0); 
    if (NULL != oInput) { 
    jstring sInput = static_cast<jstring>(oInput); 

    const char * $1_pstr = (const char *)jenv->GetStringUTFChars(sInput, 0); 
    if (!$1_pstr) return $null; 
    strTemp.assign($1_pstr); 
    jenv->ReleaseStringUTFChars(sInput, $1_pstr); 
    } 

    $1 = &strTemp; 
} 

%typemap(freearg) std::string *INOUT, std::string &INOUT "" 

%typemap(argout) std::string *INOUT, std::string &INOUT 
{ 
    jstring jStrTemp = jenv->NewStringUTF(strTemp$argnum.c_str()); 
    JCALL3(SetObjectArrayElement, jenv, $input, 0, jStrTemp); 
} 

successivo, per ogni C++ come questo ...

void foo(std::string & xyzzy); 
void bar(std::string & xyzzy); 
void baz(..., std::string & xyzzy, ...); 

... si applica la typemaps sopra con questa direttiva SWIG:

%apply std::string &INOUT { std::string & xyzzy }; 

Gli attacchi risultanti simile a questa:

public void foo(java.lang.String[] xyzzy); 
public void bar(java.lang.String[] xyzzy); 
public void baz(..., java.lang.String[] xyzzy, ...); 

essi necessitano di un elemento dell'array String. All'ingresso, il primo elemento potrebbe essere nullo. Se non null, viene convertito in un valore di stringa std :: 8 UTF e passato alla funzione C++. All'uscita, il valore della stringa std :: passata per riferimento viene convertito da UTF-8 in una stringa Java.

+0

Tornato da morto - penso di porre la stessa domanda qui: http://stackoverflow.com/questions/4652638/how-to-pass-strings-to-c-function-from-java-using-swig- interfaccia generata: puoi indicarmi qualche documentazione iniziale? Dove definisci i typemaps? Come fai a utilizzarlo da SWIG? Ho provato a inserirlo in typemaps.i e% lo includo nel mio file del modulo, e le mie uscite erano esattamente uguali – Derek

+0

Penso che dovresti cambiare '' INPUT'' in '' OUTPUT'' perché '' C++ '' code is restituendo il '' stringa'' come output. controlla questo post: http://stackoverflow.com/a/11967859/365229 –

+0

@raypixar - È possibile che tu stia interpretando erroneamente "INOUT" come "INPUT"? I parametri INOUT possono essere utilizzati sia come input sia come output. In generale, una funzione come 'void baz (std :: string & s)' potrebbe usare il parametro per input e/o output. Per ulteriori dettagli su INOUT, consultare http://www.swig.org/Doc2.0/SWIGDocumentation.html#Arguments_nn6 –