Use JNI to call graalvm native build shared library - java-native-interface

I am learning graalvm, and I was wondering if it is possible that I can use JNI to call a java native-built shared library?
Let's say there are two Java source codes, the first would be compiled as a shared library ahead of time with graalvm, and the second one would be run on JVM, which would load the shared library generated by the first source code in runtime. I find this difficult in practice since the header files generated by JNI and graalvm native-build are different in their signature.
Is there any good way I can do this?
The two source codes and their generated header files are as follows:
LibEnvMap.java:
import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.nativeimage.c.function.CEntryPoint;
public class LibEnvMap {
//NOTE: this class has no main() method
#CEntryPoint(name = "Java_Main_HelloWorld")
private static int HelloWorld(IsolateThread thread, Object object){
System.out.println("Hello Native World!");
return 0;
}
}
#ifndef __LIBENVMAP_H
#define __LIBENVMAP_H
#include <graal_isolate.h>
#if defined(__cplusplus)
extern "C" {
#endif
int filter_env(graal_isolatethread_t*, char*);
#if defined(__cplusplus)
}
#endif
#endif
Main.java:
public class Main {
static {
System.load("/Users/nealshinoda/Repos/HelloWorld/out/production/HelloWorld/LibEnvMap.dylib");
}
private native int HelloWorld();
public static void main(String[] args) {
System.out.println("Hello world!");
new Main().HelloWorld();
}
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Main */
#ifndef _Included_Main
#define _Included_Main
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Main
* Method: HelloWorld
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_Main_HelloWorld
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
As you can see from the two header files: their signatures are incompatible and it is difficult to hack the type of the parameter since they are all machine-generated.
One potential solution that comes to my mind is that I could use another C/C++ code as a "middle layer", and then the call stack could be JNI ==> C/C++ ==> graalvm native library. However, this solution seems too complicated, and is there any better practice?

Related

Right way to initialize a static library (C++)?

I have a static C library that I want to port to C++, in the C library I got some global variables that store some common data used by the functions for example:
// global variable in the C library
int global_number_of_cpu_cores;
init_global_vars()
{
global_number_of_cpu_cores = get_info();
}
void lib_function()
{
// use global_number_of_cpu_cores
}
when using the library, it must be first initialized by the init function but in C++, the object's constructors are executed before the main function, so I cannot code:
class class_lib
{
class_lib()
{
// use global_number_of_cpu_cores but this is uninitialized!
}
}
Also, you can initialize global variables with functions:
int program_var = lib_function(); // lib_function uses global_number_of_cpu_cores but this is unintialized!
what is a decent/elegant way to solve this when designing a C++ library?
how do well-designed C++ libraries like Boost, Qt, etc solve this? any idea?
What you can do - simply create a C wrapper interface for C++ I.e. something like following:
In header file, e.g. foo.h
#ifndef __FOO_H_INCLUDED__
#define __FOO_H_INCLUDED__
#ifdef __cplusplus
extern "C" {
#endif
void print_logical_cpus()
#ifdef __cplusplus
}
#endif / * extern "C" */
#endif / * __FOO_H_INCLUDED__ */
in the implementation file e.g. foo.cpp
#include "foo.h"
#include <cstdio>
#include <thread>
struct GlobalSettings {
GlobalSettings():
logical_cpus(std::thread::hardware_concurrency())
{}
std::size_t logical_cpus;
};
static GlobalSettings __gsettings;
extern "C" {
void print_logical_cpus()
{
std::printf("Logical cpus %z", __gsettings.logical_cpus);
}
}
If you still need to port on raw C, there are compiler specific tricks to run functions before and after main.
GCC uses attribute((constructor)) and attribute((destructor)))
MS VC++ uses __declspec(allocate(".CRT$XLC"))

C++ Visual Studio Windows: how to declare but not define extern function in dll

