# JNI Wrapper Klasse



## tdc (14. Okt 2012)

Hi,
mit JNI stehe ich gerade vor dem Problem eine Wrapperklasse zu schreiben. Ich muss also eine C++-Klasse/Methode aufzurufen, ohne sie vorher modifiziert zu haben.
Normalerweise muss man den Methodennamen ja folgendermaßen anpassen: Java_packageName_className_methodName
wenn ich das mit einer Methode mache, müsste ich ja von dort aus unmodifizierte C++-Methoden problemlos aufrufen können. Ich bekomme allerdings einen Error. (java: symbol lookup error: ...)

In meinem Beispiel habe ich eine Klasse Hello.cpp und eine Klasse World.cpp. Wenn ich jetzt den Header einbinde und die Methode "void create ()" mit einem "create();" aufrufe, bekomme ich obige Fehlermeldung.

Wie ruft man die Methode also richtig auf?


----------



## Marco13 (14. Okt 2012)

Einfach irgendeinen Namen modifizieren wird nicht reichen. Hast du den üblichen Weg verfolgt:
- native Methode in Java Klasse schreiben
- javah ausführen zum Header-Generieren
- Implementieren der Methode aus dem Header
...
?


----------



## tdc (14. Okt 2012)

Ja, das ist schon klar und funktioniert auch bereits. Die Frage ist nur, wie ich es machen soll, wenn ich ein größeres C++-Programm habe, was ich nicht komplett verändern möchte. In einem Tutorial wurde kurz angesprochen, man könnte eine Wrapperklasse schreiben. Das Problem ist nur, dass, wenn ich versuche aus einer angepassten C++-Klasse heraus eine "normale" C++-Methode aufzurufen, eine Fehlermeldung ausgegeben wird.


----------



## Marco13 (14. Okt 2012)

