Giter VIP home page Giter VIP logo

tommicheline / jniplusplus Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 317 KB

Modern c++ library for making JVM calls via JNI using variadic templates to reduce boilerplate, leverage automatic type conversion, and enable compile-time type checking.

Home Page: https://jniplusplus.micheline.dev/

License: BSD 2-Clause "Simplified" License

Java 15.70% C++ 79.46% Kotlin 1.40% CMake 3.22% SWIG 0.22%
jni jni-android jni-java cpp jvm

jniplusplus's Introduction

jni++ : C++ JNI without the boilerplate

Modern c++ library for making JVM calls via JNI using variadic templates to reduce boilerplate, leverage automatic type conversion, and enable compile-time type checking.

More information can be found at the project website.

Illustration by example

Let's say we want to call some regex Java code from C++ (I know, it's silly, but it is just an example). We can recreate the basic functionality of the following Java code:

    Pattern pattern = Pattern.compile(".*JNI.*rocks.*");
    Matcher matcher1 = pattern.matcher("This strings will see if JNI is dumb as rocks or not");
    Matcher matcher2 = pattern.matcher("This strings will see if jni is dumb as rocks or not");
    System.out.println("Does the pattern match text #1? %s", matcher1.matches() ? "YES" : "NO");
    System.out.println("Does the pattern match text #2? %s", matcher2.matches() ? "YES" : "NO");

First, lets see what this would look like using just plain JNI. The only part of the jni++ library used in this code is env().

    jclass patternClass = env()->FindClass("java/util/regex/Pattern");
    jclass matcherClass = env()->FindClass("java/util/regex/Matcher");
    if (!patternClass || !matcherClass) {
        getLogger()->error("SimpleApp: Could not get JNI classes for plain JNI example.");
        return -1;
    }
    
    jmethodID compileMethod = env()->GetStaticMethodID(patternClass, "compile", "(Ljava/lang/String;)Ljava/util/regex/Pattern;");
    jmethodID matcherMethod = env()->GetMethodID(patternClass, "matcher", "(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;");
    jmethodID matchesMethod = env()->GetMethodID(matcherClass, "matches", "()Z");
    if (!compileMethod || !matcherMethod || !matchesMethod) {
        getLogger()->error("SimpleApp: Could not get JNI methods for plain JNI example.");
        return -1;
    }
    
    jstring patternString = env()->NewStringUTF(".*JNI.*rocks.*");
    jobject patternObject = env()->CallStaticObjectMethod(patternClass, compileMethod, patternString);
    jstring matchingString1 = env()->NewStringUTF("This strings will see if JNI is dumb as rocks or not");
    jobject matcher1Object = env()->CallObjectMethod(patternObject, matcherMethod, matchingString1);
    jstring matchingString2 = env()->NewStringUTF("This strings will see if jni is dumb as rocks or not");
    jobject matcher2Object = env()->CallObjectMethod(patternObject, matcherMethod, matchingString2);
    
    jboolean result1 = env()->CallBooleanMethod(matcher1Object, matchesMethod);
    jboolean result2 = env()->CallBooleanMethod(matcher2Object, matchesMethod);
    
    std::cout << "[Plain JNI] Does the pattern match text #1? " << (result1 ? "YES" : "NO") << std::endl;
    std::cout << "[Plain JNI] Does the pattern match text #2? " << (result2 ? "YES" : "NO") << std::endl;

Next, lets see what it looks like using jni++:

    StaticMethod<jlocal, const std::string&> jCompilePattern("java.util.regex.Pattern", "compile");
    InstanceMethod<jlocal, const std::string&> jPatternMatcher("java.util.regex.Pattern", "matcher");
    InstanceMethod<bool> jMatcherMatches("java.util.regex.Matcher", "matches");
    
    auto pattern = jCompilePattern(".*JNI.*rocks.*");
    auto matcher1 = jPatternMatcher(pattern, "This strings will see if JNI is dumb as rocks or not");
    auto matcher2 = jPatternMatcher(pattern, "This strings will see if jni is dumb as rocks or not");
    
    std::cout << "Does the pattern match text #1? " << (jMatcherMatches(matcher1) ? "YES" : "NO") << std::endl;
    std::cout << "Does the pattern match text #2? " << (jMatcherMatches(matcher2) ? "YES" : "NO") << std::endl;

