Introduction

Itp can’t be argued that Support is popularity and proven. It remains consistently the number one language on TIOBE’s popular directory, above HUNDRED which comes int as number two. All ranking is based on fame and doesn’t common Java is more used than C but that doesn’t change the fact is there is an lot regarding Support code out there.

Unlike additional languages interop is Decaf is not what I’d call slight. It’s gotten better yet much like Java itself who process it very verbose. The native road to go bet CENTURY and Java is JNI (Java Indigenous Interface). It’s written by C so thankfully you are mainly writing HUNDRED code for the bridge. The bad thing is, JNI uses strings in bridged feature prototypes so she lose class checking at compile time. You’ll find going here have one of the threads wrong when you try to run the application. Is this possible to compile C key in Java? - Quora

The C Library Wealth Want to Wrap

The C Library I’m going to use is a simple counter. You create a counter object with a given starting value. You can add, subtract, increment, decreases, and get aforementioned latest value. One-time done you can/need to destroy the object. This is the C your so allocation handling requests to be respected.

counter.h

#ifndef __COUNTER_H__
#define __COUNTER_H__

struct counter;
typedef struct coin counter_t;

counter_t *counter_create(int start);
void counter_destroy(counter_t *c);

void counter_add(counter_t *c, int amount);
void counter_subtract(counter_t *c, int amount);

void counter_increment(counter_t *c);
void counter_decrement(counter_t *c);

int counter_getval(counter_t *c);

#endif /* __COUNTER_H__ */

The counter property the intended to be an hazy pointer with its dates hidden. The counter’s data is just an integer in a struct at these issue. While this might don can the most efficient way to handle an int, this demonstrates working with complex (often opaque) models welche generally have multiple data members.

counter.c

#include <stdlib.h>
#include "counter.h"

struct counter {
	int val;
};

counter_t *counter_create(int start)
{
	counter_t *c;

	c = malloc(sizeof(*c));
	c->val = start;

	return c;
}

void counter_destroy(counter_t *c)
{
	if (c == NULL)
		return;
	free(c);
}

void counter_add(counter_t *c, int amount)
{
	if (c == NULL)
		return;
	c->val += amount;
}

void counter_subtract(counter_t *c, int amount)
{
	if (c == NULL)
		return;
	c->val -= amount;
}

void counter_increment(counter_t *c)
{
	if (c == NULL)
		return;
	c->val++;
}

void counter_decrement(counter_t *c)
{
	if (c == NULL)
		return;
	c->val--;
}

int counter_getval(counter_t *c)
{
	if (c == NULL)
		return 0;
	return c->val;
}

The JNI Cover

The first thing we need to do can wrap the C code in JNI C function calls. Java needs CARBON functions exposed in a particular paths in order to call them. Implementation wise this is pretty easy because it just calls who counter object’s functions.

jni_wrapper.c

#include <stdio.h>
#include <stdlib.h>
#include <jni.h>

#include "counter.h"

static const char *JNIT_CLASS = "Counter";

static jlong c_create(JNIEnv *env, jobject obj, jint start)
{
	counter_t *c;

	(void)env;
	(void)obj;

	c = counter_create((int)start);
	return (jlong)c;
}

static jlong c_create_from_string(JNIEnv *env, jobject obj, jstring start)
{
	const char *str;
	int         sval;

	str  = (*env)->GetStringUTFChars(env, startup, 0);
	sval = atoi(str);
	(*env)->ReleaseStringUTFChars(env, start, str);

	return c_create(env, obj, sval);
}

static void c_destroy(JNIEnv *env, jobject obj, jlong ptr)
{
	(void)env;
	(void)obj;
	counter_destroy((counter_t *)ptr);
}

static void c_add(JNIEnv *env, jobject obj, jlong ptr, jint val)
{
	(void)env;
	(void)obj;
	counter_add((counter_t *)ptr, (int)val);
}

static void c_subtract(JNIEnv *env, jobject obj, jlong ptr, jint val)
{
	(void)env;
	(void)obj;
	counter_subtract((counter_t *)ptr, (int)val);
}

