How to implement mapping from type to object method call using C++ templates?

84 Views Asked by At

I'd like to develop (yet another) wrapper for java arrays in JNI code using C++ templates. Main goal is to improve my C++ template programming skills.

I compile the code using Android NDK (compiler is Clang 6.0.2)

What I've managed to do so far:

#include <jni.h>

template <typename T>
struct TypeToObjectType;

template <>
struct TypeToObjectType<jbyteArray>
{
    typedef jbyte type;
};

template <>
struct TypeToObjectType<jintArray>
{
    typedef jint type;
};

template <typename javaArrayT, typename nativeT>
class Java_array{
    JNIEnv *env;
    jboolean is_copy;
    jsize array_len;

    javaArrayT array;
    typename TypeToObjectType<javaArrayT>::type *array_elements;

public:
    Java_array(JNIEnv *_env, javaArrayT _array)
        : env(_env)
        , array(_array)
        , array_len(-1)
    {
        array_elements = env->GetByteArrayElements(array, &is_copy);  //TODO: make it resolving automatically to other primitives
    }

    ~Java_array() {
        env->ReleaseByteArrayElements(array, array_elements, JNI_ABORT);
    }

    jsize len(){
        if (array_len < 0)
           array_len = env->GetArrayLength(array);
        return array_len;
    }

    operator nativeT* () const {
        return reinterpret_cast<nativeT*>(array_elements);
    };
};

This does not work for int[], float[] and other arrays except byte[], since this class calls GetByteArrayElements.

I use struct TypeToObjectType to establish mapping jbyteArray -> jbyte, jintArray -> jint.

This mapping is missing part java type -> JEnv method calls, that is jbyteArray -> (GetByteArrayElements, ReleaseByteArrayElements), jintArray -> (GetIntArrayElements, ReleaseIntArrayElements)`

How can I do it?

How can I improve my code using features from C++11, C++14?

1

There are 1 best solutions below

1
On BEST ANSWER

You can add pointer to members to your trait.

template <>
struct TypeToObjectType<jbyteArray>
{
    typedef jbyte type;
    static constexpr jbyte * (JNIEnv::* const GetElements)(jbyteArray, jboolean *) = &JNIEnv::GetByteArrayElements;
    static constexpr void (JNIEnv::* const ReleaseElements)(jbyteArray, jbyte *, decltype(JNI_ABORT)) = &JNIEnv::ReleaseByteArrayElements;
};

template <>
struct TypeToObjectType<jintArray>
{
    typedef jint type;
    static constexpr jint * (JNIEnv::* const GetElements)(jintArray, jboolean *) = &JNIEnv::GetIntArrayElements;
    static constexpr void (JNIEnv::* const ReleaseElements)(jintArray, jint *, decltype(JNI_ABORT)) = &JNIEnv::ReleaseIntArrayElements;
};

Which you call through slightly different syntax

Java_array(JNIEnv *_env, javaArrayT _array)
    : env(_env)
    , array(_array)
    , array_len(-1)
{
    array_elements = (env->*TypeToObjectType<javaArrayT>::GetElements)(array, &is_copy);
}

~Java_array() {
    (env->*TypeToObjectType<javaArrayT>::ReleaseElements)(array, array_elements, JNI_ABORT);
}