2011-09-24 13 views
28

Sembra che la maggior parte delle librerie di documentazione o di supporto relative a JNI (Java Native Interface) riguardino la chiamata di codice nativo da Java. Questo sembra essere l'uso principale di questo, anche se è capace di più.Cercare un modo conveniente per chiamare Java da C++

Voglio principalmente lavorare nella direzione opposta: modificare un programma C++ portatile (abbastanza grande) esistente aggiungendo alcune librerie Java ad esso. Ad esempio, voglio farlo chiamare database via JDBC, o sistemi di code di messaggi tramite JMS, o inviare e-mail, o chiamare le mie classi Java, ecc. Ma con JNI raw questo è piuttosto spiacevole e soggetto a errori.

Quindi preferirei scrivere codice C++ che possa chiamare le classi Java con la stessa facilità con cui C++/CLI può chiamare le classi CLR. Qualcosa di simile:

using namespace java::util::regex; // namespaces mapped 

Pattern p = Pattern.compile("[,\\s]+"); 

array<java::lang::String> result = 
    p.split("one,two, three four , five"); 

for (int i=0; i < result.length(); i++) 
    std::cout << result[i] << std::endl; 

In questo modo, non avrebbe dovuto manually do the work of getting the method ID by passing the name and the weird signature strings, e sarebbe al riparo da errori di programmazione causati dai API incontrollati per chiamare i metodi. In effetti assomiglierebbe molto all'equivalente Java.

NB. SONO ANCORA PARLIAMO DI USARE JNI! Come tecnologia di base è perfetta per le mie esigenze. È "in corso" ed estremamente efficiente. Non voglio eseguire Java in un processo separato e fare chiamate RPC ad esso. JNI stessa va bene. Voglio solo una piacevole interfaccia ad esso.

Dovrebbe esserci uno strumento di generazione del codice per rendere equivalenti classi C++, spazi dei nomi, metodi, ecc. Per far corrispondere esattamente ciò che è esposto da un insieme di classi Java che specifichi. Le classi C++ generate sarebbero:

  • Hanno funzioni membro che accettano versioni simili dei loro parametri e quindi eseguono il necessario voodoo JNI per effettuare la chiamata.
  • Avvolgere i valori di ritorno nello stesso modo in modo da poter concatenare le chiamate in modo naturale.
  • Mantenere una cache statica per classe di ID metodo per evitare di cercarli ogni volta.
  • Essere totalmente thread-safe, portatile, open source.
  • Controlla automaticamente le eccezioni dopo ogni chiamata al metodo e genera un'eccezione C++ std.
  • Funziona anche quando sto scrivendo i metodi nativi nel solito modo JNI, ma ho bisogno di chiamare su altro codice Java.
  • L'array deve funzionare in modo totalmente coerente tra tipi e classi primitivi.
  • Avrà sicuramente bisogno di qualcosa di simile al globale per racchiudere i riferimenti quando devono sopravvivere al di fuori di una cornice di riferimento locale - di nuovo, dovrebbe funzionare allo stesso modo per tutti i riferimenti di matrice/oggetto.

Esiste o una libreria portatile e open source, o sto sognando?

Nota: ho trovato this existing question ma l'OP in quel caso non era quasi come esigente di perfezione come mi viene ...

Aggiornamento: un commento su SWIG mi ha portato a this previous question, che sembra indicare che è principalmente nella direzione opposta e quindi non farebbe quello che voglio.

IMPORTANTE

  • Qui si tratta di essere in grado di scrivere codice C++ che manipola le classi e gli oggetti Java, non il contrario (vedi il titolo!)
  • so già che JNI esistente (vedere la domanda!) Ma il codice scritto a mano per le API JNI è inutilmente prolisso, ripetitivo, soggetto a errori, non controllato in fase di compilazione, ecc. Se si desidera memorizzare gli ID metodo e gli oggetti classe è ancora più dettagliato. Voglio generare automaticamente classi wrapper C++ che si occupino di tutto ciò per me.

Aggiornamento: ho iniziato a lavorare sulla mia propria soluzione:

https://github.com/danielearwicker/cppjvm

Se questo esiste già, per favore fatemelo sapere!

