Paludis meets Java, part III

Once we’ve converted native types we face the task of converting arbitrary classes and types. Another thing we’ll need is extract the C++ type of a Java object (jobject in JNI).

For that task, we will follow the convention of calling the native pointers in Java classes _ptr. With that, we can define the following templates:

template <typename T_>
inline T_ * get_native_ptr(JNIEnv * env, const char * const class_name, jobject obj)
{
    jclass cls(env->FindClass(class_name));
    jfieldID ptr_field(env->GetFieldID(cls, "_ptr", "J"));
    jlong ptr(env->GetLongField(obj, ptr_field));
    env->DeleteLocalRef(cls);
    return from_java_ptr<T_>(ptr);
}

template <typename T_>
inline tr1::shared_ptr<T_> get_native_sptr(JNIEnv * env, const char * const class_name, jobject obj)
{
    jclass cls(env->FindClass(class_name));
    jfieldID ptr_field(env->GetFieldID(cls, "_ptr", "J"));
    jlong ptr(env->GetLongField(obj, ptr_field));
    env->DeleteLocalRef(cls);
    return from_java_ptr_sptr<T_>(ptr);
}

This should really be doing more error checking, but it is good enough for ilustrating how nice are things when using proper tools (both languages and libraries).

Converting arbitrary types also uses some template magic:

template <typename T_>
struct NativeToJavaTypeMapper
{
    jobject operator() (JNIEnv *, const T_ &);
};

Now we need specializations for each type we want to convert, for instance, converting a paludis’ FSEntry into a java.io.File looks like the following:

template<>
struct NativeToJavaTypeMapper<FSEntry>
{
    jobject operator() (JNIEnv * env, const FSEntry & f)
    {
        jclass cls(env->FindClass("java/io/File"));
        jmethodID constructor(env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V"));
        jobject ret(env->NewObject(cls, constructor, to_java_string(env, stringify(f))));
        env->DeleteLocalRef(cls);
        return ret;
    }
};

Neat and clean.

For containers, a java.util.LinkedList would be used for paludis’ Sequence; and java.util.TreeSet for paludis’ Set:

template <typename T_, typename It_>
jobject range_to_list(JNIEnv * env, It_ begin, It_ end)
{
    jclass list_class(env->FindClass("java/util/LinkedList"));
    jmethodID constructor(env->GetMethodID(list_class, "<init>", "()V"));
    jobject our_list(env->NewObject(list_class, constructor));
    jmethodID add_method(env->GetMethodID(list_class, "add", "(Ljava/lang/Object;)Z"));

    for (It_ i(begin) ; i != end ; ++i)
        env->CallBooleanMethod(our_list, add_method, NativeToJavaTypeMapper<T_>()(env, *i));

    env->DeleteLocalRef(list_class);

    return our_list;
}

template <typename T_, typename It_>
jobject range_to_set(JNIEnv * env, It_ begin, It_ end)
{
    jclass set_class(env->FindClass("java/util/TreeSet"));
    jmethodID constructor(env->GetMethodID(set_class, "<init>", "()V"));
    jobject our_set(env->NewObject(set_class, constructor));
    jmethodID add_method(env->GetMethodID(set_class, "add", "(Ljava/lang/Object;)Z"));

    for (It_ i(begin) ; i != end ; ++i)
        env->CallBooleanMethod(our_set, add_method, NativeToJavaTypeMapper<T_>()(env, *i));

    env->DeleteLocalRef(set_class);

    return our_set;
}

And now defining NativeToJavaTypeMapper specializations for containers is quite easy:

template<>
template <typename T_>
struct NativeToJavaTypeMapper<tr1::shared_ptr<const Sequence<T_> > >
{
    jobject operator() (JNIEnv * env, const tr1::shared_ptr<const Sequence<T_> > & s)
    {
        return range_to_list<T_>(env, s->begin(), s->end());
    }
};

template<>
template <typename T_>
struct NativeToJavaTypeMapper<tr1::shared_ptr<const Set<T_> > >
{
    jobject operator() (JNIEnv * env, const tr1::shared_ptr<const Set<T_> > & s)
    {
        return range_to_set<T_>(env, s->begin(), s->end());
    }
};

I’ve spent a fair amount of the time fighting with make and the build system. It looks mostly sane now, though.

Dealing with exceptions has been a bit tricky, however, I think I have a good system to deal with it now, even though Ciaran tagged it as icky and ugly 🙂. That’d be the topic of the next part of the series.

The documentation is currently at http://dev.gentoo.org/~ferdy/paludis-jni/ . All of that has been accomplished in:

[ $ ~/git/paludis/jni(jni) ] git diff --shortstat trunk..
 65 files changed, 4371 insertions(+), 0 deletions(-)

Which is not a lot of code for what’s exposed.

— ferdy

Paludis meets Java, part II

After showing how is the structure of a regular Paludis class using JNI, next thing is showing part of the magic behind paludis_java.hh.

What’s there is functions to convert Java types into Paludis (and C++ native) types and viceversa (this conversions exist only when needed, not for every single type). So, for instance, one of those conversions would be jboolean <--> bool and it looks like this:

inline jboolean to_java_boolean(bool b)
{
    return b ? JNI_TRUE : JNI_FALSE;
}

inline bool from_java_boolean(jboolean b)
{
    return b == JNI_TRUE;
}

That was easy, let me show you the dirty part. I call it dirty not because it is difficult or tricky to understand, but because it is a bit icky. It is the way we store the native Paludis pointers in Java classes:

inline jlong to_java_ptr(void * const ptr)
{
    return reinterpret_cast<jlong>(ptr);
}

template <typename T_>
inline T_ * from_java_ptr(jlong ptr)
{
    return reinterpret_cast<_ *>(ptr);
}

template <typename T_>
inline tr1::shared_ptr<T_> from_java_ptr_sptr(jlong ptr)
{
    return * reinterpret_cast<tr1::shared_ptr<T_> *>(ptr);
}

Although dirty, it is quite easy aswell. Something this bindings will be converting a lot is strings:

std::string
from_java_string(JNIEnv * const env, jstring s)
{
    const char * const c_s(env->GetStringUTFChars(s, 0));
    std::string result(c_s);
    env->ReleaseStringUTFChars(s, c_s);
    return result;
}

jstring
to_java_string(JNIEnv * const env, const std::string & s)
{
    return env->NewStringUTF(s.c_str());
}

And since C++ is such a nice language comparing and stringifying arbitrary types was just as easy (credits for this go to Mr. McCreesh):

template <typename T_>
jstring common_stringify(JNIEnv * const env, jlong ptr)
{
    return to_java_string(env, stringify(*from_java_ptr<T_>(ptr)));
}

template <typename T_>
jint common_compare(jlong lhs_ptr, jlong rhs_ptr)
{
    T_ * const lhs(from_java_ptr<T_>(lhs_ptr)), * const rhs(from_java_ptr<T_>(rhs_ptr));
    if (*lhs < *rhs)
        return -1;
    else if (*rhs < *lhs)
        return 1;
    else
        return 0;
}

This wasn’t quite difficult and it certainly makes working with JNI easier. However, there is still stuff to do (actually, to show, since it is implemented and working in my git repository) like exception handling, converting arbitrary Paludis types and typesafe containers.

During the weekend I’ve written bindings for almost every core Paludis class and the patch is not that big:

[ $ ~/git/paludis(jni) ] git diff --shortstat trunk..
 30 files changed, 1975 insertions(+), 0 deletions(-)

Now it is time to stop the bindings for a while and start writing documentation, examples and integrating the bindings into the Paludis codebase properly.

Next part of the series will be about how I am converting arbitrary types and containers into Java types and typesafe collections respectively.

— ferdy

Paludis meets Java, part I

Some days ago, the Wearer of the shiny hat started tinkering with JNI bindings for Paludis.

Even if we all think Java is perverse and should be avoided at all costs. Having used Java for the last four years and having studied part of the Java5 code I thought I could use everything I learnt and took the task of developing and maintaining the JNI bindings.

It has all been relatively easy. It’s good that Ciaran started them; because his experience with the Ruby bindings gave the key to developing the Java bindings cleanly.

What we are currently doing is storing a C++ pointer in Java classes and passing it around in native methods. Then we delete it when Java’s garbage collector decides it is good to call finalize.

So it mostly looks like this:

package paludis;
import paludis.Paludis;

public class Foo {
    private static Paludis _load_the_frickin_paludis_library = new Paludis();
    private long _ptr;

