简体   繁体   中英

Call C++ functions from Java

This is my first post on StackOverflow. I am working on a project and I need your help in JNI. I got stuck there... I have been reading and trying it a lot recently, but still didn't figure out how to make it work.

I have created a static library in C++ (a pseudo-code is shown below):

// file: X.h    
class X {
    public:
        X() {};
        ~X() {};

        void fooX() { // do stuff };
        void barX() { // do more stuff };
}

// file: X.cpp
#include "X.h";

// file: Y.h 
#include "X.h"
class Y {
    public:
        Y() {x = new X()};
        ~Y() {};

        fooY() { // do stuff };
        barY() { // do more stuff };

    private:
        X x;    // object of class X
}

// file: Y.cpp
#include "Y.h"

// file: Z.h 
#include "Y.h"
class Z {
    public:
        Z(uint8_t, std::string, std::vector<uint8_t>);
        ~Z() {};

        fooZ() { // do stuff };
        barZ() { // do more stuff };

    private:
        Y y;    // object of class Y
}

// file: Z.cpp
#include "Z.h"
Z::Z(uint8_t a, std::string b, std::vector<uint8_t> c)
{
     /// do stuff and create an object of Y
     Y y = new Y();
}

// file api.h
#include "Z.h"
void accessZ(uint8_t, std::string, std::vector<uint8_t>);

// file api.cpp
#include "api.h"
void accessZ(uint8_t a_uint, 
     std::string b_string, 
     std::vector<uint8_t> c_vector)
{
     // create object of Z
     Z z = new Z(a_uint, b_string, c_vector);
     z->fooZ();

     delete z;
     z = NULL; 
}

Further, I have compiled all of the above code as a static library (libXYZ.a) in C++ (Eclipse C++ CDT on Windows 7 using MinGW).

Now, what I would like to have is to be able to call the API accessZ() C++ function from a Java application! In other words, I have implemented the core functionality in C++ and I would like to implement the GUI in Java. Therefore, I need to access the C++ functions from the Java GUI...

How can I do that? I need direct answers and not vague ones please...

Your help is very appreciated.

Thanks a lot in advance for your support.

Edited

My project is as follows:

                                MyLibrary
                                    |
Makefile        header/     source/        bin/       object/     lib/
                X.h         X.cpp                     X.o       libXYZ.a
                Y.h         Y.cpp                     Y.o
                Z.h         Z.cpp                     Z.o
                api.h       api.cpp                   api.o

All what I need is to be able to pass some parameters from Java to libXYZ using the accessZ() function.

New Edit:

public final class NativeClass {
  {
    System.loadLibrary("myLibrary");
  }

  public native void accessZ(char a_uint, String b_string, char[] c_vector);

  public static final NativeClass getInstance() {
    return INSTANCE;
  }

  private static final NativeClass INSTANCE = new NativeClass();
};

You can't call a function like this directly, the function you will call from JNI will have to have a particular name and a particular form and will have to be represented by a member function in some Java class. (Also, Java does not have unsigned integers, so your uint8_t would have to be probably char or some other primitive type, and similarly you'll need to think what will represent the std::vector in Java.) But it's perfectly OK to

  1. make the Java function publicly accessible (unfortunately, it can't be static, a class instance is important),

  2. have the native implementation do nothing else than extract the parameters and call accessZ() .

Typically, your code will look something like

public final class NativeClass {

  {
    System.loadLibrary("myLibrary");
  }

  public native void accessZ(char a_uint, String b_string, char[] c_vector);

  public static final NativeClass getInstance {
    return INSTANCE;
  }

  private static final NativeClass INSTANCE = new NativeClass();

};

Then your myLibrary.cpp will have to include <jni.h> and "api.h" and to contain a function along the lines of

JNIEXPORT void JNICALL Java_NativeClass_accessZ(JNIEnv *env, jobject obj, jchar ja_uint, jstring jb_string, jcharArray jc_vector) {

  uint8_t a_uint = (uint8_t)ja_uint;

  const char *cfn = env->GetStringUTFChars(jb_string, 0);
  std::string b_string{cfn};
  env->ReleaseStringUTFChars(jb_string, cfn);

  size_t sz = env->GetArrayLength(jc_vector);
  std::vector<uint8_t> c_vector(sz);
  jchar *c_array = env->GetCharArrayElements(jc_vector, NULL);
  for(size_t i = 0; i < sz; i++)
    c_vector[i] = c_array[i];
  env->ReleaseCharArrayElements(jc_vector, c_array, 0);

  accessZ(a_uint, b_string, c_vector);

}

wrapped in an extern "C" block. Your JNI tutorial will tell you how to go about making a dynamic library out of it and helping Java find it. Just link the other parts of your project ( libXYZ.a ) with it statically when creating the .dll (ie, list the archive among the sources). You can then call from within Java for example:

NativeClass.INSTANCE.accessZ((char)10, "abc", new char[]{3, 5, 7});

This function will become the access point of your library but then can do whatever it wants, it can create objects, call functions on them, it may even keep a transient state because your library will keep running between the calls. (It's better to operate on a state object passed to the function, though.) There may be arbitrarily many of functions exposed by the library to Java like this.

Here are my command lines (for Linux):

g++ -isystem /usr/java/jdk1.8.0_101/include/ -isystem /usr/java/jdk1.8.0_101/include/linux myLibrary.cpp -c -fPIC
(creates myLibrary.o)

g++ myLibrary.o libXYZ.a -shared -o libmyLibrary.so
(creates libmyLibrary.so)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM