28
Mar 11
JNI: Emulating Pointer-to-Pointers


I recently ran into an instance where I wanted to use several native libraries directly in Java and have the method signature model the original C API as closely as possible, allowing me to store opaque pointer types directly within a Java class. For anyone not familiar, opaque pointers are typically stored within Java classes using the long primitive type which is capable of storing most target platforms native pointer types (i.e. 8 or less bytes). One drawback of storing native pointers in pure Java is a lot of C API calls allocate resources using pointer-to-pointers or double pointers (i.e. sqlite3_open(…)). Unfortunately, JNI does not have a concept for manipulating pointer-to-pointers or reference types. That said, it is possible to emulate a pointer-to-pointer via JNI by using a primitive array wrapper (i.e. long[]).

For example suppose I wanted to model the following C function that returns a simple pass/fail result and outputs a newly allocated integer using a pointer-to-pointer.

int alloc_opaque_type(T** ppT);

The JNI equivalent would instead substitute the method’s first argument with a long primitive array.

public static native int alloc_opaque_type(long[] ppT);

The native JNI implementation then requires a slight pointer fixup between the Java primitive array type and the native pointer type which is accomplished using a simple union cast below.

JNIEXPORT jint JNICALL Java_Wrapper_alloc_1opaque_type
   (JNIEnv* env, jclass klass, jlongArray ppT)
{
   union
   {
      T* p;
      jlong    l;
   } __u;
   int retval = alloc_opaque_type(&__u.p);
   env->SetLongArrayRegion(ppT, 0, 1, &__u.l);
   return retval;
}

After the native code is compiled and loaded, you can then use the method with an opaque pointer as follows:

public long allocAndGetPointer()
{
   long[] opaque_ptr_ptr = new long[] { 0 };
   if(alloc_opaque_type(opaque_ptr_ptr))
      { return long[0]; }
   throw new RuntimeException("Could not allocate opaque pointer!");
}

4 comments

  1. What would be the jni equivalent for a c funtion that has the following signature

    void sum(int a, int b, int *result);

    How would I call the above from Java?

    Thanks,
    Sky

  2. @Sky: You can’t call a typical C function from Java directly. The primary foreign function interface in Java is JNI. You would need to provide a simple JNI wrapper function (in C) to call the code directly.

    The corresponding JNI native method provides the glue between Java and the original C function.

    JNIEXPORT void JNICALL Java_com_trevorpounds_WrapperClass_sum
       (JNIEnv* env, jclass klass, jint a, jint b, longArray ppResult)
    {
       union
       {
          int*  p;
          jlong  l;
       } __u = {0};
       sum((int) a, (int) b, __u.p);
       env->SetLongArrayRegion(ppResult, 0, 1, &__u.l);
    }

    You could call the above JNI function using the following class from the main method.

    package com.trevorpounds;
     
    public class WrapperClass
    {
       public static void main(String[] args)
       {
          long[] result = new long[1];
          sum(1, 2, result);
          System.out.println(result[0]);
       }
     
       public static native void sum(int a, int b, long[] ppResult);
    }

    You can read all about JNI’s nitty-gritty details in the JNI: Programmer’s Guide and Specification.

  3. Hi Trevor,

    Well, the above code example did not compile. In order to make it compile I changed ppT to ppResult in the following line

    env->SetLongArrayRegion(ppT, 0, 1, &__u.l);

    Ater that I tried to run it but I got a runtime error message indicating that __u was not initialized. Therefore, to what value should I initialize the union __u?

    Sky

  4. @ Sky: Sorry about that. I adapted the code from my original post and didn’t’ actually try compiling it. I’ve fixed the code in my previous comment. The union can be initialized to 0, assuming the sum(…) function is the one allocating the pointer.

Leave a comment

*