    private static native long _construct_string(String s);
    private static native void _destruct(long ptr);

    private static native boolean _is_foo(long ptr);

    protected void finalize() throws Throwable {
        _destruct(_ptr);
        super.finalize();
    }

    public Foo(String s) {
        _ptr = _construct_string(s);
    }

    public boolean isFoo() {
        return _is_foo(_ptr);
    }
}

Then the C++ part looks like this:

/* vim: set sw=4 sts=4 et foldmethod=syntax : */

#include "foo.hh"
#include "paludis_java.hh"
#include <jni.h>
#include <paludis/foo.hh>

using namespace paludis;
using namespace paludis::java;

JNIEXPORT jlong JNICALL
Java_paludis_Foo__1construct_1string(JNIEnv * env, jclass, jstring s)
{
    return to_java_ptr(new Foo(from_java_string(env, s)));
}

JNIEXPORT void JNICALL
Java_paludis_Foo__1destruct(JNIEnv *, jclass, jlong ptr)
{
    delete from_java_ptr<Foo>(ptr);
}

JNIEXPORT jboolean JNICALL
Java_paludis_Foo__1is_1foo(JNIEnv *, jclass, jlong ptr)
{
    return to_java_boolean(from_java_ptr<Foo>(ptr)->is_foo());
}

Here, all the magic is happening behind the scenes in the paludis_java.hh header which I’ll show in a future post of the Paludis meets Java series.

— ferdy

Hidden PI

The people from Gaussianos.com (spanish blog abouth math) posted a couple of algorithms to calculate Pi and asked readers to provide some more.

I recently read on The Computational Beauty of Nature that Pi is hidden in the Mandelbrot set:

Take the mandelbrot iteration function:

z0 = 0
z = z^2 + c

And the initial complex number:

c = -3/4 + a * i

That set of complex numbers lays just between the two main parts of the body of the set, it is also known as the ‘neck’ (since it joins the cardiod with the biggest ‘head’).

For a = 0 the point belongs to the mandelbrot set. Let k be the number of iterations needed for a point (c = -3/4 + a * i) to reach modulus greater than 2 (escape radius), as a approaches to zero, k * a approaches to pi.

I wrote a simple C program that shows this (http://dev.gentoo.org/~ferdy/stuff/pi_mandel_gmp.c). When compiled, linked against libgmp and run, it outputs something like the following:

  a      || k         || aprox. pi
 --------++-----------++-----------
  1      || 3         || 3.0000000
  0.1    || 33        || 3.3000000
  0.01   || 315       || 3.1500000
  0.001  || 3143      || 3.1430000
  0.0001 || 31417     || 3.1417000
  1e-05  || 314160    || 3.1416000
  1e-06  || 3141593   || 3.1415930
  1e-07  || 31415927  || 3.1415927

It is an utterly expensive and pointless way to calculate pi, but it is one of the most obscure and weird ways to do it 🙂

Of course, all the credit goes to Dave Boll, who discovered the fact on 1991.

– ferdy