Not only is the jni++ code much shorter, it is easier to write and to understand. When writing the plain JNI section, several errors were introduced in the signatures which can be tricky to get right. In particular, I didn't notice that Pattern.matcher() takes a CharSequence not a String which wasted time in the debugger figuring out which method lookup failed and why.

Both of these examples are pulled directly from the simple example app included in examples/simple_app.

Build

First, make sure you have the submodules checked out. If you did not use --recursive (or similar) on your clone you need to get the submodules with something like:

# git submodule update --init --recursive
Submodule 'extern/doxygen-awesome-css' (https://github.com/jothepro/doxygen-awesome-css.git) registered for path 'extern/doxygen-awesome-css'
Submodule 'extern/googletest' (https://github.com/google/googletest.git) registered for path 'extern/googletest'
Cloning into '/home/vagrant/jniplusplus/extern/doxygen-awesome-css'...
Cloning into '/home/vagrant/jniplusplus/extern/googletest'...
Submodule path 'extern/doxygen-awesome-css': checked out 'a7f7891706c656903326f79baf74beb2b711688d'
Submodule path 'extern/googletest': checked out 'b796f7d44681514f58a683a3a71ff17c94edb0c1'
#

Currently, the C++ code uses cmake and the Java code uses gradle. Eventually, the build systems will be unified, probably to gradle. However, CLion gradle support seems to be a work in progress and there don't seem to be any plans to enable using the gradle native build plugins for in the AGP.

I use both CLion and IDEA to edit the two parts of the code. Using different IDEs for the native and java code causes its own friction above and beyond that caused by two different build systems. Both IDEs use the same project files, so they collide. To solve this there is a project folder (native_project) which links to the necessary parts of the native build. The Java build files are in the top level folder.

Building the Java library

You can either build it on the command line like ./gradlew :jni++:build or use an IDE that supports gradle based projects like JetBrains's IDEA. However, just building the project in IDEA will not create the jar file nor install it. You need to run the :jni++:build target for that.

Building the native library

You will either need cmake and do something like this:

        # cd <install location>/jniplusplus/native_project
        # mkdir cmake-build-cl
        # cd cmake-build-cl
        # cmake -S .. -B .
        # make install

Or you can also use CLion (which has its own copy of cmake) by selecting Install from the Build menu.

Deveoping / IDE

Using Intellij IDEA to edit the JVM code

To open the java code project in IDEA just open the jniplusplus directory (or the jniplusplus/settings.gradle.kts file) as a project. IDEA will load the gradle build in that directory and use it to find all the necessary paths.

Using CLion to edit the C++ code

To open the native code project in CLion just open the native_project directory as a project. CLion will load the CMakeLists.txt file in that directory and use it to find all the necessary paths.

Another option (Android Studio)

Android Studio allows you to edit and build both C++ and Java code but it really works best with an Android project. There is, however, an Android example project in examples/android that links to the jni++ libraries in such a way that it is pretty convenient to edit both even if you aren't interested in the App code itself.

Examples

There are a couple of examples in the examples directory to illustrate how jni++ can be used and experiment with either calling jni++ code from other languages (currently rust and go) or calling into other JVM languages (currently just Kotlin in the android example but more to come). It is a work-in-progress.

If you followed the build steps above, you should end up with the directory jniplusplus/install with top level lib, include, and bin directories. Assuming that all worked you can test it by running install/bin/simpleapp (source code in examples/simple_app) which will use both standard JNI calls and jni++ to do the same, simple calls into the JDK regex library. You will need to pass the path to jni++.jar on the command line:

# pwd
/home/parallels/Projects/jniplusplus
# ./install/bin/simpleapp install/lib/jni++.jar 
Is this upper case? 'HELLO, WORLD.'
[Plain JNI] Does the pattern match text #1? YES
[Plain JNI] Does the pattern match text #2? NO
Does the pattern match text #1? YES
Does the pattern match text #2? NO
# 

jniplusplus's People

Contributors

tommicheline avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.