Some Thoughts on Interfacing Java to C++

These thoughts are derived from earlier ruminations concerning accessing ROOT objects from Java and a talk on the same subject at the 1999 root workshop.

They have been updated after discussions at the recent "Hep Migration to Java meeting" organized by Steve Fisher at CERN, as well as discussions with Mark Donszelmann and Julius Hrivinac.

Interesting material on this subject can also be found in "Essential JNI" chapters 9 and 10.


Accessing C++ classes from Java

Goals

Limitations

  1. Only C++ methods that accept primitives, or simple object references supported

  2. No template

  3. Only methods that return void, primitives or simple object references supported.

  4. No templates

  5. No access to C++ member variables from Java (they should be private anyway)

  6. No templates

Choices

A)     If we need to support C++ multiple inheritance then C++ classes must be mapped to Java Interfaces. Since Java interfaces support multiple inheritance there is no problem. Tool will generate a proxy class for each interface. Interfaces do not support static methods, so the interface cannot specify constructors. Constructors can still be implemented in the proxy class, but this means users should use the interface in all cases except class creation, when they should use the proxy class.

B)      If we do not need to support multiple inheritance then C++ class can be mapped to Java classes. This would allow the classes to also contain constructors and would even allow Java classes to subclass C++ classes (subject to limitation 5 above).

For now we assume option A.

Conversion Tool

The conversion tool will be written in Java (of course). A tool will expect to be provided a list of classes that implement an interface (CPPDescription) that describes a C++ class. The tool will generate:

1)      A corresponding Java interface. The Java interface will extend the Java interfaces corresponding to any superclasses of the C++ class being converted.

2)      A java proxy object that extends CPPProxy, and implements all the interfaces corresponding to the class. An implementation as a native method will be provided for each method and constructor.

3)      A C++ file containing a C++ implementation of the JNI methods.

Notes:

A)     Any C++ object references returned from the C++ side will result in Java Proxies being created, and the Java proxy being returned. Multiple returned references to the same C++ object will return the same Java proxy object.

B)      No protection is provided for a C++ object being deleted while a Java Proxy for the object exists. It is the users responsibility to guard against this.

C)      Each active Java Proxy object will have an entry in a (C+) hash table. This hash table will provide a unique index for each C++ object which has a Java Proxy, making it possible to implement (A) above.

D)     The Java Proxy finalizer will delete the corresponding entry from the hash table.

E)      Multiple implementations of CPPDescription can be provided, which could read IDL, .h files, interface to CINT etc).

What is needed:

1) Mapping from C++ primitives to Java primitives

2) Mapping from C++ collection classes to Java collection classes?


Implementation Notes

// An interface provided by CPPProxy
abstract class CPPUtil
{
   delete(); // deletes the C++ class corresponding to this Java Proxy.
}
// CPPProxy is the base class for all Java Proxy classes
abstract class CPPProxy implements CPPUtil
{
   protected CPPProxy(int handle)
   {
      this.handle = handle;
   }
   public native void delete();

   public void finalize()
   (
      release(handle);
   }
   private native void release(handle);
   protected int handle;  // index into C++ hash table
}

// An example of an Java Proxy class

public class Event_Proxy extends CPPProxy implements Event
{
   protected Event_Proxy(int handle)
   {
      super(handle);
   }
   public static native CPPEvent create();
   public native CPPTrack getTrack(int index);  
}

HashTable implementation.

On the C++ side we need to be able to find out if a given pointer is already in the table and, if so get its handle. Given a handle we must be able to immediately return the C++ pointer. A allocateHandle method must accept a jptr, and a C++ pointer, and return a handle. We also need a method that will return a jptr given a handle.