I have a project that is compiled into a library and declares a certain function to be implemented by the user of the library:
//To be defined by user
Application* CreateApplication();
When compiling the code into a shared library on Linux this works perfectly. Any user of the library can define an implementation for the declared function and it can be used inside the library. If the user of the library forgets to define an implementation, they will get an error pointing this out.
I'm now in the process of porting the library to Windows, where it is supposed to be compiled into a dll. However, I'm running into problems as the linker used by Visual Studio is complaining:
unresolved external symbol Application* __cdecl CreateApplication(void)
I tried adding the extern keyword to indicate that the definition of the function is somewhere else, but this didn't work.
Why can't I declare (but not define) a function in a dll like this? How should I fix my code so it works both on Linux and on Windows?
What you are attempting to do only works in a static library, it cannot work in a dynamic library like a DLL. For that, you will have to change the code to use a function pointer instead. The application that is using the DLL can pass in the address of the desired function from its own code, and the DLL can then assign that address to a variable that it uses as needed, eg:
HEADER:
#ifndef MYLIB_H
#ifndef MYLIB_H
#ifdef COMPILING_MY_LIB
#define MY_EXPORT __declspec(dllexport)
#else
#define MY_EXPORT __declspec(dllimport)
#endif
// declare Application as needed...
typedef Application (*lpCreateApplicationFunc)();
#ifdef __cplusplus
extern "C" {
#endif
MY_EXPORT void SetCreateApplicationFunc(lpCreateApplicationFunc func);
#ifdef __cplusplus
}
#endif
#endif
DLL:
#define COMPILING_MY_LIB
#include "MyLib.h"
//To be defined by user
lpCreateApplicationFunc CreateApplication = NULL;
void SetCreateApplicationFunc(lpCreateApplicationFunc func)
{
CreateApplication = func;
}
void doSomething()
{
Application *app = NULL;
if (CreateApplication)
app = (*CreateApplication)();
if (app)
{
...
}
}
EXE:
#include "MyLib.h"
Application MyCreateApplicationFunc()
{
...
}
// during startup, call this...
SetCreateApplicationFunc(&MyCreateApplicationFunc);

How to properly create a DLL from C code and use it in a C++ project

