I am trying to generate java bindings through swig for these two c++ functions
C++ functions
void SetUserData(void* data);
void* GetUserData() const;
On the java side I want it to look like this
setUserData(Object object);
getUserData() //return java.lang.Object
I would like the void* pointer to point to any java object I pass to it. In my swig file I tried to add these lines
//swig file
%typemap(jstype) void* "java.lang.Object";
%apply Object {void*};
I get a compile error:
b2Fixture.swig(22) : Warning 453: Can't apply (Object). No typemaps are defined. I have no idea how to make typemaps for this. Any ideas?
You probably want to do:
%apply jobject { void* };
because %apply copies the typemaps defined on one C++ type to another C++ type. Object is a Java type, not a C++ one so doesn't have any typemaps to be copied. jobject on the other hand is the JNI equivalent.
Additionally assuming you want to have 'normal' GC semantics (i.e. not be required to retain whatever data you pass in) you'll need to do some more work, for example:
%module test
%{
#include "test.hh"
%}
%apply jobject { void * };
%typemap(in) void * {
void *old = getData();
if (old) JCALL1(DeleteGlobalRef, jenv, (jobject)old);
$1 = JCALL1(NewGlobalRef, jenv, $input);
}
%typemap(out) void * {
$result = (jobject)($1);
}
%include "test.hh"
This makes a new Global Reference for your data, which could be the only thing keeping it from getting freed by the GC.
Given a header file:
void setData(void *);
void *getData();
and an implementation:
#include "test.hh"
namespace {
void *d = nullptr;
}
void setData(void *d) {
::d = d;
}
void *getData() {
return d;
}
That is sufficient to allow:
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
Object example = "HELLO";
test.setData(example);
System.out.println(test.getData());
}
}
to work correctly.
As written these typemaps are pretty ugly - they'll impact all usage of void *. So if you really used this you would want to use %apply or %clear a second time to limit their impact. You could also name the arguments in the header file and use that to limit where the typemap gets applied.
For completeness, here's another (simpler?) way to do it with JavaCPP. Assuming we have a GlobalData.h file like this in C++:
class Data {
public:
JNIEnv* env;
jobject data;
Data(JNIEnv* env, jobject obj) : env(env), data(NULL) { }
~Data() { if (data != NULL) env->DeleteGlobalRef(data); }
void SetUserData(jobject obj) {
if (data != NULL) env->DeleteGlobalRef(data);
data = env->NewGlobalRef(obj);
}
jobject GetUserData() { return data; }
};
We can use it from Java this way:
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;
#Platform(include="GlobalData.h")
public class GlobalData {
static { Loader.load(); }
public static class Data extends Pointer {
public Data() { allocate(); }
private native #Raw(withEnv=true) void allocate();
public native void SetUserData(#Raw Object obj);
public native #Raw Object GetUserData();
}
public static void main(String[] args) {
Object someObject = new Object();
Data myData = new Data();
myData.SetUserData(someObject);
Object sameObject = myData.GetUserData();
System.out.println(someObject);
System.out.println(sameObject);
}
}
Where the output shows that someObject and sameObject point to the same object, for example:
java.lang.Object#7aa06577
java.lang.Object#7aa06577
Related
I'm new in JNI and C++. I have to call lib function with shared pointer. My code:
JNIEXPORT jint JNICALL Java_com_test_NativeClient_subscribe(JNIEnv* env, jobject thisObj, jobject handler) {
jclass handlerClass = env->GetObjectClass(handler);
jmethodID starts = env->GetMethodID(handlerClass, "starts", "(I)V");
jmethodID joins = env->GetMethodID(handlerClass, "joins", "(Ljava/lang/String;Ljava/lang/String;)V");
// int subscribe(std::shared_ptr< SomeHandler > handler) // I need implement this
std::shared_ptr<?> sharedPointer = new std::shared_ptr<?>;
return some::lib::subscribe(sharedPointer);
}
SomeHandler it is an interface from lib - some::lib::SomeHamdler, but also I pass java implementation in the method (jobject handler). How I can properly define sharedPointer to call java implementation after subscribe method performed? Thanks in advance.
UPD: Java code:
public native int subscribe(SomeHandler handler); // native method in NativeClient
SomeHandler interface:
public interface SomeHandler {
void starts(int uptime);
void joins(String mac, String name);
SomeHandlerImpl class:
public class SomeHandlerImpl implements SomeHandler {
#Override
public void starts(int uptime) {
System.out.println("uptime is " + uptime);
}
#Override
public void joins(String mac, String name) {
System.out.println("mac: " + mac + ", nName: " + name);
}
All you need to do is store a global reference to the jobject and write some wrapper code:
class JavaWrapperHandler : public some::lib::callback {
jobject java_handler;
public:
JavaWrapperHandler(jobject handler) {
JNIEnv *env = nullptr;
vm->GetEnv(&env, JNI_VERSION_1_6);
java_handler = env->NewGlobalRef(handler);
}
~JavaWrapperHandler() {
JNIEnv *env = nullptr;
vm->GetEnv(&env, JNI_VERSION_1_6);
env->DeleteGlobalRef(java_handler);
}
virtual joins(std::string mac, std::string name) {
JNIEnv *env = nullptr;
vm->GetEnv(&env, JNI_VERSION_1_6);
jclass handlerClass = env->GetObjectClass(java_handler);
jmethodID joins = env->GetMethodID(handlerClass, "joins", "(Ljava/lang/String;Ljava/lang/String;)V");
env->CallVoidMethod(java_handler, joins, ...);
};
};
And you can instantiate this as follows in your JNI method:
std::make_shared<JavaWrapperHandler>(handler);
Note that you still need to store the shared_ptr again somewhere, otherwise it will immediately be freed. You could for example store it in a std::map<long, shared_ptr<JavaWrapperHandler>> and return the long as a jlong.
Points of note:
This code keeps a global reference to prevent the Java handler object from being garbage collected.
The global reference is freed when the handler is destroyed. Make sure to unregister the callback at some point if you want to free the Java object.
We use the GetEnv method from the JNI Invocation API. It will only produce a useful value if the current (C++) thread has already been attached to the JVM. If it fails, you need to call vm->AttachCurrentThread or vm->AttachCurrentThreadAsDaemon.
I'm creating a simple C++ wrapper through Java JNI to access my Java code. My question is, I have enumeration in my Java code that I need to get the value from my C++ JNI code. What is the best way to do it?
Here is my snapshot of my Java code :
package mypackage;
public class ClassFooJava
{
public enum MyEnum
{
Enabled (0),
DoNothing (1),
Disable (2)
;
public int getValue()
{
return valueEnum;
}
private final int valueEnum;
private MyEnum(int valueEnum)
{
this.valueEnum = valueEnum;
}
}
static
{
try
{
System.load("MyCPlusPlus.dll");
}
catch(Exception e)
{
System.err.println("Caught exception: " + e.toString());
}
}
public native long GetInstance();
public native void FreeInstance(long LearningAccess);
public native int FooFunctionJava(MyEnum myEnum);
}
And here is my snapshot of my C++ code :
JNIEXPORT jint JNICALL Java_com_cummins_ctp_ClassFooJava
(JNIEnv * env, jobject obj,
jobject j_myEnum)
{
jclass cls = env->GetObjectClass(obj);
jfieldID myEnumField = env->GetFieldID(cls, "MyEnum", "Lmypackage.ClassFooJava;");
if (myEnumField == NULL)
{
// IT ALWAYS NULL
cout << "NULL NULL NULL" << endl;
return 0;
}
}
Notice that in my C++ code, the enumField is always null.
What is wrong with my code? Is my code doing the right thing to get the enumeration field within the object? Should I create the enumeration outside my ClassFooJava class?
I do really appreciate any help.
What is wrong with my code?
Java_com_cummins_ctp_ClassFooJava
The name of the function you are implementing is missing.
Is my code doing the right thing to get the enumeration field within the object?
You are passing in the MyEnum as an argument. It is j_myEnum. Perhaps you wanted the valueEnum
jfieldID valueEnumField =
env->GetFieldID(cls, "valueEnum", "Lmypackage/ClassFooJava$MyEnum;");
Should I create the enumeration outside my ClassFooJava class?
That's up to you.
I have some Java methods that I need to call from C++ via JNI. My JNI implementation is based on
Is it possible to make a Java JNI which calls jdbc?
There are two java files in my Java project. One is to define a class, and the other one contains the actual methods that c++ will call.
public class MyObject {
private static int no;
private static LocalDateTime time;
private static String status;
// getters, setters and toString
}
public class ObjectHandler {
public static MyObject objectReturnToC;
public static Object methodA (type1 arg1, type2 arg2, type3 arg3) {
objectReturnToC = new MyObject();
// setting fields in returnObject according to passed-in parameters
return objectReturnToC;
}
public static void methodB(Object objectReturnedFromC) {
// access fields in objectReturnedFromC, do computation and store in
}
}
I created C++ DLL in Visual Studio 2010. There's JVM.cpp, JVM.h, JavaCalls.h, and JavaCalls.cpp
JavaCalls.h
#ifdef JAVACALLSDLL_EXPORTS
#define JAVACALLSDLL_API __declspec(dllexport)
#else
#define JAVACALLSDLL_API __declspec(dllimport)
#endif
namespace JavaCalls
{
class JavaCalls
{
public:
static JAVACALLSDLL_API void *javaMethodA(type1, type2, type3);
static JAVACALLSDLL_API string toString(void **javaObject);
static JAVACALLSDLL_API void javaMethodB(void **javaObject);
};
}
JavaCalls.cpp
namespace JavaCalls
{
void *JavaCalls::javaMethodA(type1 arg1, type2 arg2, type3 arg3)
{
// invoke JVM
// Find class, methodID
jobject javaObject = CallStaticObjectMethod(jMain, "methodA",...);
return javaObject;
}
void JavaCalls::javaMethodB(void** javaObject) {
// invoke JVM
// Find class, methodID
CallStaticVoidMethod(jMain, "methodB",...);
}
}
C++ calling Java methodA and methodB using DLL:
int main()
{
void* a = JavaCalls::JavaCalls::javaMethodA(arg1, arg2, arg3);
// doing other stuff and updating fields in a
JavaCalls::JavaCalls::javaMethodB(static_cast<void**>(a));
}
Obviously, passing pointer, wishing it would be available to C++ is not working. But what should I do to keep the Java Object in C++ and pass it back to Java later? Should I create a C++ struct and map Java Object field into it using GetObjectField?
I don't quite understand why you need void** in your code. If you want to make the interface opaque, just use void*. Also don't forget to call NewGlobalRef() and DeleteGlobalRef() on the returned jobject - this will prevent its destruction by garbage collector:
void *JavaCalls::javaMethodA(type1 arg1, type2 arg2, type3 arg3)
{
jobject javaObject = CallStaticObjectMethod(jMain, "methodA",...);
return NewGlobalRef(jMain, javaObject);
}
void JavaCalls::javaMethodB(void* javaObject) {
CallStaticVoidMethod(jMain, "methodB", static_cast<jobject>(javaObject));
}
// add this method - it should be called when you finish using the object
void JavaCalls::ReleaseObject(void* javaObject) {
DeleteGlobalRef(jMain, static_cast<jobject>(javaObject));
}
There is a great helper program called javah.exe.
Write your code like you like to code and execute javah to the .class. It will create a full compatible .h file for you.
You will become for this:
public class Test {
public class MyObject {
private int no;
private String status;
}
public class Callback {
public void callback(MyObject afterCpp) {
}
}
public native void register(Callback v, MyObject beforeCpp);
}
This header:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Test */
#ifndef _Included_Test
#define _Included_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Test
* Method: register
* Signature: (LTest/Callback;LTest/MyObject;)V
*/
JNIEXPORT void JNICALL Java_Test_register
(JNIEnv *, jobject, jobject, jobject);
#ifdef __cplusplus
}
#endif
#endif
You simply have to call callback to the 3rd c++-parameter.
I have a JNI class with methods init() work(), and cleanup(). On the C++ side I create an instance of a C++ class Foo during init(), then call some methods on it during work(), and finally delete it inside cleanup(). Right now I store instance of Foo as a global singleton on the C++ so that I can retrieve it from the different JNI calls. What I would really like to do is store a pointer to the Foo instance inside the jobject instance that gets passed to each JNI call, so that I can avoid having a global singleton and also so that I can support multiple instances of Foo. Is something like this possible?
You can store a pointer to your C++ object as a Java class member. For example, in Java:
class Foo
{
public long ptr = 0;
public native void init();
public native void work();
public native void cleanup();
}
And in C++:
jfieldID getPtrFieldId(JNIEnv * env, jobject obj)
{
static jfieldID ptrFieldId = 0;
if (!ptrFieldId)
{
jclass c = env->GetObjectClass(obj);
ptrFieldId = env->GetFieldID(c, "ptr", "J");
env->DeleteLocalRef(c);
}
return ptrFieldId;
}
class Foo
{
/* ... */
};
extern "C"
{
void Java_Foo_init(JNIEnv * env, jobject obj)
{
env->SetLongField(obj, getPtrFieldId(env, obj), (jlong) new Foo);
}
void Java_Foo_work(JNIEnv * env, jobject obj)
{
Foo * foo = (Foo *) env->GetLongField(obj, getPtrFieldId(env, obj));
foo->work();
}
void Java_Foo_cleanup(JNIEnv * env, jobject obj)
{
Foo * foo = (Foo *) env->GetLongField(obj, getPtrFieldId(env, obj));
delete foo;
}
}
Absolutely.
Create a Foo instance in JNI. Simply return the pointer (points to the instance created) as a jlong type. So you can use it as a handler later. Here is an example:
JNIEXPORT jlong JNICALL Java_com_example_init(JNIEnv *env, jobject thiz) {
Foo* pFoo = new Foo();
if (NULL == pFoo) {
// error handling
}
pFoo->initialize();
return reinterpret_cast<jlong>(pFoo);
}
JNIEXPORT void JNICALL Java_example_start(JNIEnv *env, jobject thiz,
jlong fooHandle) {
Foo* pFoo = reinterpret_cast<Foo*>(fooHandle);
pFoo->start();
}
You can do it with a long in java, however I would argue that its not a very good idea to put a pointer to some native memory address in an instance variable of a language that is expected to be operating in a sandbox. Its sloppy and it could be a exploit vector depending on what your doing.
I am guessing you are running into this problem because your native code is very close to your JNI code. If you structure your JNI layer as translation between your native code and Java, you may find it easier to work with.
I have a function already implemented in cpp with prototype
MyFunction(int size, int (* callback)(UINT16* arg1, UINT16* arg2));
Second argument is a function pointer which must be implemented in java. How can i implement that function?
Also how can i call MyFunction in JNI? Please help
Try this
Java
import java.util.*;
public class JNIExample{
static{
try{
System.loadLibrary("jnicpplib");
}catch(Exception e){
System.out.println(e.toString());
}
}
public native void SetCallback(JNIExample jniexample);
public static void main(String args[]){
(new JNIExample()).go();
}
public void go(){
SetCallback(this);
}
//This will be called from inside the native method
public String Callback(String msg){
return "Java Callback echo:" +msg;
}
}
In C++ native:
#include "JNIExample.h"
JNIEXPORT void JNICALL Java_JNIExample_SetCallback (JNIEnv * jnienv, jobject jobj, jobject classref)
{
jclass jc = jnienv->GetObjectClass(classref);
jmethodID mid = jnienv->GetMethodID(jc, "Callback","(Ljava/lang/String;)Ljava/lang/String;");
jstring result = (jstring)jnienv->CallObjectMethod(classref, mid, jnienv->NewStringUTF("hello jni"));
const char * nativeresult = jnienv->GetStringUTFChars(result, 0);
printf("Echo from Setcallback: %s", nativeresult);
jnienv->ReleaseStringUTFChars(result, nativeresult);
}
The idea here is calling a method in Java through its class instance. In case you do not know the name of java function in advance then function name can be passed as parameter as well.
For more information: http://www.tidytutorials.com/2009/07/java-native-interface-jni-example-using.html
In your C++ native code you can simply define a function call Java callback implementation and then pass the function pointer to your native library -- like this:
native c++:
// link func
callback_link(int size, int (* callback)(UINT16* arg1, UINT16* arg2)){
//jni->call Java implementation
}
// call your lib
MyFunction(int size, int (* callback_link)(UINT16* arg1, UINT16* arg2));