Daily Archives: March 25, 2014

migration Java, C++ (day 15), professional), call C++ from Java, call Java from C++

logo

Guys, day15! I was not giving up. I haven’t found any example on the web that explains, how to return arrays of classes/structures when crossing language barriers. I was close to giving up. But after roughly six hours I had found the solution. And as usual: In hindsight it all looks simple.

Today is the last day I cover C++ in this series. There will be more, but not as a part of this 15 day bet. I have learnt a lot during these days. I proved someone else’s opinion wrong and can firmly support people with a similar attitude now. Well, so the conclusion and good news is: YES, you can learn C++ in 15 days and be on a good level afterwards. All you need is resilience.
Surely, 15 days are not enough to become an expert. But it was amazing to see how much someone can learn in such a short time.

EDIT 27 March:
I spoke to a friend yesterday. He asked me why I was not using SWIG. It is useful to learn driving a car with gears before you upgrade to automatic transmission. However, here is the link for further studies to Swig.

What settings do we need today?

In C++ add jvm.lib to Additional Dependencies of the Linker. Then enter C:\Program Files\Java\jdk1.8.0\lib in field Additional Library Directories.

Setting1

You also need additional include directories:
a) C:\Program Files\Java\jdk1.8.0\include\win32
b) C:\Program Files\Java\jdk1.8.0\include

Setting2

Google for javah for further research. (You don’t need it here as I provide all required code). You can use javah to generate header files for you. It simplifies your work a lot. Furthermore you may be interested in the memory management of the JNI. You don’t need to manage objects, which you generate via the JNI in C++, yourself. Great deal!

DOS

I created a temporary directory in C:\temp . It helped me to keep my environment neat and tidy. Some people prefer to store the files directly in the build directories. But this can cause problems when you clear all project data to rebuild your code from scratch.

