0

I have three C++ files that I want to use in Android studio.

  • Header.h
  • A.cpp (which contains the main method + other methods)
  • B.cpp

I have compiled them into a static library. Now I want to write the JNI wrapper around a C++ method and call it into the java part. Here is my wrapper so far :

#include <jni.h>
#include <string.h>
#include <stdlib.h>

extern "C" {


   JNIEXPORT int JNICALL Java_cgi_pi_detect(? ,?) {

   IplImage * byteQueryImage = loadByteImage ( ? );
  if ( !byteQueryImage )
  {
    printf ( "couldn't load query image\n" );
    return -1;
  }

  // Detect text in the image
  IplImage * output = textDetection ( byteQueryImage, atoi(1));
  cvReleaseImage ( &byteQueryImage );
  cvSaveImage ( ? , output );
  cvReleaseImage ( &output );
  return 0;
}
}

I want to give it two pictures as arguments : the one to load IplImage * byteQueryImage = loadByteImage ( ? ); and the one to save cvSaveImage ( ? , output );. What should be the jni types JNIEXPORT int JNICALL Java_cgi_pi_detect(? ,?) for these two arguments (if I consider that the pictures are .png) ?

Kraft
  • 39
  • 5

2 Answers2

1

Calling main() like that is fraught with peril. Which main() is going to be called? The JVM executable also has a main(). (And yes, I'm ignoring the "undefined behavior" as the question is about how to make it work.)

The hard way to get the main() you want is to compile it into a shared object, load that shared object, and find main() in that shared object yourself with runtime dynamic linking using dlopen() and dlsym() (error checking omitted):

#include <dlfcn.h>
...
// use a typedef for the function pointer
typedef int ( *main_func_t )( int, char ** );
...
// Handle to your .so with your "main()" in it
// make them static so they're only loaded once
static void *libHandle = NULL;
static main_func_t libMain = NULL;

if ( NULL == libHandle )
{
    libHandle = dlopen( "yourLibName.so", RTLD_NOW );
    libMain = ( main_func_t ) dlsym( libHandle, "main" );
}
...

// now call the main() in that library
int mainRetVal = libMain( argc, argv );
...

So that means you need two shared objects: the first "normal" one that holds your JNI calls, and the second that holds your "main()" that you want to call. The first "normal" JNI library will need to be linked with a dependency on libdl.so using an -ldl linker argument.

The easy way?

Rename the main() you want to call to something else and put it into your normal JNI shared object. Then just call it - it's no longer called main() so there's no longer any name conflict.

Even with all that, I suspect you still might run into problems - name collisions or incompatible libraries come to mind immediately.

An even easier way that will work?

Run it in a subprocess since that's the way it was designed to run, and it's effectively a black box anyway: you call it with arguments, it does whatever it does, and you get an int return value back. That's the same for a function call or a subprocess.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • as long as the "native app" is built into a static lib, calling its **main** is legitimate – Alex Cohn Jan 09 '16 at 21:14
  • How do I run it in a subprocess ? – Kraft Jan 09 '16 at 22:21
  • @AlexCohn *as long as the "native app" is built into a static lib, calling its main is legitimate* But how would you load it to run it then if you're doing the call from inside a Java process where the running binary is the JVM? – Andrew Henle Jan 11 '16 at 11:14
  • @Kraft *How do I run it in a subprocess ?* See this: http://stackoverflow.com/questions/3468987/executing-another-application-from-java – Andrew Henle Jan 11 '16 at 11:16
  • @AndrewHenle: with JNI, you need a wrapper function. The wrapper could be called **Java_com_company_pack_Class_main** and declared as `public static native void main()` in class **com.company.pack.Class**. Now there is no ambiguity. – Alex Cohn Jan 11 '16 at 12:12
  • @AlexCohn - But if by "static lib" you mean a `*.a` archive, I don't see any way to run that from within another binary (in this case the JVM) without linking it into an executable to be run separately or as a shared library (`*.so`), which could potentially introduce the ambiguity. Then again, it wouldn't be the first time I've missed seeing something... – Andrew Henle Jan 11 '16 at 13:37
  • JNI on Android works through *shared library*. The linker will create the shared library of A.cpp, B.cpp, and wrapper.cpp. The reference to *main()* inside A.cpp will be resolved on the stage of linking, and no other global *main* will confuse the dynamic linker (a.k.a. loader) at runtime. – Alex Cohn Jan 11 '16 at 20:12
  • I am sorry I added the notion of "static lib" to this potion, I was distracted by another discussion. It is quite common for native Android devs to prepare a static library (libMyNative.a) of platform independent code (in this case, A.o and B.o), and link this static lib into a shared lib by an equivalent of `g++ -shared wrapper.cpp -lMyNative -o libMyJni.so`. In this case again, external `main` function somewhere in `libMyNative.a` will be resolved unambiguously. – Alex Cohn Jan 11 '16 at 20:17
0

You can put the wrapper function in any file that will be compiled, new or existing. Unlike Java, in C++ the file name has no meaning for compiler.

You don't need to wrap other methods if you don't intend to call them independently from Java.

PS: note that the name Java_cgi_pi_detect of the wrapper function is derived from the Java class that defines this detect native method. Use javah tool to generate the correct name of the C function that implements a native Java method.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307