NB. Se stai pensando di usarlo nel tuo progetto, sentiti libero, ma tieni a mente che in questo momento il codice ha poche ore di vita, e ho scritto solo tre test molto non complicati finora.

+1

C'è [SWIG] (http://www.swig.org/Doc1.3/Java.html) anche se l'ho usato solo per C e Python ... un * lungo * tempo fa. –

+0

Vedi aggiornamento: SWIG a quanto pare non lo fa. –

+0

in quale compilatore/interprete vuoi eseguire il tuo programma, C o Java? Se Java, il reverse non è possibile usando JNI. – Naved

risposta

16

Sì, esistono strumenti che fanno esattamente questo: generare wrapper C++ per classi Java. Ciò rende le API Java in C++ più trasparenti e divertenti, con costi e rischi inferiori.

Quello che ho usato di più è JunC++ion. È maturo, potente e stabile. L'autore principale è molto carino e molto reattivo. Sfortunatamente, è un prodotto commerciale e costoso.

Jace è uno strumento open source gratuito con licenza BSD. Sono passati anni dall'ultima volta che ho suonato con Jace.Sembra che ci sia ancora dello sviluppo attivo. (Ricordo ancora il post USENET dell'autore originale, oltre un decennio fa, chiedendo in sostanza la stessa domanda che stai chiedendo.)

Se è necessario supportare i callback da Java a C++, è utile definire classi C++ che implementare interfacce Java. Almeno JunC++ ion consente di passare tali classi C++ ai metodi Java che accettano i callback. L'ultima volta che ho provato Jace, non supportava questo, ma era sette anni fa.

+0

Grazie. Vedo dalla fonte di Jace che trasforma ogni singolo riferimento in un globale! Questa non è una grande idea. La JNI fa deliberatamente un'opzione: i globals sono più pesanti dei locali e la stragrande maggioranza delle variabili in un programma reale sono i locali. Non c'è bisogno che i locali vengano "rilasciati" in un distruttore, come la JVM si prende cura di esso. Hai solo bisogno di una classe RAII per incapsulare 'PushLocalFrame' /' PopLocalFrame'. Quale approccio prende lo ione JunC++ per questo? –

+2

BTW con la mia ricerca di Google per i termini 'C++ Java JNI Wrapper', vedo ora che JunC++ ion è a pagina 22 dei risultati! Non ho ancora notato Jace ... –

+0

JunC++ ion sarebbe a pagina 1, se la ricerca fosse basata sulla qualità. Non ho accesso a JunC++ ion adesso. Direi che i riferimenti globali sono necessari in alcuni casi e sufficienti per gli altri, ma è solo un'ipotesi. Vedrò se riesco a convincere l'autore a rispondere. –

1

L'articolo Java Tip 17: Integrating Java with C++ descrive come farlo in dettaglio.

+2

Immagino, ma quell'articolo è del 1996 (si dice che non c'è modo di contenere riferimenti globali! Questo è stato aggiunto in 1.2, molto tempo fa). Anche l'articolo descrive come scrivere a mano il codice di invocazione JNI, che è la cosa che voglio evitare di fare con uno strumento di generazione del codice. –

+0

+1, roba buona. – Naved

2

Re chiamando Java da C++.

È possibile eseguire ciò che si desidera ma è necessario consentire a Java di avere il controllo. Ciò che intendo è che si creano thread Java che chiamano in codice Nativo e da lì si bloccano, tipo di attesa del codice nativo per dargli qualcosa da fare. Si creano tutti i thread Java necessari per ottenere abbastanza lavoro/throuhput.

Così l'applicazione C++ si avvia, crea una JVM/JavaVM (come per il modo documentato, esempio esiste in qtjambi codebase vedi sotto), questo a sua volta esegue la normale inizializzazione JNI e System.loadLibrary() e fornisce JAR con il collegamento "nativo". Quindi inizializzi un gruppo di thread e chiami un codice JNI (che hai creato) dove possono bloccare il codice C++ per dare loro del lavoro da fare.

Il codice C++ (presumibilmente da un altro thread) quindi imposta e passa tutte le informazioni necessarie a uno degli operatori thread Java in attesa e bloccati, viene quindi dato l'ordine di esecuzione, quindi può tornare in Codice Java per fare lavoro e restituire con un risultato.