I have a set of functions written in C that I need to be able to call from another project written in C++. The C code is essentially some functions that do some calculations on a large data set. I didn't write them - all I want to do is allow my C++ project to be able to call those functions. My solution was to create a DLL for the C code and link it to my C++ project.
In order to make the DLL, I structured myCproj.h (the header in the C project, not C++ project) like so:
#ifdef __cplusplus
extern "C" {
#endif
struct __declspec(dllexport) neededStruct {
int a;
//I need to be able to initialize this struct in my C++ project.
}
__declspec(dllexport) void neededFunc( struct neededStruct *input ) {}
//I need to be able to call this function from my C++ project and feed
//it my local instance of neededStruct.
#ifdef __cplusplus
}
#endif
The src file, myCproj.c, was not changed at all. The function definitions do not have __declspec(dllexport)in front of them, nor is extern "C" inserted anywhere. The code compiles without error and produces myCproj.dll and myCproj.lib.
I then tell my C++ project in VS where to find myCproj.lib and myCproj.h accordingly and copy the DLL over to the directory where my C++ executable lives. To use the DLL, I gave myCPPproj.cpp the following addition:
#define DLLImport __declspec(dllimport)
struct DLLImport neededStruct input;
input.a = 0;
extern "C" DLLImport void neededFunc( &input );
However, I get error EO335 'linkage specification is not allowed' on that last line. What am I doing wrong?
It is preferable to use the same header for both the library and using code.
As mentioned, it is usually done by a conditional define, like the following:
MyLibrary.h:
#if defined(MYLIBRARY_API)
#define MYLIBRARY_EXPORTS __declspec(dllexport)
#else
#define MYLIBRARY_EXPORTS __declspec(dllimport)
#endif
#if defined(__cplusplus)
extern "C" {
#endif
MYLIBRARY_API bool MyLibFunc();
#if defined(__cplusplus)
#endif
MyLibrary.c:
#include "MyLibrary.h"
void MyLibFunc()
{
....
}
App.cpp:
#include <MyLibrary.h>
int main()
{
MyLibFunc();
}
The symbol MYLIBRARY_API will be defined for the library project (usually as a /D on the compiler command line). And if you use visual studio that is pretty much exactly what you get when creating a dll project with exports.

Load a C++ DLL into matlab which calls functions in another DLL

For learning purposes, I'm trying to load a DLL into MATLAB which calls functions defined in another DLL. I'm new to all of this, and haven't yet been able to figure out how I would go about doing this, and nor have I managed to find any relevant resources.
I wrote a small DLL in C++ which goes something like this:
//example_dll.h
#ifndef EXAMPLE_DLL_H
#define EXAMPLE_DLL_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef BUILDING_EXAMPLE_DLL
#define EXAMPLE_DLL __declspec(dllexport)
#else
#define EXAMPLE_DLL __declspec(dllimport)
#endif
int EXAMPLE_DLL Double(int x);
#ifdef __cplusplus
}
#endif
#endif // EXAMPLE_DLL_H
and the source file:
//example_dll.cpp
#include <stdio.h>
#include "example_dll.h"
int Double(int x)
{
return 2 * x;
}
This I built using MinGW w64 and loaded into matlab using loadlibrary('example_dll') without any problems.
I now want to define the function
int Double(int x)
{
return 2 * x;
}
In another DLL, (let's call it DLL2) and to call that function from my example_dll.
What would be the easiest way to do it?
I would appreciate a short example code (preferably for run-time dynamic linking, and without the use of module definition (.def) files) or a link to a relevant resource on the interwebs.
Thanks!
SOLUTION TO SIMPLE EXAMPLE:
I think I got the solution. It seems to be working anyway.
I created a DLL named interface_DLL which I loaded into MATLAB and from which I called my function in example_dll
here it is:
//interface_dll.h
#ifndef INTERFACE_DLL_H
#define INTERFACE_DLL_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef BUILDING_INTERFACE_DLL
#define INTERFACE_DLL __declspec(dllexport)
#else
#define INTERFACE_DLL __declspec(dllimport)
#endif
int INTERFACE_DLL Quadruple(int x);
#ifdef __cplusplus
}
#endif
#endif // INTERFACE_DLL_H
and the source file:
//interface_dll.cpp
#include <windows.h>
#include <stdio.h>
#include "interface_dll.h"
#include "example_dll.h"
int Quadruple(int x)
{
/* get handle to dll */
HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\Users\\uidr0605\\Documents\\ExampleDLL\\example_dll.dll");
/* get pointer to the function in the dll*/
FARPROC lpfnGetProcessID = GetProcAddress(HMODULE (hGetProcIDDLL),"Double");
/*
Define the Function in the DLL for reuse. This is just prototyping the dll's function.
A mock of it. Use "stdcall" for maximum compatibility.
*/
typedef int (__stdcall * pICFUNC)(int);
pICFUNC Double;
Double = pICFUNC(lpfnGetProcessID);
/* The actual call to the function contained in the dll */
int intMyReturnVal = Double(x);
intMyReturnVal = Double(intMyReturnVal);
/* Release the Dll */
FreeLibrary(hGetProcIDDLL);
/* The return val from the dll */
return intMyReturnVal;
}
I load it from MATLAB as follows:
%loadDLL.m
path = 'C:\Path\to\DLL\';
addpath(path);
loadlibrary('interface_dll')
i = 2;
x = calllib('interface_dll', 'Quadruple', i)
The reason I'm going through this process is that the MATLAB shared library interface supports C library routines only and not C++ classes.
My idea of a workaround is to use an intermediate DLL to act as an interface between MATLAB and the DLL who's classes I intend to access.
Is there a better way of doing this?
FURTHER QUESTIONS:
Can anyone please explain the significance of the line typedef int (__stdcall * pICFUNC)(int); as applied here?
What would I have to add or what changes would I have to make if I wanted to call a function in a class in example_dll?
EDIT: I added the following code to the example_dll header file:
class EXAMPLE_DLL MyClass
{
public:
int add2(int);
};
#ifdef __cplusplus
extern "C" {
#endif
MyClass EXAMPLE_DLL *createInstance(){
return new MyClass();
}
void EXAMPLE_DLL destroyInstance(MyClass *ptrMyClass){
delete ptrMyClass;
}
#ifdef __cplusplus
}
#endif
Further question 1
The following definition
typedef int (__stdcall * pICFUNC)(int);
defines a new type pICFUNC which is a pointer to a function which takes an int and returns an int. Also, the function must be called according to the __stdcall calling convention, which specifies how arguments must be passed and how to retrieve the return value.
This link explains typedef with function pointers. Have a look also at the following section, Using typedef with type casts, since on line
Double = pICFUNC(lpfnGetProcessID);
pICFUNC is used to cast instead.
Further question 2
The following is a very trivial example to give an idea. If you have a class in example_dll called MyClass which has a method
int add(int num);
you could implement the following functions:
MyClass *createInstance(){
return new MyClass();
}
void destroyInstance(MyClass *ptrMyClass){
delete ptrMyClass;
}
These function need to be extern "C" and you could import them with GetProcAddress. Then, it would be just a matter of creating an instance, calling its methods through the pointer and eventually destroying it.
EDIT: Some hints for the implementation
Import the function to create the instance
FARPROC lpfnCreateInstance = GetProcAddress(HMODULE (hGetProcIDDLL), "createInstance");
Declare a proper pointer type for the function (returns a MyClass*, no arguments)
typedef MyClass* (__stdcall * pCREATINST)();
Cast lpfnCreateInstance
pCREATINST createInstance;
createInstance = pCREATINST(lpfnCreateInstance)
Create your instance
MyClass *myInstance = creatInstance();
Then you don't need a wrapper for add, you can just call it from your pointer.
int res = myInstance->add(123);
You should do the same for destroyInstance, being careful with the types
Please note that I can't test this code, but it should be the right approach.

