2010-07-28 12 views
11

Tengo un proyecto escrito en C++ y estoy buscando escribir una interfaz gráfica de usuario de Java como una interfaz para él. La elección de Java es fija, así que tendría que aprender a cómo llamar al código C++ desde Java. Reescribir el código C++ no es una opción. Me gustaría obtener información sobre: ​​Uso de Java para envolver C++

  1. ¿Qué herramientas puedo usar para lograr este envoltorio?
  2. Cuánto del código de C++ tendría que modificar necesariamente, si corresponde.
  3. Cualquier otra información/preguntas de seguimiento que pueda tener.

Gracias.

Respuesta

3

Debe buscar JNI - Java Native Interface

+0

¿esto realmente es compatible con C++, o solo admite funciones, y no C++ - clases/class-methods, etc.? – smerlin

+0

Entonces, lo que estoy viendo en JNI es que los wrappers se escriben antes de que se introduzca el código C++ (o cualquier otro idioma). Ya tengo todo el código C++, entonces ¿qué es lo que no estoy acertando sobre JNI? – sparkFinder

+0

Creo que tienes que escribir algo en C++ para llamar a tu código, y el resto en Java para llamar a tu contenedor. Por lo tanto, tendrá su código original, el contenedor de C++ accediendo al código original, el JNI de Java y el acceso de Java al envoltorio de Java. – Victor

2

Dependiendo de qué tan estrechamente unida la interfaz tiene que ser al código C++, la cosa más fácil de hacer podría ser para ejecutar la interfaz gráfica de usuario y el código C++ como programas separados que se comunican a través de algún tipo de IPC (sockets, named pipes en Unix, etc.)

La otra alternativa que he escuchado, pero nunca he hecho, es crear un contenedor JNI (Java Native Interface) alrededor de su código C++ . Por lo que tengo cabeza, este no es un ejercicio directo.

0

Compile la GUI utilizando GCJ y utilice el CNI para ajustar el código de C++. Consulte http://gcc.gnu.org/java/faq.html#6_2 para obtener algunos ejemplos comparativos del uso de CNI frente a JNI (lo que usaría con todos los demás tiempos de ejecución Java).

Se supone que el CNI es mucho más simple que JNI.

+2

-1 gcj nunca es una buena respuesta. – KitsuneYMG

+0

por qué ? porque no se está desarrollando activamente? –

+0

@robert: No estoy seguro de cuál es la razón de los kts. Creo que él siente que no es estándar. GCJ ciertamente se está desarrollando activamente. –

5

JNI

  • Si el código es C++
  • JNI es parte del entorno de ejecución Java
  • declaran funciones nativas y generan c/C++ cabecera con javah
  • escritura C/C++ de código para pegar su código a los métodos de Java

JNA

  • Si tiene ac API para su código
  • no funciona con clases de C++
  • JNA no forma parte del tiempo de ejecución de Java, jna.jar es de 300 kb
  • declarar una interfaz Java con los métodos c usted utilizar
  • código java escritura para pegar el archivo DLL de java
  • necesitará crear una copia de las estructuras java c utilizado por las funciones DLL

Ambos son platf orm independiente. Yo prefiero jna porque es fácil confundir el código jni. Existen varias soluciones dependientes de la plataforma, también, pero no las conozco tan bien.

  • JACOB: java COM puente (ventanas)
  • ...
+0

+1, aunque JNA/JNI son ambas interfaces C (no C++). Cuando dices que JNA no funciona con clases de C++, olvidas que devuelves un puntero opaco. Significa que se escribe una función "externa" C "' para cada función miembro que toma dicho puntero opaco como argumento (estilo c-with-classes). Recomiendo usar JNA si ya tiene una API "externa" y JNI si no (JNI le hará escribir uno, pero solo para los métodos que marca como "nativo" en Java) – KitsuneYMG

1

Me gustaría algún tipo de IPC (tubos, tal vez incluso tomas de corriente). De esta forma, su código se reduce a copiar hacia y desde matrices de bytes en C++, y usando InputStreams y OutputStreams en Java.

Recientemente trabajé en un proyecto donde teníamos una biblioteca distribuida por un tercero, que se escribió en C++. Pero cada sistema que tenemos que podría usar esta biblioteca fue escrita en Java.

Fuimos por la ruta de envolver la biblioteca como un archivo ejecutable nativo, que lee la entrada de stdin, traduciéndola a llamadas de función a la biblioteca . En consecuencia, los resultados de la biblioteca se convirtieron y el se imprimió en stdout.

Esto también significaba que el contenedor era fácil de probar, ya que todo lo que tenía que hacer era invocarlo en la línea de comando. Descubrió un lote de errores y problemas debido a esto. Lo recomiendo ampliamente

El siguiente problema era '¿Cómo invoco el envoltorio de C++' y '¿Cómo lo incluyo en el paquete con la aplicación Java'? De hecho, evitamos las respuestas a estas preguntas , al exponer el ejecutable a través de inetd. Entonces nuestras aplicaciones java invocaron el código C abriendo un socket. Porque había codificado la envoltura para comunicarse a través de la entrada estándar y salida estándar, que no tenía que modificar que en absoluto para exponerlo a través de TCP (Lea sobre inetd si estás desconcertado). La programación más ingeniosa que he hecho ... :-)

