Calling C++ Native Code from Java
Recently, I faced a lot of troubles in finding a good article that completely explains the process of calling a C++ native function from Java. I did some experiments and I finally succeeded. I will be sharing the instructions step-by-step.
In this article, I will be taking a very basic example of taking an array of numbers as input in Java, computing their sum using a native C++ code and then printing the output back to the console using Java. I would be keeping the article concise and would encourage you to explore the official documentation in case you run into some issues. Also, I will be using Windows and GCC compilers. A 32-bit C++ compiler is compatible with 32-bit Java compiler and a 64-bit C++ compiler is compatible with 64-bit Java compiler. MinGW doesn't publish x64 versions of GCC compilers for Windows (it is only available for Linux). So, in case you are using Windows make sure that you install 32-bit Java version. I am also assuming that you are using JDK 8 or some above version. All the mentioned commands must be run from the same directory unless specified otherwise.
Let's begin.
To compile:-
> g++ -shared -fPIC NativeSumCalculator.cpp -o NativeSumCalculator.dll
-shared flag indicates generating a shared library that is dynamically loaded during runtime (if this library is supposed to be in a separate directory, then add the directory to the PATH) and -fPIC generates a position independent code. I am using .dll extension on Windows. Use .so extension on Linux (By default, Java searches for .dll file on Windows and .so on Linux when System.loadLibrary() is used. We would be using System.load() instead of System.loadLibrary(), which takes an absolute path to avoid confusion).
Once compiled, you may delete the C++ file ( and not the header file) as by doing this, we would be in a position that we only would be having the shared library that calculates the sum. Generally, this is always the case. The native code is in the form of a shared library with available method signature in a header file and we are supposed to write a bridge and a wrapper to call such methods.
Important: Move this shared library to one of the Java library locations. Use the command below to find out the location. Java will search for dependencies in these locations.
> java -XshowSettings:properties -version
> javac SumCalculator.java
You will get a class file. Use the javah tool (available with JDK) to generate the JNI header file. This JNI header file is a C++ header file and would be implemented in C++.
> javah SumCalculator
Note: From JDK 8 and above, the compilation and generation of JNI header file has been combined in a single command. -h tells the directory to place the generated header file. We would be placing it in the header file in the current working directory.
> javac SumCalculator.java -h .
The above header file would be implemented by the following C++ file. This C++ code would convert the Java types to C++ types, call the native library function and return the result back in the form of Java types. You may look at the official documentation for details regarding the conversion of Java and C++ types.
Compiling this C++ file is a bit tricky.
> g++ -shared -fPIC NativeBridge.cpp -o NativeBridge.dll -L. -l:NativeSumCalculator.dll -I "C:\Program Files\Java\jdk-12.0.1\include" -I "C:\Program Files\Java\jdk-12.0.1\include\win32"
-L flag tells the compiler about the directory containing the shared library (please specify the directory in case the shared library is not in the current working location). -l (small l) flag tells the compiler about the full name of the shared library file. -I (capital i) flag adds two C++ headers include folders which contain jni.h and jni_md.h. These header files contain information about the Java types. Replace the C++ include folders with the folder containing the header files.
In this article, I will be taking a very basic example of taking an array of numbers as input in Java, computing their sum using a native C++ code and then printing the output back to the console using Java. I would be keeping the article concise and would encourage you to explore the official documentation in case you run into some issues. Also, I will be using Windows and GCC compilers. A 32-bit C++ compiler is compatible with 32-bit Java compiler and a 64-bit C++ compiler is compatible with 64-bit Java compiler. MinGW doesn't publish x64 versions of GCC compilers for Windows (it is only available for Linux). So, in case you are using Windows make sure that you install 32-bit Java version. I am also assuming that you are using JDK 8 or some above version. All the mentioned commands must be run from the same directory unless specified otherwise.
Let's begin.
1. Create a C++ shared library
We will create a shared library that is written in C++ along with a header file which will only contain the signature of the native method. Remember, on Windows the shared libraries have .dll extension by convention and on Linux the shared libraries have .so extension.To compile:-
> g++ -shared -fPIC NativeSumCalculator.cpp -o NativeSumCalculator.dll
-shared flag indicates generating a shared library that is dynamically loaded during runtime (if this library is supposed to be in a separate directory, then add the directory to the PATH) and -fPIC generates a position independent code. I am using .dll extension on Windows. Use .so extension on Linux (By default, Java searches for .dll file on Windows and .so on Linux when System.loadLibrary() is used. We would be using System.load() instead of System.loadLibrary(), which takes an absolute path to avoid confusion).
Once compiled, you may delete the C++ file ( and not the header file) as by doing this, we would be in a position that we only would be having the shared library that calculates the sum. Generally, this is always the case. The native code is in the form of a shared library with available method signature in a header file and we are supposed to write a bridge and a wrapper to call such methods.
Important: Move this shared library to one of the Java library locations. Use the command below to find out the location. Java will search for dependencies in these locations.
> java -XshowSettings:properties -version
2. Write the Java code and generate the JNI headers
Our Java code has a single class SumCalculator with a native method which when called would be resolved to the C++ method. Compile the Java code.> javac SumCalculator.java
You will get a class file. Use the javah tool (available with JDK) to generate the JNI header file. This JNI header file is a C++ header file and would be implemented in C++.
> javah SumCalculator
Note: From JDK 8 and above, the compilation and generation of JNI header file has been combined in a single command. -h tells the directory to place the generated header file. We would be placing it in the header file in the current working directory.
> javac SumCalculator.java -h .
3. Implement the header file in C++
A SumCalculator.h header file would be generated after the above step. The contents of the file would be as follows:The above header file would be implemented by the following C++ file. This C++ code would convert the Java types to C++ types, call the native library function and return the result back in the form of Java types. You may look at the official documentation for details regarding the conversion of Java and C++ types.
Compiling this C++ file is a bit tricky.
> g++ -shared -fPIC NativeBridge.cpp -o NativeBridge.dll -L. -l:NativeSumCalculator.dll -I "C:\Program Files\Java\jdk-12.0.1\include" -I "C:\Program Files\Java\jdk-12.0.1\include\win32"
-L flag tells the compiler about the directory containing the shared library (please specify the directory in case the shared library is not in the current working location). -l (small l) flag tells the compiler about the full name of the shared library file. -I (capital i) flag adds two C++ headers include folders which contain jni.h and jni_md.h. These header files contain information about the Java types. Replace the C++ include folders with the folder containing the header files.
4. Load the library and run the Java program
We will be modifying the Java program to load the library by adding a static line. Please note that System.load() takes an absolute path to the library. The dependent shared library (NativeSumCalculator.dll) would be automatically loaded as it is in Java library location. Once loaded, re-compile the program and run it. The output would be displayed.
> javac SumCalculator.java
> java SumCalculator
> java SumCalculator
You are providing a post that is very useful for developing my knowledge and I learn more info from your blog
ReplyDeletejava training in anna nagar
Java training in chennai
selenium training in tambaram
Software testing training in Tambaram
RPA Training in Anna Nagar
Angularjs Training in Tambaram
Web Designing Course in Porur
php training in tambaram
dot net training in Velachery
Python Training in Tambaram
Thanks for sharing the best information and suggestions, I love your content, and they are very nice and very useful to us. If you are looking for the best C++ Tutorial,then TutorialCup.I appreciate the work you have put into this.
ReplyDeleteThis is really useful information. I am satisfied with reading your blog.
ReplyDeletehow to learn java easily
cross platform mobile app development
successful social media campaigns
what is microsoft azure
tableau interview questions and answers
Well Done...! it is very comprehensive and keeps updating here...
ReplyDeleteReactJS Training in Chennai
ReactJS Course in Chennai
PlC Training in Chennai
Thanks for sharing this article here about the Write A Good Article. Your article is very informative and I will share it with my other friends as the information is really very useful. Keep sharing your excellent work.How To Write A Good Article
ReplyDelete