How to call cpp function from java?
- Create a header file (. h file) for a CPP program.
- Create CPP file.
- Create a DLL.
- In java code, declare the method as native, load the DLL using System. loadLibrary() method and call the method.
Why would you want to integrate C++ code into a Java program in the first place? After all, the Java language was created, in part, to address some of the shortcomings of C++. Actually, there are several reasons why you might want to integrate C++ with Java:
If you take the plunge and decide to integrate Java and C++, you do give up some of the important advantages of a Java-only application. Here are the downsides:
As you can see, integrating Java and C++ is not for the faint of heart! However, if you wish to proceed, read on.
We'll start with a simple example showing how to call C++ methods from Java. We'll then extend this example to show how to support the observer pattern. The observer pattern, in addition to being one of the cornerstones of object-oriented programming, serves as a nice example of the more involved aspects of integrating C++ and Java code. We'll then build a small program to test our Java-wrapped C++ object, and we'll end with a discussion of future directions for Java.
What's so hard about integrating Java and C++, you ask? After all, SunSoft's Java Tutorial has a section on "Integrating Native Methods into Java Programs" (see Resources). As we'll see, this is adequate for calling C++ methods from Java, but it doesn't give us enough to call Java methods from C++. To do that, we'll need to do a little more work.
As an example, we'll take a simple C++ class that we'd like to use from within Java. We'll assume that this class already exists and that we're not allowed to change it. This class is called "C++::NumberList" (for clarity, I'll prefix all C++ class names with "C++::"). This class implements a simple list of numbers, with methods to add a number to the list, query the size of the list, and get an element from the list. We'll make a Java class whose job it is to represent the C++ class. This Java class, which we'll call NumberListProxy, will have the same three methods, but the implementation of these methods will be to call the C++ equivalents. This is pictured in the following object modeling technique (OMT) diagram:
A Java instance of NumberListProxy needs to hold onto a reference to the corresponding C++ instance of NumberList. This is easy enough, if slightly non-portable: If we're on a platform with 32-bit pointers, we can simply store this pointer in an int; if we're on a platform that uses 64-bit pointers (or we think we might be in the near future), we can store it in a long. The actual code for NumberListProxy is straightforward, if somewhat messy. It uses the mechanisms from the "Integrating Native Methods into Java Programs" section of SunSoft's Java Tutorial.
A first cut at the Java class looks like this:
The static section is run when the class is loaded. System.loadLibrary() loads the named shared library, which in our case contains the compiled version of C++::NumberList. Under Solaris, it will expect to find the shared library "libNumberList.so" somewhere in the $LD_LIBRARY_PATH. Shared library naming conventions may differ in other operating systems.
Most of the methods in this class are declared as "native." This means that we will provide a C function to implement them. To write the C functions, we run javah twice, first as "javah NumberListProxy," then as "javah -stubs NumberListProxy." This automatically generates some "glue" code needed for the Java runtime (which it puts in NumberListProxy.c) and generates declarations for the C functions that we are to implement (in NumberListProxy.h).
I chose to implement these functions in a file called NumberListProxyImpl.cc. It begins with some typical #include directives:
In the NumberListProxy constructor, we call the native method initCppSide(). This method must find or create the C++ object we want to represent. For the purposes of this article, I'll just heap-allocate a new C++ object, although in general we might instead want to link our proxy to a C++ object that was created elsewhere. The implementation of our native method looks like this:
As described in the Java Tutorial, we're passed a "handle" to the Java NumberListProxy object. Our method creates a new C++ object, then attaches it to the numberListPtr_ data member of the Java object.
Now on to the interesting methods. These methods recover a pointer to the C++ object (from the numberListPtr_ data member), then invoke the desired C++ function:
The function names (NumberListProxy_addNumber, and the rest) are determined for us by javah. For more information on this, the types of arguments sent to the function, the unhand() macro, and other details of Java's support for native C functions, please refer to the Java Tutorial.
While this "glue" is somewhat tedious to write, it's fairly straightforward and works well. But what happens when we want to call Java from C++?
Before delving into how to call Java methods from C++, let me explain why this can be necessary. In the diagram I showed earlier, I didn't present the whole story of the C++ class. A more complete picture of the C++ class is shown below:
As you can see, we're dealing with an observable number list. This number list might be modified from many places (from NumberListProxy, or from any C++ object that has a reference to our C++::NumberList object). NumberListProxy is supposed to faithfully represent all of the behavior of C++::NumberList; this should include notifying Java observers when the number list changes. In other words, NumberListProxy needs to be a subclass of java.util.Observable, as pictured here:
It's easy enough to make NumberListProxy a subclass of java.util.Observable, but how does it get notified? Who will call setChanged() and notifyObservers() when C++::NumberList changes? To do this, we'll need a helper class on the C++ side. Luckily, this one helper class will work with any Java observable. This helper class needs to be a subclass of C++::Observer, so it can register with C++::NumberList. When the number list changes, our helper class' update() method will be called. The implementation of our update() method will be to call setChanged() and notifyObservers() on the Java proxy object. This is pictured in OMT:
Before going into the implementation of C++::JavaObservableProxy, let me mention some of the other changes.
NumberListProxy has a new data member: javaProxyPtr_. This is a pointer to the instance of C++JavaObservableProxy. We'll need this later when we discuss object destruction. The only other change to our existing code is a change to our C function NumberListProxy_initCppSide(). It now looks like this:
Note that we cast javaObj to a pointer to an HObservable. This is OK, because we know that NumberListProxy is a subclass of Observable. The only other change is that we now create a C++::JavaObservableProxy instance and maintain a reference to it. C++::JavaObservableProxy will be written so that it notifies any Java Observable when it detects an update, which is why we needed to cast HNumberListProxy* to HObservable*.
Given the background so far, it may seem that we just need to implement C++::JavaObservableProxy:update() such that it notifies a Java observable. That solution seems conceptually simple, but there is a snag: How do we hold onto a reference to a Java object from within a C++ object?
It might seem like we could simply store a handle to a Java object within a C++ object. If this were so, we might code C++::JavaObservableProxy like this:
Unfortunately, the solution to our dilemma is not so simple. When Java passes you a handle to a Java object, the handle] will remain valid for the duration of the call. It will not necessarily remain valid if you store it on the heap and try to use it later. Why is this so? Because of Java's garbage collection.
First of all, we're trying to maintain a reference to a Java object, but how does the Java runtime know we're maintaining that reference? It doesn't. If no Java object has a reference to the object, the garbage collector might destroy it. In this case, our C++ object would have a dangling reference to an area of memory that used to contain a valid Java object but now might contain something quite different.
Even if we're confident that our Java object won't get garbage collected, we still can't trust a handle to a Java object after a time. The garbage collector might not remove the Java object, but it could very well move it to a different location in memory! The Java spec contains no guarantee against this occurrence. Sun's JDK 1.0.2 (at least under Solaris) won't move Java objects in this way, but there are no guarantees for other runtimes.
Java is a platform independent programming language. An application written in Java does not require recompilation for running in different platforms. Java compiler never compiles the code to native platform instructions binaries which is often seen with C, C++ programming languages. Java compiler (JAVAC) takes .java source files and compiles them into object files. These object files are in the form of JAVA byte code. To run these object codes JAVA setup provides JAVA runtime Environment (JRE). JAVA runtime Environment consists of a program running tool as JAVA and a couple of libraries and run time binaries. These are platform dependent. JAVA makes JRE for each platforms. JRE for Windows, Linux, Android are different kind of binaries and they are native to platfrom architectures like x86, x86_64, ARM, ARM64 etc. JAVA byte codes are interpreted by the runtime binary to translate to native instructions and then they run as native code.
JAVA is good for writing codes without much knowledge of the platform on which it will be running. However system applications may need to access some of the system resources which are strictly platform dependent. C, C++ are the languages which can access OS resources and low level interfaces. Java cannot access this directly from java context. However there is a provision in Java to interface with native binaries. This interface is well known as Java Native Interface or JNI in short. JNI in the gateway between JAVA and native binaries, written in C/C++/VB or any other compiled languages. Most of the system applications are developed in two parts. One part which is tightly coupled to OS and low layer calls are written with C,C++. These binaries exposes native calls to JAVA environment. The other halves of the JAVA code calls these native interfaces.
Above code has three functions which are declared with native keyword. Native keyword informs Java Compiler that the function can be linked even if the definition of HelloWorld(), CallC() and SetMembers() do not exist. This is similar to importing a function from DLL/SO file. Java compiler puts these interfaces in import table or checks import table of DLL/so during runtime linking.
At this point if we try to execute this code we will get error as Java runtime will fail to load import library MyJNI.dll or libMyJNI.so.
So the java side of the application is ready but the native implementation part is missing here. Let us see what are the things we need here.
Native DLL binary will be generated by compiling and linking the header and source files. We will see how these three files will be generated in this tutorial.
We need a tool called JAVAH which generates this interface. JAVAH comes with JDK and no additional installation or setup is required. Javah can be executed in the same way as we execute comiler javac or java. Javah takes byte code file name (without .class extension) as argument. It process than binary and generates a C/C++ header file in the same name.
Note carefully javah has generated three function prototype in C/C++. JNIEXPORT macro tells C/C++ compiler to export these functions from DLL/SO library so that java run time can import these calls. Additionally there are two extra arguments for each functions. First argument is of type JNIEnv which gives a pointer to Java Runtime Environment. JNIEnv provides many interfaces which native binary can utilize. Next argument is of type jobject which represents the object itself or the "this" pointer. We can access members of the object through this arguments. Next arguments are same as declared in java. jboolean, jbyte, jchar, jshort, jlong, jfloat, jdouble are type definition of unsigned char, signed char, signed short, short, int, long, float and double in C/C++. Now java treats strings as UNICODE thus jstring is not directly char * but conversions are possible in both the ways.
Call by reference in java is not possible but this is equivalent of Call by reference in java. Java_MyJNI_SetMembers access and modify member variables of MyJNI class object and thus can be treated as the equivalent of call by reference.
Now we will implement these interfaces in a dynamic link library in Windows or in a shared library in Linux. We need to create a C/C++ source file and may need to copy this header file to the same folder. Here we created MyJNI.c in the same folder where MyJNI.h is located. We should include this file and implement the definitions.
jchar, jshort, jlong, jfloat, jdouble are type definition of char, short, long, float and double in C/C++ thus we can used these directly. But this is not true for the strings. Java treats strings char char as UNICODE. There are two bytes for each characters. First character contains ASCII value and next one contains language id. jstring can be converted to UTF char* and vice versa. GetStringUTFChars() can be used to extract char* from Java strings. NewStringUTF() is used to allocate a new UNICODE string from UTF char*. ReleaseStringUTFChars() is used to release existing buffer.
Here in Java_MyJNI_CallC(), we are simply printing the values passed from Java in C/C++ context and directly returning to Java context. Java_MyJNI_SetMembers() is not taking arguments. Every C++/Java function has inherent member called this pointer. This function uses this object to access all member variables. JNI can access and modify all the members. These are the steps to access/modify Java members.
This is only a demo however in practical situation where system applications need to access system resources can open device files and can call mmap/ioctls/sysctl etc to interface deeper inside into system side.
Now we need to place this native library in the same folder where our Java Class binary located and can run. If we have more than one class We can also place this in the Java Runtime binary folder jre\bin if we want to access this from more than one java class located in different folder.
About our authors: Team EQA
You have viewed 1 page out of 27. Your DLL learning is 0.00% complete. Login to check your learning progress.
Related Questions
- How to make ysolda own the bannered mare?
- How to activate roaming in jio?
- How to center desktop screen windows 10?
- How to size carhartt overalls?
- How to calculate mwh to kwh?
- How to factorise 5 terms?
- How to hide gboard app icon?
- How to pop axle back in?
- How to activate tp link router?
- How to handle sd card?