...

E 'possibile impostare e creare e contengono un'istanza JavaVM dal codice C++. Questo può essere alimentato forzatamente dai propri CLASSPATH/JAR per configurare l'ambiente contenuto che è necessario racchiudere nel programma C++.

Schema di che, come sono sicuro che hanno trovato già a http://download.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html

...

C'è una sorta di C++ => generatore Java JNI nel progetto QtJambi (che io lavoro su e aiutare a mantenere). Questo è abbastanza su misura per il toolkit Qt, ma essenzialmente traduce una serie di file di intestazione C++ in una raccolta di C++ .cpp/ .h file e * .java file per fornire il collegamento e il contenimento della shell dell'oggetto in modo che la memoria concorrente gli schemi di assegnazione giocano bene insieme. Forse c'è qualcosa da prendere da questo.

Questa è certamente una prova in cencept per ciò che si sta chiedendo il generatore è semplicemente contenuta nel progetto qtjambi (ma potrebbe essere reso indipendente con un po 'di lavoro) e questo è licenza LGPL (open-source). Il toolkit Qt non è una piccola API, ma può generare centinaia di classi per coprire un'alta percentuale di API (> 85% e quasi il 100% delle parti Core/GUI).

HTH

+0

La parte della tua risposta sui thread non ha senso per me. Posso già fare qualcosa di molto più semplice di quello usando puro JNI. Ho appena iniziato la JVM, chiedo di caricarmi una classe, chiamare il costruttore su di essa, chiamare i suoi metodi. Questo è tutto ciò che c'è da fare. –

+0

La libreria QtJambi è un progetto che consente al codice Java di utilizzare la libreria Qt, ad esempio Java che chiama C++. Legge le classi Java esistenti e produce classi wrapper C++ corrispondenti in modo che le classi Java possano essere facilmente richiamate da C++? –

+1

Il problema del thread riguardava come impostare un processo in modo che C++ potesse in qualsiasi momento chiamare in Java in modo sicuro. Parti della libreria Qt sono complesse, è una libreria C++ che deve anche richiamare in Java. È possibile registrare callback/eventHandlers/thread eseguiti in C++ ma occasionalmente richiamare cose in Java. Java ha API in pure-Java e anche C/C++. Ma C/C++ non ha API per fare cose in Java. Non ha senso creare un wrapper C++ di Java quando Java ha già un'API C++ (cioè JNI). –

0

Forse un po 'di un troppo grosso martello per questo chiodo, ma non è che ciò che è stato costruito per CORBA?

+3

Da questo link: "La mappatura del C++ è notoriamente difficile, la mappatura richiede al programmatore di imparare tipi di dati complessi e confusi che precedono la libreria di modelli standard C++" Sembra fantastico! :) –

1

Ho anche avuto molte difficoltà ottenere JNI per lavorare su diversi sistemi operativi , gestire le architetture 32/64-bit e assicurarsi che le librerie condivise corrette sono state trovate e caricate. Ho trovato difficile usare CORBA (MICO e JacORB).

ho trovato alcun modo efficace per chiamare da C/C++ in Java e le mie soluzioni preferite in questa situazione sono di eseguire il mio codice Java sia come:

  1. un programma stand-alone che posso facilmente eseguito da programmi C/C++ con java -cp myjar.jar org.foo.MyClass. Immagino che sia troppo semplicistico per la tua situazione.

  2. come un mini-server accetta le richieste da C/C++ programmi su un socket TCP/IP e risultati che ritornano attraverso questa presa troppo. Ciò richiede la scrittura di funzioni di rete e serializzazione ma disaccoppia i processi C/C++ e Java ed è possibile identificare chiaramente eventuali problemi come nel lato C++ o lato Java.

  3. Come servlet in Tomcat ed eseguire richieste HTTP dal mio programma C/C++ (anche gli altri contenitori servlet funzionerebbero). Ciò richiede anche la scrittura di funzioni di rete e serializzazione. Questo è più simile a SOA.

+0

