Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Callbacks from C++ back to Java

I have coded the following for numerical integration in C++:

// integrate.h:
#ifdef BUILDING_DLL
#define  DLL_MACRO __declspec(dllexport)
#else
#define  DLL_MACRO __declspec(dllimport)
#endif

extern "C" {
  typedef double (*Function1VariablePtr)(double x);
  double DLL_MACRO integrate(Function1VariablePtr function, double min, double max);
}

// integrate.cpp: 
#include "integrate.h"
double integrate(Function1VariablePtr function, double min, double max) {
  const int n = 1001;
  double dx  = (max - min)/(1.0*(n - 1));
  double sum = 0.0;
  for(int i = 0; i < n; i++) {
    double xmid = min + (i + 0.5)*dx;
    sum += function(xmid)*dx;
  }
  return sum;
}

Now I want to call this function from Java. I found how I can implement the integration directly in the JNI "bridge" code:

// C++ "bridge" code to from/to Java:  
JNIEXPORT jdouble JNICALL 
Java_IntegrateJApp_JIntegrate(JNIEnv *jnienv, jclass jc,    
                              jdouble xmin, jdouble xmax) {
  jmethodID mid = jnienv->GetStaticMethodID(jc, "Function1D","(D)D");
  if (mid == 0)
    return - 1.0;
  const int n = 1001;
  double dx  = (xmax - xmin)/(1.0*(n - 1));
  double sum = 0.0;
  for(int i = 0; i < n; i++) {
    double xmid = xmin + (i + 0.5)*dx;
    double f = jnienv->CallStaticDoubleMethod(jc, mid, xmid);
    sum += f*dx;
  }
  return sum;
}

// Java code calling "bridge":
class IntegrateJApp { 
  public static void main(String[] args) { 
      System.loadLibrary("JIntegrate");
      double I = JIntegrate(0.0, 2*Math.PI);
      System.out.println( Double.toString(I) ); 
  } 
  public static double Function1D(double x) {
      return Math.sin(x);
  }
  public static native double JIntegrate(double xmin, double xmax);
} 

However I do not want to implement the numeric integration directly in the C++ bridge code, but rather call the code in integrate.cpp.

How do I do this? The integrate() function inside integrate.cpp requires a function pointer which I do not have. Is there a way to get a pointer to a function inside Java using JNI?

Thanks in advance for any answers!

like image 397
Andy Avatar asked Jun 17 '11 08:06

Andy


2 Answers

You have to create a DLL with your C++ code and call it from JNI

Load the DLL:

System.loadLibrary("PATH\\yourdllname.dll");

Creating the link to your function

public static native integrate(parameters);
like image 171
RamonBoza Avatar answered Oct 14 '22 06:10

RamonBoza


One way that can be done is by using a pointer to member function and changing the signature of the integrate function.

See below the general idea:

functionwrapper.h

Declare a function wrapper class.

class FunctionWrapper
{
public:
    typedef double (FunctionWrapper::*Function1VariablePtr)(double x);

    FunctionWrapper(JNIEnv*, jclass);
    double compute(double x);
};

integrate.h

Eliminate the previous pointer to function typedef and change the method signature to include the wrapper object and a pointer to its member function.

#include "functionwrapper.h"
extern "C" {
    double DLL_MACRO integrate(FunctionWrapper*, FunctionWrapper::Function1VariablePtr, double min, double max);
}

integrate.cpp

Change the function invocation to a member function invocation.

#include "integrate.h"
double integrate(FunctionWrapper* wrapper, FunctionWrapper::Function1VariablePtr function, double min, double max)
{
    // ...
    sum += (wrapper->*function)(xmid)*dx;
    // ...
}

return sum; }

JNI "bridge" code:

Define the wrapper code and define the function that does the actual invocation. Invoke the integrate function directly from the JNI function:

#include "functionwrapper.h"

FunctionWrapper::FunctionWrapper(JNIEnv *jnienv, jclass jc) : m_jnienv(jnienv), m_jc(jc) {
    m_method= jnienv->GetStaticMethodID(jc, "Function1D","(D)D");
}

double FunctionWrapper:compute(double x) {
    return m_jnienv->CallStaticDoubleMethod(m_jc, m_method, x);;
}

// C++ "bridge" code to from/to Java:
JNIEXPORT jdouble JNICALL
Java_IntegrateJApp_JIntegrate(JNIEnv *jnienv, jclass jc,
                              jdouble xmin, jdouble xmax) {
    FunctionWrapper wrapper(jnienv, jc);
    return integrate(&wrapper, &FunctionWrapper::compute, 2, 3);
}
like image 45
Nick Avatar answered Oct 14 '22 05:10

Nick