Disculpa, me puse en tangente. Trataba de ilustrar la flexibilidad que puede obtener si decide separar el código C++ en un proceso separado . El punto es que ha hecho el trabajo de coordinar sus estructuras de datos por adelantado. Entonces, inicialmente, puede mantener su otro proceso local y comunicarse a través de un conducto. El problema es que si alguna vez decide que necesita enviar las solicitudes a un servidor TCP remoto, , no le costaría mucho esfuerzo cambiar el programa java. Ya ha realizado la mayor parte del trabajo. Puede valer la pena considerarlo. Si es realmente adecuado para usted, no lo sé.

no he estará con JNI, pero sí tenemos aplicaciones que lo utilizan en el trabajo, y todos ellos sufren problemas de gestión de memoria:

1) Si la C/C++ código comete un error con la aritmética de punteros , tu aplicación Java también está jodida. Probablemente muera con un SIGSEGV. Por lo tanto, su código C/C++ es mejor, sólido, probado y merece la pena. Con el proyecto en el que estaba trabajando, la mayoría de las aplicaciones Java eran procesos de servidor, que se supone que funcionan 24/7. No confiamos en este biblioteca que mucho, útil como puede ser.

2) Pasar estructuras de datos entre Java y C++ puede ser problemático. Especialmente si pasa objetos Java a las funciones C.Por lo general, tiene que realizar algún tipo de conversión, lo que plantea problemas tales como '¿Debería copiar esta estructura de datos? ¿Cuándo debería destruirlo? Es especialmente malo si inadvertidamente llamas 'gratis' en algún objeto que fue asignado en el programa Java.

3) He visto un código JNI. Simplemente se ve feo ... [barf]

Perdón por la larga publicación.

7

Recientemente trabajé en un proyecto en el que teníamos que hacer exactamente lo mismo. Teníamos un modelo de datos escrito en C++ y necesitábamos poner una GUI de Java en la parte superior. Lo que terminamos haciendo fue identificar las clases de C++ a las que necesitábamos acceder desde la GUI y utilizamos SWIG para generar clases antiguas simples de java que envolvían los objetos de C++.

http://www.swig.org/

las clases Java generadas por SWIG tienen interfaces idénticas a la C++ clases que son de envoltura que significa que la comunicación con los objetos C++, de Java, sólo implica trabajar con objetos Java.

Aquí se muestra un ejemplo:

Dadas dos clases de C++:

class SomeClass { 
public: 
    void foo(SomeOtherClass bar); 
    SomeOtherClass baz(); 
} 

class SomeOtherClass { 
public: 
    void do(); 
} 

TRAGO generará dos clases de Java:

public class SomeClass { 
    public void foo(SomeOtherClass bar); 
    public SomeOtherClass baz(); 
} 

public class SomeOtherClass { 
    public void do(); 
} 

Calling objetos C++, desde Java, es lo mismo que escribir Java normal:

SomeClass sc = new SomeClass(); 
SomeOtherClass soc = sc.baz(); 
sc.foo(soc); 

Línea 1: El contenedor Java de SomeClass se crea una instancia así como un objeto C++ del tipo SomeClass.

Línea 2: Las llamadas a la instancia sc de SomeClass se envían a la instancia de C++ de SomeClass. El valor de retorno de la instancia de C++ se pasa al contenedor de Java y el envoltorio de Java lo devuelve.

Línea 3: SWIG maneja la conversión de los tipos de envoltura de Java (o tipos de primitiva Java) a los tipos subyacentes de C++.

TRAGO se hará cargo de la conversión a/desde Java/C tipos ++ durante invocaciones de métodos y todos los detalles JNI están ocultos a la vista :)

El código de interfaz SWIG necesaria para generar un envoltorio de Java para una clase de C++ puede ser tan simple como:

interface.i: { # include "ClassDefinition.h" } % include "ClassDefinition.h"

TRAGO es muy potente. Todo lo que tenga que hacer lo puede hacer con las funciones básicas, los mapas de tipos, los mapas de tipos de javacode o los directores.

SWIG también permite que su código C++ llame a sus objetos Java sin ninguna modificación a su código C++ existente. Esta es una característica llamada "polimorfismo de lenguaje cruzado". el polimorfismo de lenguaje cruzado esencialmente le permite crear clases de Java que subclasifican las clases de C++. A continuación, puede pasar instancias de esas clases de Java como parámetros a las llamadas al método C++. Todas las invocaciones, desde C++, en la instancia aprobada se reenviarán a su objeto Java.No voy a entrar en detalles aquí, pero no es terriblemente complicado una vez que superas el impacto inicial de la idea.

+0

SWIG ha tendido a introducir los propios bloqueos en el código que de otra manera funcionaría bien por sí solo, y hace que sea difícil depurar dónde está el problema. –

+0

Lo hemos usado en dos proyectos hasta ahora. Los contenedores SWIG han manejado miles de llamadas por segundo y hemos envuelto cientos de clases. Todavía tenemos que experimentar un bloqueo que fue resultado del código generado por SWIG. Es cierto que hay muchos trucos y dificultades ya que los punteros a los objetos se comparten entre los dos idiomas. –

+0

Oye, esto se ve bastante prometedor. Definitivamente lo investigaré. – sparkFinder

Cuestiones relacionadas