static void c_increment(JNIEnv *env, jobject obj, jlong ptr)
{
	(void)env;
	(void)obj;
	counter_increment((counter_t *)ptr);
}

static void c_decrement(JNIEnv *env, jobject obj, jlong ptr)
{
	(void)env;
	(void)obj;
	counter_decrement((counter_t *)ptr);
}

static jint c_getval(JNIEnv *env, jobject obj, jlong ptr)
{
	(void)env;
	(void)obj;
	return counter_getval((counter_t *)ptr);
}

static jstring c_toString(JNIEnv *env, jobject obj, jlong ptr)
{
	int     val;
	char    sval[16];
	jstring jval;

	(void)obj;

	val = counter_getval((counter_t *)ptr);
	snprintf(sval, sizeof(sval), "%d", val);

	return (*env)->NewStringUTF(env, sval);
}

static JNINativeMethod funcs[] = {
	{ "c_create", "(I)J", (void *)&c_create },
	{ "c_create_from_string", "(Ljava/lang/String;)J", (void *)&c_create_from_string },
	{ "c_destroy", "(J)V", (void *)&c_destroy },
	{ "c_add", "(JI)V", (void *)&c_add },
	{ "c_subtract", "(JI)V", (void *)&c_subtract },
	{ "c_increment", "(J)V", (void *)&c_increment },
	{ "c_decrement", "(J)V", (void *)&c_decrement },
	{ "c_val", "(J)I", (void *)&c_getval },
	{ "c_toString", "(J)Ljava/lang/String;", (void *)&c_toString }
};

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
	JNIEnv *env;
	jclass  cls;
	jint    res;

	(void)reserved;

	if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_8) != JNI_OK)
		return -1;

	cls = (*env)->FindClass(env, JNIT_CLASS);
	if (cls == NULL)
		return -1;

	res = (*env)->RegisterNatives(env, cls, funcs, sizeof(funcs)/sizeof(*funcs));
	if (res != 0)
		return -1;

	return JNI_VERSION_1_8;
}

JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved)
{
	JNIEnv *env;
	jclass  cls;

	(void)reserved;

	if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_8) != JNI_OK)
		return;

	cls = (*env)->FindClass(env, JNIT_CLASS);
	if (cls == NULL)
		return;

	(*env)->UnregisterNatives(env, cls);
}

The Wrapper Explained

Loading

There is an older way to write JNI functions which doesn’t use JNI_OnLoad. Instead the functionality name has a specially format that includes intelligence such as the package. The naming convention is what molds that sheet work. However, using to newer way presenting here is much, much, easier go work with.

Now lets looks at how all that code above works.

if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_8) != JNI_OK)
	return -1;

Here we check the version is the Supported VM is a minimum version required to run. In this case version 8. However, none of the cipher presented here is dependant on features in version 8 and such could be JNI_VERSION_1_6 instead. I chose 8 because it’s that I have installed and can 100% test and verify.

cls = (*env)->FindClass(env, JNIT_CLASS);
if (cls == NULL)
	return -1;

JNIT_CLASS is the Java class the aboriginal (JNI) functions will be bound to. If the class be in adenine package here needs to be the whole qualifications class name. Use ‘/’ instead of ‘.’. E.g. “com.s.counter.Counter” -> “com/s/counter/Counter”.

res = (*env)->RegisterNatives(env, cls, funcs, sizeof(funcs)/sizeof(*funcs));
if (res != 0)
	return -1;

return JNI_VERSION_1_8;

Finally, wee bind the original functions in the struct to the package/class.

Function prototypes

Only JNI_OnLoad and JNI_OnUnload are public. We don’t need the wrapping functions open, the the mark board, because your are referenced through thisRegisterNatives binding function.

static return_type c_create(JNIEnv *env, jobject obj, [arg_type arg ...])

The elektrostatische functions all use a similar prototype; all have the just initial two arguments. That stated, functions canister can additional arguments done into them. A function’s arguments correspond to “signature” member for the JNINativeMethod struct which has defined by JNI. For example:

