JNI and STL
C and C++ This lecture looks at: 8. JNI and STL I Java Native Interface (JNI) as an example of the sort of techiques you need for mixed language working. C (rather than C++) is often used because of it being a “high-level assembly language”. Alan Mycroft I The Standard Template Library for C++. University of Cambridge (heavily based on previous years’ notes – thanks to Alastair Beresford and Andrew Moore) The treatment is more of an overview compared to the previous lectures (i.e. you only have to understand the large-scale issues in inter-language Michaelmas Term 2013–2014 working and the STL, rather than needing to learn all the details).
1 / 41 2 / 41
JNI — Java Native Interface Justification
Java Native Interface (JNI) is the Java interface to non-Java code.
I interoperate with applications and libraries written in other programming languages (e.g., C, C++, assembly) Pro: I Reuse: allow access to useful native code
Examples we will look at: I Efficiency: use best language for the right task I Embedding C in Java
I Using Java from C Cons: Why use JNI? I Applets: mobility makes this painful I Portability: native methods aren’t portable I Low-level specifics not provided by Java I Extra work: javah, creating shared library I Performance (java is not famous for being fast)
I Legacy code wrapper
3 / 41 4 / 41
Embedding C in Java Generate JNI Header 1. Declare the method using the keyword native (without implementation) 2. (Make sure Java loads the required library) 3. Run javah to generate names & headers 4. Implement the method in C Compile HelloWorld.java 5. Compile as a shared library $ javac HelloWorld.java 1 class HelloWorld 2 { 3 public native void displayHelloWorld(); 4 static Create HelloWorld.h 5 { $ javah HelloWorld 6 System.loadLibrary("hello"); 7 } 8 public static void main(String[] args) 9 { 10 new HelloWorld().displayHelloWorld(); 11 } 12 } 5 / 41 6 / 41
HelloWorld.h HelloWorldImp.c
1 #include
$ java HelloWorld 1 class HelloWorld 2 { Sometimes: 3 ... 4 System.loadLibrary("hello"); Hello World! 5 ... 6 } Other times: Exception in thread "main" java.lang.UnsatisfiedLinkError: no hello in java.library.path Compile the native code into a shared library: at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1698) at java.lang.Runtime.loadLibrary0(Runtime.java:840) (on MCS machines, other OS may require the -I option) at java.lang.System.loadLibrary(System.java:1047) at HelloWorld.
9 / 41 10 / 41
Primitive Types and Native Equivalents The JNIEnv *env argument
I passed to every native method as the first argument.
I contains function entries used by native code.
Some examples: I organised like a C++ virtual function table. typedef unsigned char jboolean;// 8, unsigned typedef unsigned short jchar;// 16, unsigned In C: (*env)->(env,foo); typedef short jshort;// 16 In C++: env->(foo); typedef float jfloat;// 32 typedef double jdouble;// 64 Through JNI interface pointer, native code can: I operate on classes (loading, checking, etc.) Each Java language element must have a corresponding native equivalent I catch and throw exceptions I access fields I Platform-specific implementation I call methods I Generic programming interface I manipulate strings and arrays
I enter/exit monitors
11 / 41 12 / 41
Accessing Java Strings Accessing Java Array
The jstring type is not equivalent to the C string type 1 /* Wrong way*/ 2 JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env, \ 1 /* Wrong way*/ 3 jobject obj, jintArray arr) { 2 JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, \ 4 int i, sum = 0; 3 jobject obj, jstring prompt) 5 for (i=0; i<10; i++) { 4 { 6 sum += arr[i]; 5 printf("%s", prompt); ... 7 } ... 6 } 8 /* Right way*/ 7 /* Right way*/ 9 JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env, \ 8 JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env,\ 10 jobject obj, jintArray arr) { 9 jobject obj, jstring prompt) 11 int i, sum = 0; 10 { 12 /* 1. obtain the length of the array*/ 11 char buf[128]; 13 jsize len = (*env)->GetArrayLength(env, arr); 12 const char *str = (*env)->GetStringUTFChars(env, prompt, 0); 14 /* 2. obtaina pointer to the elements of the array*/ 13 printf("%s", str); 15 jint *body = (*env)->GetIntArrayElements(env, arr, 0); 14 /* release the memory allocated for the string operation*/ 16 /* 3. operate on each individual elements*/ 15 (*env)->ReleaseStringUTFChars(env, prompt, str); ... 17 for (i=0; i
Accessing Java Member Variables Calling a Java Method
1 class FieldAccess { 2 static int si;/* signature is"si"*/ 1. Find the class of the object 3 String s;/* signature is"Ljava/lang/String;"; Call GetObjectClass 4 }/* run javap-s-p FieldAccess to get the signature*/ 2. Find the method ID of the object 5 6 /* 1. get the fieldID*/ Call GetMethodID, which performs a look-up for the Java method in 7 fid = (*env)->GetStaticFieldID(env, cls, "si", "I"); a given class 8 /* 2. find the field variable*/ 3. Call the method 9 si = (*env)->GetStaticIntField(env, cls, fid); JNI provides an API for each method-type 10 /* 3. perform operation on the primitive*/ e.g., CallVoidMethod(), etc. 11 (*env)->SetStaticIntField(env, cls, fid, 200); You pass the object, method ID, and the actual arguments to the 12 13 /* 1. get the fieldID*/ method (e.g., CallVoidMethod) 14 fid = (*env)->GetFieldID(env, cls, "s", "Ljava/lang/String;"); jclass cls = (*env)->GetObjectClass(env, obj); 15 /* 2. find the field variable*/ 16 jstr = (*env)->GetObjectField(env, obj, fid); jmethodID mid = (*env)->GetMethodID(env,cls,’’hello’’,’’(I)V’’); 17 /* 3. perform operation on the object*/ (*env)->CallVoidMethod(env,obj,mid,parm1); 18 jstr = (*env)->NewStringUTF(env, "123"); 19 (*env)->SetObjectField(env, obj, fid, jstr); 15 / 41 16 / 41 Garbage Collection & Thread Issues Synchronisation
I Arrays and (explicit) global objects are pinned and must be explicitly I Synchronize is available as a C call released I Wait and Notify calls through JNIEnv do work and are safe to use I Everything else is released upon the native method returning
In C: (JNIEnv *) is only valid in the current thread In Java: (*env)->MonitorEnter(env,obj); synchronized(obj) { ... /* synchronized block*/ I Interface pointer are not valid to pass between threads /* synchronized block*/ I Local references are not valid to pass between threads ... } (*env)->MonitorExit(env,obj); I Thread access to global variables requires locking
17 / 41 18 / 41
Embedding a JVM in C JVMinC.c
1 #include
JVMinC.c (cont.) Compiling Embedded JVM and C
1 vm_args.version = JNI_VERSION_1_4; 1 public class JVMinCTest 2 vm_args.options = options; 2 { 3 vm_args.nOptions = 4; 3 public static void main(String [] args) 4 vm_args.ignoreUnrecognized = 1; 4 { 5 /* spawn JVM, find class and the class entry-point*/ 5 System.out.println("Hello from JVMinCTest"); 6 res = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args); 6 System.out.println("String passed from C: " + args[0]); 7 cls = (*env)->FindClass(env, "JVMinCTest"); 7 } 8 mid = (*env)->GetStaticMethodID(env, cls, "main", \ 8 } 9 "([Ljava/lang/String;)V"); To compile 10 /* createa valid string to pass*/ 11 jstr = (*env)->NewStringUTF(env, " from C!"); $ javac JVMinCTest 12 args = (*env)->NewObjectArray(env, 1, \ $ cc JVMinC.c -o JVMinC -L 13 (*env)->FindClass(env, "java/lang/String"), jstr); /usr/lib/jvm/java-1.6.0-openjdk-1.6.0/jre/lib/i386/client/ 14 /* Run and cleanup*/ -ljvm 15 (*env)->CallStaticVoidMethod(env, cls, mid, args); Running: 16 (*jvm)->DestroyJavaVM(jvm); $ ./JVMinCTest Hello from JVMinCTest 17 return 0; String passed from C: from C! 18 } $ 21 / 41 22 / 41
Standard Template Library (STL) Additional references
Alexander Stepanov, designer of the Standard Template Library says:
“STL was designed with four fundamental ideas in mind: I Musser et al (2001). STL Tutorial and Reference Guide (Second Edition). Addison-Wesley. I Abstractness
I Efficiency
I Von Neumann computational model
I Value semantics” I http://gcc.gnu.org/onlinedocs/libstdc++/documentation.html
It’s an example of generic programming; in other words reusable or “widely adaptable, but still efficient” code
23 / 41 24 / 41 Advantages of generic programming Plugging together storage and algorithms
I Traditional container libraries place algorithms as member functions Basic idea: of classes
I Consider, for example, "test".substring(1,2); in Java I I So if you have m container types and n algorithms, that’s nm pieces define useful data storage components, called containers, to store a of code to write, test and document set of objects I I Also, a programmer may have to copy values between container types define a generic set of access methods, called iterators, to manipulate to execute an algorithm the values stored in containers of any type I I The STL does not make algorithms member functions of classes, but define a set of algorithms which use containers for storage, but only uses meta programming to allow programmers to link containers and access data held in them through iterators algorithms in a more flexible way
I This means the library writer only has to produce n + m pieces of code The time and space complexity of containers and algorithms is specified in
I The STL, unsurprisingly, uses templates to do this the STL standard
25 / 41 26 / 41
A simple example Containers
1 #include
7 std::vector
27 / 41 28 / 41
Containers Using containers
1 #include
I vector
29 / 41 30 / 41 std::string Iterators
I Built-in arrays and the std::string hold elements and can be considered as containers in most cases I You can’t call “.begin()” on an array however! I Strings are designed to interact well with C char arrays I Containers support iterators, which allow access to values stored in a I String assignments, like containers, have value semantics: container I Iterators have similar semantics to pointers 1 #include
31 / 41 32 / 41 Iterator types Adaptors
I An adaptor modifies the interface of another component Iterator type Supported operators I For example the reverse_iterator modifies the behaviour of an iterator Input == != ++ *(read only) Output == != ++ *(write only) 1 #include
33 / 41 34 / 41
Generic algorithms Algorithm example
I Generic algorithms make use of iterators to access data in a container I Algorithms usually take a start and finish iterator and assume the I This means an algorithm need only be written once, yet it can valid range is start to finish-1; if this isn’t true the result is function on containers of many different types undefined I When implementing an algorithm, the library writer tries to use the most restrictive form of iterator, where practical
I Some algorithms (e.g. sort) cannot be written efficiently using Here is an example routine search to find the first element of a storage anything other than random access iterators container which contains the value element:
I Other algorithms (e.g. find) can be written efficiently using only 1 //search: similar to std::find input iterators 2 template
35 / 41 36 / 41
Searching over multiple containers Heterogeneity of iterators
1 #include "example23.hh" 1 #include "example24.hh" 2 2 3 #include "example23a.cc" 3 int main() { 4 4 char one[] = {1,2,3,4,5}; 5 int main() { 5 int two[] = {0,2,4,6,8}; 6 char s[] = "The quick brown fox jumps over the lazy dog"; 6 std::list
Function objects Higher-order functions in C++
I In ML we can write: foldl (fn (y,x) => 2*x+y) 0 [1,1,0]; I Or in Python: reduce(lambda x,y: 2*x+y, [1,1,0]) I Or in C++: I C++ allows the function call “()” to be overloaded 1 #include
39 / 41 40 / 41 Higher-order functions in C++
I By using reverse iterators, we can also get foldr:
1 #include
41 / 41