Grazie. Ho Jni che lavora su Windows e Linux finora senza problemi - sembra fondamentalmente identico. CORBA è lontano da ciò di cui ho bisogno. Il marshalling di tutte le chiamate out-of-process sembra una versione scritta a mano delle cose che si ottengono dall'uso di SOA (o CORBA), e tutte quelle che hanno un grande sovraccarico e una maggiore complessità. –

+0

Nel frattempo, JNI sarebbe la soluzione leggera perfetta, se solo l'incantesimo per ogni chiamata di metodo (passare il nome del metodo e la firma, l'ID memorizzato nella cache, determinare l'aroma corretto di 'Call [Static] MethodT', verificare la presenza di eccezioni) potrebbe essere automatizzato in modo pulito. È tutto ció che voglio. –

+0

Un altro prodotto commerciale: http://www.javain.com/javain/oojni.jsp?cat=oojni&sub=whatIs –

1

Cosa succede ad usare Thrift o Protocol Buffers per facilitare il vostro Java per le chiamate C++?

+0

Devo aggiornare la mia domanda per escludere l'utilizzo di RPC sui server fuori processo. –

0

Rispondendo alla mia domanda:

http://java4cpp.kapott.org/

non sembra essere un progetto attivo. L'autore raccomanda di non utilizzarlo con JDK 1.5 o successivo.

Sembra avere un problema serio: esso passa puntatori intorno nuda ai suoi oggetti involucro:

java::lang::Integer* i = new java::lang::Integer("10"); 

delete i; // don't forget to do this! 

Essa provoca anche un problema più sottile che, al fine di rappresentare compatibilità assegnazione (ad esempio una classe come un sottotipo dell'interfaccia che implementa) i wrapper devono ereditare l'uno dall'altro.

7

Sono uno degli architetti prinicpal per i prodotti di integrazione linguistica di Codemesh, incluso lo ione JunC++. Facciamo questo tipo di integrazione dal 1999 e funziona davvero bene. Il problema più grande non è la parte JNI. JNI è noioso e difficile da eseguire il debug, ma una volta che hai capito bene, continua a funzionare. Ogni tanto, vieni danneggiato da una JVM o da un aggiornamento del sistema operativo, e quindi devi perfezionare il tuo prodotto, ma in generale è stabile.

Il problema più grande è la mappatura del sistema di tipi e il trade-off tra usabilità generale e soluzione mirata. Ad esempio, affermi che non ti piace il fatto che JACE tratti tutti i riferimenti agli oggetti come globali. Facciamo la stessa cosa (con alcuni portelli di fuga) perché si scopre che questo è il comportamento che funziona meglio per il 95% dei clienti, anche se fa male le prestazioni. Se hai intenzione di pubblicare un'API o un prodotto, devi scegliere i valori predefiniti che fanno funzionare le cose per la maggior parte delle persone. Scegliere i riferimenti locali come opzione predefinita sarebbe sbagliato perché sempre più persone scrivono applicazioni multithread e molte API Java che le persone vogliono utilizzare da altre lingue sono intrinsecamente multithread con callback asincroni e simili.

Abbiamo anche scoperto che vuoi davvero dare alle persone un generatore di codice basato su GUI per creare le specifiche di integrazione. Una volta specificata, si utilizza la versione CLI per integrarla nella build notturna.

Buona fortuna per il tuo progetto. È molto lavoro da fare bene. Ci abbiamo dedicato diversi anni e lo stiamo ancora migliorando regolarmente.

+0

Grazie.Ri: la scelta sensata dei valori predefiniti, sono assolutamente d'accordo: è la cosa più importante del design IMO. Per il mio progetto attuale, avere la scelta di usare la gente del posto sarà importante, e i dati condivisi saranno molto di minoranza, quindi vado per 'java :: lang :: String' e' global 'rispettivamente come sintassi per locali e globali. –

3

Avevo praticamente gli stessi problemi, ho finito per farlo da solo, forse questo aiuta qualcuno.

https://github.com/mo22/jnipp

Ha una piccola orma runtime (< 30KB), gestisce i riferimenti, e supporta la generazione di interfacce di classi Java. I.e. LocalRef> stringArray; e quindi usando stringArray [1] -> getBytes() o qualcosa del genere.