Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subclass a C++ abstract class in Java using JNI

I have a C++ library that I have to use in an existing Android implementation. I'm using Android NDK and using the C++ classes via JNI.

However, I am not able to find how to subclass a C++ abstract class in Java using JNI.

Problems I face: My aim is to provide Java implementation for the virtual methods in C++ by subclassing the abstract C++ class. I have loaded the native library and I'm trying to declare the native methods. The C++ methods have keyword 'virtual'. When I declare the native functions in Java after loading the C++ library, 'virtual' is not recognized. What is wrong here?

Any help is appreciated. I'm a newbie to JNI. Thanks in advance.

like image 862
vplusplus Avatar asked Aug 07 '13 18:08

vplusplus


1 Answers

Let's consider we have a C++ class:

class iVehicle
{
public:
   virtual void Run() {}; // not-pure virtual here for simplicity of a wrapper, but could be pure (see the end of the post)
   virtual int  GetSize() const; // we want to reuse it in Java
};

We want to create a class Bot in Java that extends class iVehicle in the sense that calls to super invoke the C++ code from iVehicle::GetSize() and, from the C++ point of view, we can use the instances of Bot as iVehicle* variables. That's tough since C++ provides no good built-in functionality for reflection.

Here is one possible solution.

To use C++ class in Java we need to generate a Java wrapper, i.e:

class iVehicle
{
   public void Run() { Native_Run(); }
   public int  GetSize() { return Native_GetSize(); }

   private native void Native_Run();
   private native int  Native_GetSize();

   // typecasted to pointer in C++
   private int NativeObjectHolder;

   // create C++ object
   native static private int CreateNativeObject();
}

The usage in Java is simple:

class Bot extends iVehicle
{
   public int GetSize()
   {
      if ( condition ) return 0;

      // call C++ code
      return super.GetSize();
   }
}

However, there is a C++ part to this code:

static jfieldID gNativeObjectHolderFieldID;

JNIEXPORT void JNICALL Java_com_test_iVehicle_Run( JNIEnv* env, jobject thiz )
{
   int Value = env->GetIntField(thiz, gNativeObjectHolderFieldID);
   iVehicle* Obj = (iVehicle*)Obj;

   // todo: add checks here, for NULL and for dynamic casting

   Obj->Run();
}

The similar code is for GetSize().

Then creating an instance of Java's Bot you have to call CreateNativeObject() and assign the returned value to the NativeObjectHolder field.

JNIEXPORT int JNICALL Java_com_test_iVehicle_CreateNativeObject( JNIEnv* env, jobject thiz )
{
   iVehicle* Obj = new iVehicle;
   return (int)Obj;    
}

So, this is the scheme. To make this work you will need to add the destruction code and to parse C++ classes to generate all this glue code.

Added:

In case where iVehicle is actually abstract you will have to generate a non-abstract wrapper that you are able to instantiate:

class iVehicle
{
   virtual void Run() = 0;
}

class iVehicle_Wrapper: public iVehicle
{
   virtual void Run() { ERROR("Abstract method called"); };
}

And instantiate iVehicle_Wrapper in CreateNativeObject(). Vuala! You have inherited an abstract C++ class in Java.

like image 133
Sergey K. Avatar answered Sep 27 '22 19:09

Sergey K.