Can I mix JNI headers implementation with normal C++ classes?

If I try to implement my class on this file I get an error UnsatisfiedLinkError, however if I remove the implementation of the Broker.h Class it goes ok. Why?
Broker.h
#include "XletTable.h"
#ifndef BROKER_H_
#define BROKER_H_
class Broker {
private:
static Broker* brokerSingleton;
static XletTable *table;
// Private constructor for singleton
Broker(JNIEnv *, XletTable *);
// Get XletTable (Hash Table) that contains the...
static XletTable* getTable();
public:
virtual ~Broker();
static Broker* getInstance(JNIEnv *);
jobject callMethod(JNIEnv *, jclass, jstring, jobject, jbyteArray);
};
#endif /* BROKER_H_ */
BrokerJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Broker */
#ifndef _Included_Broker
#define _Included_Broker
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Broker
* Method: callMethod
* Signature: (Ljava/lang/String;Ljava/lang/reflect/Method;[B)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_Broker_callMethod
(JNIEnv *, jclass, jstring, jobject, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif
Probably your library miss reference to some symbol, or another library. Try make some main.cpp with empty main() function, and link it with your library - g++ main.cpp -o main -lInterAppCC. If you miss something, the linker will give you a detailed error message.
PS. Since your header file already wraps function prototype with extern "C", you don't required to do the same when writing implementation.
You need to use extern "C" around the JNIEXPORT stuff, to avoid c++ name mangling of the JNI functions.
C++ name mangling changes function names (in the obj-files) to include the types of parameters, virtual-ness, etc, to be able to link different overloaded functions with the same name.
So, wrap your JNIEXPORT with extern "C" { ... } (look at the JNI header) and make sure your c++-code isn't wrapped in the same.