JNI sometimes uses weird abbreviations for IDs like in lJNIEnv->GetMethodID(lTestClass, “getFriends”, “()[LFriend;”). They look worse than they are. In the brackets () you enter the input parameter types. Next to it, on the right side, you have the output parameter type. Do not add any space or separators. For instance (II)V would describe a method with two integer parameters and a void return type.

JvmTypeSignatures

Calling Java code from C++

import javax.swing.JOptionPane;

public class TestLib {

  double[]  DoubleArrayField  = { 1.1, 2.2, 3.3, 4.4, 5.5 };
  int       IntField           = 99;

  public TestLib() {
    System.out.println("Java: Executing constructor");
  } // constructor

  public static void main(String[] args) {
    System.out.println("Java: main()");
  } //

  public String ShowErrorMessage(String xMessage) {
    System.out.println("Java: Do you see the nice Java MessgeBox?");
    int dialogResult = JOptionPane.showConfirmDialog(null, "Therefore let's use Javax Swing and do the stuff that would take too much time in C++.", xMessage, JOptionPane.YES_NO_CANCEL_OPTION);
    switch (dialogResult) {
      case JOptionPane.CANCEL_OPTION:
        return "Cancel? Any other decision would have been better than that one!";
      case JOptionPane.YES_OPTION:
        return "Yes? PS:I lov ya!";
      case JOptionPane.NO_OPTION:
        return "No? What a pessimist you are!";
      default:
    }

    return "impossible";
  } //

  public Friend[] getFriends() {
    Friend[] lFriends = new Friend[6];

    lFriends[0] = new Friend(1946, "Michael Sylvester Gardenzio Stallone");
    lFriends[1] = new Friend(1978, "Rocky Balboa");
    lFriends[2] = new Friend(1982, "John Rambo");
    lFriends[3] = new Friend(2013, "Ray Breslin");
    lFriends[4] = new Friend(1993, "Gabe Walker");
    lFriends[5] = new Friend(1989, "Ray Tango");

    return lFriends;
  } //

} // class
#include <iostream>
#include <jni.h>
#include <string>

using namespace std;


JNIEnv* GetJVM(JavaVM ** xJVM) {
  JavaVMOption lOptions;
  lOptions.optionString = "-Djava.class.path=C:\\Ohta\\cppin15days\\Day15\\bin"; // path to your java source code

  JavaVMInitArgs lInitArgs;
  lInitArgs.version = JNI_VERSION_1_6;
  lInitArgs.nOptions = 1;
  lInitArgs.options = &lOptions;
  lInitArgs.ignoreUnrecognized = 0;

  JNIEnv *lJNIEnv;
  int lResult = JNI_CreateJavaVM(xJVM, (void**)&lJNIEnv, &lInitArgs);
  if (lResult < 0)  cout << "Cannot launch JVM" << endl;

  return lJNIEnv;
} //


void test() {
  JavaVM *lJavaVM;
  JNIEnv *lJNIEnv = GetJVM(&lJavaVM);
  if (lJNIEnv == nullptr)  return;

  jclass lTestClass = lJNIEnv->FindClass("TestLib");
  if (!lTestClass) { std::cerr << "C++: TestLib class not found." << std::endl; return; }

  // call main()
  jmethodID lMain = lJNIEnv->GetStaticMethodID(lTestClass, "main", "([Ljava/lang/String;)V");
  if (!lMain) { std::cerr << "C++: Failed to get main()." << std::endl; return; }
  jobjectArray lArguments = lJNIEnv->NewObjectArray(1, lJNIEnv->FindClass("java/lang/String"), NULL);
  jstring lFirstArgument = lJNIEnv->NewStringUTF("dummy");
  lJNIEnv->SetObjectArrayElement(lArguments, 0, lFirstArgument);
  lJNIEnv->CallStaticVoidMethod(lTestClass, lMain, lArguments);

  // instantiate our TestClass
  jmethodID lConstructor = lJNIEnv->GetMethodID(lTestClass, "<init>", "()V"); // get the constructor by using <init>
  jobject lInstance = lJNIEnv->NewObject(lTestClass, lConstructor);

  // DoubleArrayField
  jobject lJArray = lJNIEnv->GetObjectField(lInstance, lJNIEnv->GetFieldID(lTestClass, "DoubleArrayField", "[D"));
  jdoubleArray *lJDoubles = reinterpret_cast<jdoubleArray*>(&lJArray);
  jsize lJSize = lJNIEnv->GetArrayLength(*lJDoubles);
  jboolean lFalse(false);
  double *lDoubles = lJNIEnv->GetDoubleArrayElements(*lJDoubles, &lFalse);
  cout << "DoubleArrayField: ";
  for (int i = 0; i < lJSize; i++) cout << " " << lDoubles[i];
  cout << endl;

  // IntField
  jint lInt = lJNIEnv->GetIntField(lInstance, lJNIEnv->GetFieldID(lTestClass, "IntField", "I"));
  cout << "IntField: " << (int)lInt << endl;

  // ShowErrorMessage()  
  jboolean lTrue(true);
  if (lJNIEnv->IsInstanceOf(lInstance, lTestClass) == JNI_TRUE) { // <= this code line is just for demo purposes
    jmethodID lMethod = lJNIEnv->GetMethodID(lTestClass, "ShowErrorMessage", "(Ljava/lang/String;)Ljava/lang/String;");
    lFirstArgument = lJNIEnv->NewStringUTF("A kiss from C++");
    jobject lResult = lJNIEnv->CallObjectMethod(lInstance, lMethod, lFirstArgument); // arguments in function call ,CallCharMethodA => arguments in a jvalues array, CallCharMethodV => arguments in a va_list   
    jstring lString = reinterpret_cast<jstring>(lResult);
    const char *s = lJNIEnv->GetStringUTFChars(lString, nullptr);
    cout << "C++: Java said => " << s << endl;   
    //lJNIEnv->ReleaseStringUTFChars(lString, s);
  }
  
  // getFriends() 
  jmethodID lMethod = lJNIEnv->GetMethodID(lTestClass, "getFriends", "()[LFriend;");
  jobject lResult = lJNIEnv->CallObjectMethod(lInstance, lMethod);  
  jobjectArray lArray = reinterpret_cast<jobjectArray>(lResult);
  jclass lFriendClass = lJNIEnv->FindClass("Friend");  
  
  int n = (int)lJNIEnv->GetArrayLength(lArray);
  for (int i = 0; i < n; i++)  {
    jobject lFriend = lJNIEnv->GetObjectArrayElement(lArray, jsize(i));
    jfieldID lFieldId0 = lJNIEnv->GetFieldID(lFriendClass, "friendSince", "I");
    int lFriendSince = lJNIEnv->GetIntField(lFriend, lFieldId0);
    jfieldID lFieldId1 = lJNIEnv->GetFieldID(lFriendClass, "name", "Ljava/lang/String;");
    jobject h = lJNIEnv->GetObjectField(lFriend, lFieldId1);   
    jstring lName = reinterpret_cast<jstring>(h);
    const char *s = lJNIEnv->GetStringUTFChars(lName, nullptr);  
    cout << s << " is my best buddy since " << lFriendSince << endl;
  }

  cin.get();
} //

example output:
Java: main()
Java: Executing constructor
DoubleArrayField: 1.1 2.2 3.3 4.4 5.5
IntField: 99
Java: Do you see the nice Java MessgeBox?
C++: Java said => No? What a pessimist you are!
Michael Sylvester Gardenzio Stallone is my best buddy since 1946
Rocky Balboa is my best buddy since 1978
John Rambo is my best buddy since 1982
Ray Breslin is my best buddy since 2013
Gabe Walker is my best buddy since 1993
Ray Tango is my best buddy since 1989

Calling C++ code from Java


// -------------------------------------------------------------------------------------------------------------
//   Friend.java 
// -------------------------------------------------------------------------------------------------------------

public class Friend {
  public int		friendSince;	// value type
  public String	name;				// reference type

  public Friend(int xFriendSince, String xName) {
    friendSince = xFriendSince;
    name = xName;
  }
} // class

// -------------------------------------------------------------------------------------------------------------
//   TestProgram.java file
// -------------------------------------------------------------------------------------------------------------

import java.io.IOException;

public class TestProgram {

  public native String ShowErrorMessage(String xMessage);
  public native Friend[] getFriends();

  public static void main(String[] args) throws IOException {
    System.load("c:\\temp\\Day15.dll"); 
    TestProgram lTestProgram = new TestProgram();
    String s = lTestProgram.ShowErrorMessage("Java: Hello C++");
    System.out.println(s);

    Friend[] lFriends = lTestProgram.getFriends();

    for (Friend lFriend : lFriends) {
      System.out.println(lFriend.name + " is my best buddy since " + lFriend.friendSince);
    }
    System.in.read();
  } //
} // class
// -------------------------------------------------------------------------------------------------------------
//   TestProgram.h  (generated with javah.exe)
// -------------------------------------------------------------------------------------------------------------

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestProgram */

#ifndef _Included_TestProgram
#define _Included_TestProgram
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     TestProgram
 * Method:    ShowErrorMessage
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_TestProgram_ShowErrorMessage (JNIEnv *, jobject, jstring);

/*
 * Class:     TestProgram
 * Method:    getFriends
 * Signature: ()[LFriend;
 */
JNIEXPORT jobjectArray JNICALL Java_TestProgram_getFriends (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

// -------------------------------------------------------------------------------------------------------------
//  Day15.cpp file
// -------------------------------------------------------------------------------------------------------------

#include <stdio.h>
#include <iostream>
#include <string>
#include <jni.h>
#include "C:\temp\TestProgram.h"

using namespace std;

jstring Alloc(JNIEnv *xJNIEnv, const char *xText){
  return xJNIEnv->NewStringUTF(xText);
} //

JNIEXPORT jstring JNICALL Java_TestProgram_ShowErrorMessage(JNIEnv *xJNIEnv, jobject xObject, jstring xMessage) {
  const char *lMessage = xJNIEnv->GetStringUTFChars(xMessage, nullptr);
  cout << "C++: I am too lazy for MessageBoxes." << endl;
  wcout << "C++: Your message was: " << lMessage << endl;
  return Alloc(xJNIEnv, "C++: Good night!");
} //

void AddFriend(JNIEnv *xJNIEnv, jclass xFriendClass, jobjectArray xArray, int xIndex, int xFriendSince, const char *xFriendName) {
  jobjectArray lArguments = xJNIEnv->NewObjectArray(2, xFriendClass, NULL);
  jint lFriendSince(xFriendSince);
  jstring lName = xJNIEnv->NewStringUTF(xFriendName);
  jmethodID lConstructor = xJNIEnv->GetMethodID(xFriendClass, "<init>", "(ILjava/lang/String;)V"); // get the constructor by using <init>
  jobject lInstance = xJNIEnv->NewObject(xFriendClass, lConstructor, lFriendSince, lName);
  xJNIEnv->SetObjectArrayElement(xArray, jsize(xIndex), lInstance);
} //

JNIEXPORT jobjectArray JNICALL Java_TestProgram_getFriends(JNIEnv *xJNIEnv, jobject xObject) {
  //jclass lObjectClass = xJNIEnv->GetObjectClass(xObject);      
  jclass lFriendClass = xJNIEnv->FindClass("Friend");
  jobjectArray lArray = xJNIEnv->NewObjectArray(6, lFriendClass, NULL);
  if (!lFriendClass) { std::cerr << "C++: Friend class not found." << std::endl; return lArray; }

  AddFriend(xJNIEnv, lFriendClass, lArray, 0, 1946, "Michael Sylvester Gardenzio Stallone");
  AddFriend(xJNIEnv, lFriendClass, lArray, 1, 1978, "Rocky Balboa");
  AddFriend(xJNIEnv, lFriendClass, lArray, 2, 1982, "John Rambo");
  AddFriend(xJNIEnv, lFriendClass, lArray, 3, 2013, "Ray Breslin");
  AddFriend(xJNIEnv, lFriendClass, lArray, 4, 1993, "Gabe Walker");
  AddFriend(xJNIEnv, lFriendClass, lArray, 5, 1989, "Ray Tango");

  return lArray;
} //

example output:
C++: I am too lazy for MessageBoxes.
C++: Your message was: Java: Hello C++
C++: Good night!
Michael Sylvester Gardenzio Stallone is my best buddy since 1946
Rocky Balboa is my best buddy since 1978
John Rambo is my best buddy since 1982
Ray Breslin is my best buddy since 2013
Gabe Walker is my best buddy since 1993
Ray Tango is my best buddy since 1989