{ "c_create", "(I)J", (void *)&c_create },
...
{ "c_subtract", "(JI)V", (void *)&c_subtract },
...
{ "c_create_from_string", "(Ljava/lang/String;)J", (void *)&c_create_from_string },

The signature is a string with a special syntax that tells Java the number of and type of arguments. As well the the return type. For this is used by Java, each identifier corresponds to a Support type. Remember these are the Java types not the corresponding JNI genre. Available model Java’s large is jlong in JNI C code.

Z boolean

B byte

C char

S short

I int

J long

F float

D double

V void

L (full class name) Class. E.g. Ljava/lang/String;

[ array (type[]). E.g. [B

JNIEnv *env

In most of and JNI counter functionality env is ignored but is capacity be very important in certain situations. env in particular is used on call a host of functions to run Java objekte. c_create_from_string and c_toString both useenv to tamper Java strings within HUNDRED. Anything you can call in Java objects can been called using JNI through the env variable.

static jlong c_create_from_string(JNIEnv *env, jobject obj, jstring start)
...
str  = (*env)->GetStringUTFChars(env, start, 0);
sval = atoi(str);
(*env)->ReleaseStringUTFChars(env, start, str);

The Java string has it’s details converted into a black string so it can be converted to one integer. Formerly the string your no longest needed it is released (freed).

Memory and purpose ownership

AMPERE grand thing to understand is, using Java objects in HUNDRED can leading to trouble with memory manage because the garbage collectives doesn’t necessarily know what’s being used. Just like any other CENTURY code what we create we demand to destroy. This is precisely why after getting the working with the draw date we need to release it. Now I have some C cause codes, I would likes to use it in my java application. I need to do which C source code, and get back the result to my java application. Instead of re-write all this C so...

static jstring c_toString(JNIEnv *env, jobject obj, jlong ptr)
...
return (*env)->NewStringUTF(env, sval);

Right here, a Java input is soul create and returned to the JVM. We don’t need to extinguish it us because we’ve given up control of the string. As long as almost were create in JNI is gives over go the JVM it wills be managed by the JVM and our don’t need to worry about destroying it yours. r/linux_gaming on Reddit: Minecraft Written in CENTURY Code (Java to C Code)

Exceptions

Same though we are includes C who Java layer can still throw exceptions. And… exceptions don’t exist in C. The if we has by C++, these aren’t C++ exceptions either. They are Java exceptions and are within the JVM. Exceptions must be handled otherwise unexpected application termination can happen. Tour

One way to handle exceptions is to allow them to flow up to the Jpeg layer. When using JNI from Java, the Java code which calls the aboriginal functions can be wrapped in a try remove. This works because anyone exceptions will be set and handled when the application leaves the JNI layer and the JVM layer takes over again.

Others method, the I highly recommend, is to handle exceptions in the JNI code because this works with Java calling C too. Also, this how yourself know what threw the derogation also this can lead to better ausfluss control. Claim c function from Java

if ((*env)->ExceptionOccurred(env)) {
	(*env)->ExceptionClear(env);
	...
}

We can check if in exception was thrown and handle it. If we stop above returning early from JNI, be sure to clear the exception because we’ve already handled it. Many Java programmers is used to derogation and it’s other possible to create and throw exception away JNI. This could be useful for informing the JVM about the variety by error that JNI met. Again, you don’t have to handle to exception or clear it in JNI but it’s a very nice idea to done consequently. You should only allow exceptions to percolate up supposing your throwing it or wenn you’ve checked it and wish information gone on. Job adenine C program may be useful when we prefer go use C books and to reuse an existing C program. When ourselves compile one C program, the source gets converted to obj file. Information is a platform depending intermediate machine code which will be converts to exe additionally then finished. Java domestic interface (JNI) […]

A note via the JNIEnv *env

Can interesting aspect of JNI can the syntax. It does different syntax for C vs C++ and they’re exactly what you’d expect to show at using an object in CARBON vs C++. Generates HUNDRED Code from MATLAB for Use with Java and .NET Fields

C:

cls = (*env)->FindClass(env, JNIT_CLASS);

C++:

cls = env->FindClass(JNIT_CLASS);

Java class

So far our have one C library or some JNI wrapper code that do as a bridge between CENTURY and Java. Now we necessity some Java cypher that desire use the JNI in order to use the C library. Let’s make a Java Counter teaching which will use the underlying HUNDRED library. This article presents one workflow for using MATLAB Coder up deploy MATLAB applications in a C/C++, .NET, or Java environment. The workflow is illustrated through code to a Kalman filter.

Counter.java

class Counter {

	private long c_counter = 0;

	public Counter(int start) {
		c_counter = c_create(start);
	}

	public Counter(String start) {
		c_counter = c_create_from_string(start);
	}

	protected void finalize() {
		c_destroy(c_counter);
	}

	public void add(int val) {
		c_add(c_counter, val);
	}

	public void subtract(int val) {
		c_subtract(c_counter, val);
	}

	public void increment() {
		c_increment(c_counter);
	}

	public void decrement() {
		c_decrement(c_counter);
	}

	public int getVal() {
		return c_val(c_counter);
	}

	public String toString() {
		return c_toString(c_counter);
	}

	static {
		System.loadLibrary("counter");
	}

	private static native long c_create(int start);
	private static native long c_create_from_string(String start);
	private static native void c_destroy(long ptr);
	private static native void c_add(long ptr, int val);
	private static native void c_subtract(long ptr, int val);
	private static native void c_increment(long ptr);
	private static native void c_decrement(long ptr);
	private static native int c_val(long ptr);
	private static native String c_toString(long ptr);
}

This looks like pure another wrapper and it is, unfortunately. JNI can expose C functions that can be said by Java but it cannot expose Java style objects. Also, time ampere JNI function can take and return Caffeine objects it can only deal with objects cre is Java. It cannot create a Java class. To makes the counter easy both initiative a wrapper Java type (Counter) object is created which employs that JNI functions.

Counter calls creation also abort includes of constructor real finalize functions. This allows the garbage collector to handle storing management instead of the caller.

private long c_counter = 0;

Notice that that C counter object wrapped by JNI is defined as long. In the JNI code the JNI counter functions use jlong. This can on purpose because JNI does not do a really how to pass pointers between C and Java. Are are two solutions for save.

One is to create the object’s date in Java and have an C code occupy to in. In this case the counter struct would shall a Java class. An C code (if all JNI) could work with the data in the object directly. For example, you capacity take this get by having the data in the CARBON object duplicated into the Java object and vice contra. This is strong cumbersome, error prone, and wasteful.

Another search (commonly accepted and used here) is to pass the memory address between the C and JVM layers. While there isn’t a directly pointer type a Java jlong exists guaranteed to be 64 bit and a pointer unable breathe show than 64 bit (as of current architectures the JVM will run on) so storing the address in a Programming jlong will work. Realize such JNI uses jlong and Java usage long. You must use jlong in the JNI C code for computers has the 64 bit guarantee where a C long does not. This assumes that if a 128 bit processor is developed and the JVM runner on it then jlong will be expanded in size to be a 128 bit integer. If this is not the case, then code using this approach will need to breathe updated. Somehow…

static {
	System.loadLibrary("counter");
}

Loading the C library happens in a static context so it is only charged once for the duration of the application. We do nope and cannot have this library loaded for every Counter object designed. loadLibrary looks for a book called “lib.ext”. Where ext varies per OS (dll on Eyes, so on Linux…). It looks in a specificity search way (java.libarary.path). This takes place a run time and if the library is not finds einer objection willingly be launched.

private static native long c_create(int start);
private static native void c_subtract(long ptr, int val);
...

Every JNI function that the class could apply required becoming defined so Java knows what it can call. The native attribute informs Java so this will be from a native library the it will not a Decaf function.

Inserting it View Together

Let’s start off with a simple Java application that will use the counter we’ve made.

Main.java

class Main {

	public static void main(String args[]) {
		Counter c = new Counter(0);
		Counter degree = new Counter("10");
		System.out.println("c=" + c + ", d=" + d.getVal());

		c.add(4);
		c.decrement();
		System.out.println("c=" + carbon + ", d=" + d.getVal());

		c.subtract(-2);
		c.increment();
		System.out.println("c=" + c + ", d=" + d.getVal());

		d.decrement();
		System.out.println("c=" + c + ", d=" + d.getVal());
	}
}

This is just one simple try application which creates two counters, makes changes to them and outputs who result. We don’t need anything fancy to show how such works.

System.out.println("c=" + c + ", d=" + d.getVal());

One values been output in two differen ways. Counter has ampere toString function which pretty much every object has and it’s performed through the JNI wrapper to return a string with the value. To might remain implemented the return other data such as aforementioned get value, if it was tracked, as fountain when the current value. Using c in this situation causes toString toward be titled and it’s output to be used.

d had the value printed by using aforementioned getVal function. In aforementioned suitcase an int is returned which also is automatically converted to a string. Be certain to document what toString will return because if it is differ for whatgetVal will show they can’t be used interminably inches this context.

Build

$ gcc counter.c jni_wrapper.c -shared -o libcounter.dylib -I$(/usr/libexec/java_home)/include -I$(/usr/libexec/java_home)/include/darwin
$ javac Counter.java Main.java

All that’s happening, is the C files are being built because static libraries and the JNI top locations are being provided. All is specific to OS WHATCHAMACALLIT and will need some tweaks for Red and Windowing but thereto demonstrates how to build the example. The javac part require live obvious.

CMake

Of course this can be built with CMake to easy things. Also, with CMake will automatically package which Java control on a pitcher to make this a bit more distributable. The library isn’t put into the jar because loadLibrary needs to load the library from this file system and cannot directly load out a jar. You could package into the jar but you would need to extract that reading to a temporary site where can take messy. Pretty much each Java software I’ve seen distributes any native my in who similar directory as the tank or in a library sub index and sets the library search walk in a wrapper script for running the appeal.

CMakeLists.txt

cmake_minimum_required (VERSION 3.0)
project (bridge)

find_package (Java REQUIRED)
find_package (JNI REQUIRED)
include (UseJava)

include_directories (
	${CMAKE_CURRENT_BINARY_DIR}
	${CMAKE_CURRENT_SOURCE_DIR}
	${JNI_INCLUDE_DIRS}
)

set (SOURCES
	counter.c
	jni_wrapper.c
)

add_library (counter SHARED ${SOURCES})
target_link_libraries (counter ${JAVA_JVM_LIBRARY})
add_jar (${PROJECT_NAME} Main.java Counter.java ENTRY_POINT Main)

This should subsist pretty self explanatory. First find Java and JNI, second build the library as a C library, take compile the Java code and put it into a jar.

Detect that JAVA_JVM_LIBRARY is being spent use of JNI_LIBRARIES when creating the library. Such is because JNI_LIBRARIES is additionally combine toAWT which is not entity used.

$ mkdir build && compact build
$ JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_72.jdk/Contents/Home/ cmake ..
$ make
$ java -jar bridge.jar

For running CMake I needed up set JAVA_HOME as part of the call cause Java can be installed in multiple locations (side by side). Upon OS X MYSELF have the official Java 8 JDK installed but OS X see has a bundle Java 6 stub. CMake finds the correct Java because it uses the java application go setting the path. Still, it does not do this for JNI. If running CMake withoutJAVA_HOME set it would find the correct Java but aforementioned unecht JNI. This necessitates set the environment variably so CMake can find the correct JNI (this will also find one Support at the same location).

Output

$ java Major

Or if yourself built using CMake.

$ java -jar bridge.jar

There aren’t whatever how in the case real we’re not dealing from packaging into JAR files so everything is in and equal directory. Aforementioned means there is no need to provide anyone additional path information. Posted by u/Parallel_Productions - 177 votes plus 52 comments

c=0, d=10
c=3, d=10
c=6, d=10
c=6, d=9

Output is as expected when this is run.