Was du mit "angepasst" meinst, ist mir immernoch nicht klar... (Nebenbei: Wäre https://github.com/twall/jna vielleicht was?)


----------



## tdc (15. Okt 2012)

Mit anpassen meine ich:
"Normales C++":

```
#include <iostream>
using namespace std;
 
int main()
{
	cout << "Hello World!" << endl;
}
```
"angepasst":

```
#include <jni.h>
#include <iostream>
using namespace std;

extern "C"
JNIEXPORT void JNICALL Java_jni_callnative(JNIEnv *env,
                                                  jobject obj)
{
   cout << "Hello World!" << endl;
   return;
}
```

Natürlich sind diese Anpassungen oft recht einfach, aber bei ein paar 100 Klassen _könnte_ es nervig werden.


----------



## troll (15. Okt 2012)

einfaches beispiel

>.../jni/JNIClass.java

```
package jni;
public class JNIClass
{
	public native void callnative();
}
```

call > javah jni.JNIClass

>.../jni_JNIClass.h

```
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jni_JNIClass */

#ifndef _Included_jni_JNIClass
#define _Included_jni_JNIClass
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     jni_JNIClass
 * Method:    callnative
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_jni_JNIClass_callnative
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
```

im implementierenden C-code ist nun die "jni.h" sowie "jni_JNIClass.h" einzubinden ... sowie die funktion Java_jni_JNIClass_callnative zu implementieren ...

einfach in irgendeine C-datei die "jni.h" einzubinden und einen zur klasse passenden funktions-namen zu wählen wird so halt nicht funktionieren ...
wenn du also wrapper basteln willst dann bau erstmal so normal eine JNI-lib und arbeite diese dann in dein bestehendes C-projekt ein ...


----------



## Marco13 (15. Okt 2012)

Ja, die Strategie wäre da eher so wie troll angedeutet hat, und aus der (automatisch mit allm drum und dran generierten) Methode heraus die 'eigene' Methode aufzurufen ('main' in dem Beispiel)


----------



## tdc (15. Okt 2012)

troll hat gesagt.:


> einfach in irgendeine C-datei die "jni.h" einzubinden und einen zur klasse passenden funktions-namen zu wählen wird so halt nicht funktionieren ...



Ja, das ganze drumherum mit nen Header generieren, einzubinden, das zu kompilieren, etc. funktioniert ja bereits.



Marco13 hat gesagt.:


> Ja, die Strategie wäre da eher so wie troll angedeutet hat, und aus der (automatisch mit allm drum und dran generierten) Methode heraus die 'eigene' Methode aufzurufen ('main' in dem Beispiel)



Ja, nur das aufrufen der Methode funktioniert bei mir noch nicht. Hier mal ein kleines Beispiel:
hello.cpp

```
#include "jniTest.h"
#include <iostream>
#include "world.h"
using namespace std;

extern "C"
JNIEXPORT void JNICALL Java_jniTest_callnative(JNIEnv *env,
                                                  jobject obj)
{
	cout << "Hello" << endl;
	create();
	return;
}
```

world.cpp

```
#include "world.h"
#include <iostream>

void create () {
	std::cout << "World!" << std::endl;
	return;
}
```

world.h

```
#ifndef world_H
#define world_H
void create();
#endif /* world_H */
```

Wenn ich in Hello.cpp den Aufruf "create();" weglasse, funktioniert alles. (Ausgabe: "Hello")
Wenn ich es aber so lasse, bekomme ich eine Fehlermeldung. (java: symbol lookup error:...)

jniTest.h ist übrigens der, aus der Java-Klasse generierte Header.


----------



## Gastredner (15. Okt 2012)

Ich bin jetzt nicht der C/C++-Experte, aber du solltest vielleicht mal versuchen, deine Funktionsdefinition von create in world.h als extern "C" zu definieren:

```
#ifndef world_H
#define world_H
#ifdef __cplusplus
extern "C" {
#endif
void create();
#ifdef __cplusplus
}
#endif
#endif /* world_H */
```
C++ verarbeitet Funktionsnamen anders als C (unter anderem um überladene Funktionen zu ermöglichen), weswegen C-Code C++-Funktionen, die nicht als extern "C" deklariert sind (und somit den gleichen Konventionen wie C-Funktionen unterworfen sind) nicht aufrufen kann.


----------



## tdc (15. Okt 2012)

Okay, jetzt ist zumindest die Fehlermeldung schöner. 
davor:

```
java: symbol lookup error: /home/.../JNITestpp2/libhello.so: undefined symbol: _Z6createv
```
jetzt:

```
java: symbol lookup error: /home/.../JNITestpp2/libhello.so: undefined symbol: create
```
Scheint aber nicht das einzige Problem an meinem Code zu sein...


----------



## Marco13 (15. Okt 2012)

Die Fehlermeldung ist die gleiche, einmal mit und einmal ohne Name mangling - Wikipedia, the free encyclopedia  

Scheint als würde da beim Linken irgendwas nicht klappen, aber dann sollte sich eigentlich schon der Linker beschweren. (Ich nehme auch an, dass du (spziell für diesen Test noch) keine anderen Libs einbindest). Bin auch nicht soo fit mit Linux Linking & Co (Verwöhnt von CMake und IDEs  ). Mehr als Websuchen ( java - Loading JNI crashes with undefined symbols - Stack Overflow etc) kann ich da im Moment leider nicht anbieten.


----------



## Gastredner (15. Okt 2012)

Also, ich habe das jetzt mal versucht nachzubauen und bei mir funktioniert es.

jni_JniClass.h:

```
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jni_JniClass */

#ifndef _Included_jni_JniClass
#define _Included_jni_JniClass

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     jni_JniClass
 * Method:    callnative
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_jni_JniClass_callnative
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif

#endif
```

jniImpl.cpp:

```
#include <iostream>
#include "jni_JniClass.h"
#include <jni.h>
#include "world.h"

JNIEXPORT void JNICALL Java_jni_JniClass_callnative(JNIEnv *, jobject) {
	std::cout << "In callnative()" << std::endl;
	create();
	return;
}
```

world.h:

```
#ifndef __WORLD_H
#define __WORLD_H

#ifdef __cplusplus
extern "C" {
#endif

void create();

#ifdef __cplusplus
}
#endif

#endif
```

world.cpp:

```
#include <iostream>
#include "world.h"

void create() {
	std::cout << "In create()" << std::endl;
	return;
}
```

JniClass.java:

```
package jni;
public class JniClass
{
	static {
		System.loadLibrary("jnitest");
	}
    public native void callnative();
}
```

Test.java:

```
import jni.JniClass;

public class Test {

	public static void main(String[] args) {
		JniClass test = new JniClass();
		test.callnative();
	}
}
```

Der Aufruf [c]C:\Users\ANON\Desktop>java -Djava.library.path=C:\Users\ANON\Desktop\jni\jnitest-build-desktop-Qt_4_8_1_for_Desktop_-_MSVC2010__Qt_SDK__Debug\release\ Test[/c] erzeugt dann auch die korrekte Ausgabe:

```
In callnative()
In create()
```
Fehlt dir vielleicht das [c]System.loadLibrary("your_lib");[/c]?


----------



## Marco13 (15. Okt 2012)

Ohne das loadLibrary würde er sich AFAIK schon über das 'callnative' beschweren. Die aktuelle Fehlermeldung deutet m.E. auf einen Fehler beim Linken hin. Poste vielleicht mal die Kommandozeilen, die du jetzt verwendet hast, dann kann tdc schauen, ob er da was anders hat...


----------



## Gastredner (15. Okt 2012)

Meinst du den Aufruf des C-Compilers? Habe ich nicht (mehr), habe den QtCreator zum Schreiben der C++-Anteile genommen und das Projekt mittlerweile wieder gelöscht. Der Aufruf von java steht ja im Post.


----------



## Marco13 (15. Okt 2012)

Ja, den meinte ich - da gibt's einige ziemlich trickreiche und wichtige flags (siehe auch der verlinkte Post). 
[ot]
Das haben die Linuxer nun davon, weil sie sich durch Tippen ellenlanger Zeilen an der Konsole als uber-Geek fühlen wollen, während man als Klickibunti-Windows-DAU nur auf "Compile" klickt  
[/ot]
Aber mal im ernst: @tdc Du hast das manuell an der Console compiliert? Vielleicht kann jemand erahnen (oder ggf. auch schlicht lokal bei sich ausprobieren) was da falsch sein könnte, wenn du die Compiler-Zeilen posten würdest...


----------



## troll (16. Okt 2012)

es gab hier docn vor ein paar wochen einen ähnlichen thread ... ich glaube es war der wo versucht wurde eine .net-lib mit java anzusprechen ...
dabei wurden einige compiler und linker optionen genannt die wohl für normale c-libs "lebenswichtig" sind ... vielleicht hilft die sufu den thread zu finden ...
wenn es nicht daran lag könnte es auch was damit zu tun haben das c und c++ gemis ht werden ... hier hilft allerdings das sun tut mit einem beispiel weiter


----------



## tdc (17. Okt 2012)

Danke für den Hinweis. Es lag wirklich an dem Compile-Befehl, so funktioniert es jetzt:

```
g++ -fPIC -shared -I/usr/local/java/include -I/usr/local/java/include/genunix hello.cpp world.cpp -o libhello.so
```
Muss ich jetzt jede *.cpp-Datei einzeln auflisten? Gibt es nicht eine bessere/schönere Lösung bei verdammt vielen Sourcefiles? (Okay, die Frage geht jetzt eher in Richtung C++ als Java...)


----------



## Marco13 (17. Okt 2012)

AFAIK würde man da eher makefiles (oder, als Mittelding, Batch/ShellScripte) verwenden...


----------



## tdc (23. Okt 2012)

Danke für die Hilfe, ich habs jetzt endlich hinbekommen! :toll:
(die letzten paar Tage war nicht mehr JNI mein Problem, sondern das ganze C++-Zeug zu kompilieren, jetzt funktioniert es aber endlich  )


----------

