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

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.

Related

Use JNI to call graalvm native build shared library

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?

How to wrap c++ std::shared_ptr in wrapper headerfile so it can be called from c?

I'm writing a small Wrapper API so i can call some C++ code (classes/ functions) from C.
I got the problem, that one of my C++ functions is initialised in my wrapper header with a "shared_ptr".
ClassName *ClassName _new(std::shared_ptr<Lib::Instance> p_Instance);
So as you can see, the wrapper file is infested with C++ style. This is bad because the Wrapper file should be readable by C AND C++.
This is my Wrapper.h file:
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct ClassName ClassName;
ClassName *ClassName_new(std::shared_ptr<Lib::Instance> p_Instance);
void ClassName_setValue(ClassName* t, double p_value);
void ClassName_delete(ClassName* t);
#ifdef __cplusplus
}
#endif /* __cplusplus */
and this is my Wrapper.cpp file:
#include "Wrapper.h"
#include "ClassName.h"
extern "C"{
ClassName* ClassName_new(std::shared_ptr<Lib::Instance> p_Instance){
return new ClassName(p_Instance);
}
void ClassName_setValue(ClassName* t, double p_value){
t->setValue(p_value);
}
void ClassName_delete(ClassName* t){
delete t;
}
}
And this is the part of my main .cpp file
Header:
class ClassName: public Lib::Util::Task {
public:
ClassName(std::shared_ptr<Lib::Instance> p_Instance);
virtual ~ClassName();
void setValue(double p_value);
...
.Cpp:
ClassName::ClassName(std::shared_ptr<Lib::Instance> p_Instance) ...
...
void ClassName::setValue(double p_value){
doSomething()
}
...
I'm not allowed to change my structure of the main c++ file where I am using the ClassName(std::shared_ptr<Lib::Instance> p_Instance);
Do you have any ideas how I can fix this problem?
Maybe writing a second Wrapper?
Edit:
Here is the error given by the Terminal:
Wrapper.h:21:45: error: expected ‘)’ before ‘:’ token
ClassName *ClassName_new(std::shared_ptr<Lib::Instance> p_Instance);
^
The function cannot be used in C, so you can use the pre-processor to remove the declaration just like you used with extern "C":
// Wrapper.h
#ifdef __cplusplus
ClassName *ClassName_new(std::shared_ptr<Lib::Instance> p_Instance);
#endif
Although, it this wrapper isn't supposed to be used in C++ by anything other than Wrapper.cpp, then moving the declaration into Wrapper.cpp might be a better option.
If you need a way of invoking ClassName_new from C, I would suggest steering away from shared pointer. But you could make it work with an opaque wrapper:
// Wrapper.h
struct opaque_wrapper* make_instance(void);
void release_instance(struct opaque_wrapper*);
ClassName *ClassName_new(struct opaque_wrapper*);
// Wrapper.cpp
struct opaque_wrapper {
std::shared_ptr<Lib::Instance> p_Instance;
};
opaque_wrapper* make_instance() {
return new opaque_wrapper{std::make_shared<Lib::Instance>()};
}
void release_instance(struct opaque_wrapper* instance) {
delete instance;
}
ClassName *ClassName_new(struct opaque_wrapper* instance) {
return ClassName_new(instance->p_Instance);
}
I would suggest embedding the reference counter into the object and use boost::intrusive_ptr with it. This way you can pass a plain pointer to C functions and those C functions can still manage the object's lifetime directly calling C-style addref/release on it.
You cannot use classes from C++ in C.
You can define function prototypes of C calling convention C functions, to be used from C++ with the following wrapper code around in the header file:
#ifdef __cplusplus
extern "C" {
#endif
/* put here C calling convention C functions */
#ifdef __cplusplus
};
#endif
and you'll get a definitions file that can be used either in C or in C++. __cplusplus is a macro defined by the c++ compiler when compiling C++ code, and it introduces the extern "C" { environment to support C functions. Those functions follow the C style calling conventions (you cannot overload them, you cannot use method definitions or define classes in there)

How to fix "UnsatisfiedLinkError: No implementation found" error using jni and javah

I want to use C++ method inside my Java code. So I decided to use JNI. But the link seams to not work properly, du to my error at the execution No implementation found for void com.me.Native.helloWorld() (tried Java_com_me_Native_helloWorld and Java_com_me_Native_helloWorld__)
Native.java (called elsewhere as Native.helloWorld()):
package com.me;
public class Native{
static {
System.loadLibrary("detection_based_tracker");
}
public static native void helloWorld();
}
Android.mk :
...
LOCAL_SRC_FILES += com_me_Native.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_MODULE := detection_based_tracker
include $(BUILD_SHARED_LIBRARY)
com_me_Native.h (generated with javah command):
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_me_Native */
#ifndef _Included_com_me_Native
#define _Included_com_me_Native
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_me_Native
* Method: helloWorld
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_me_Native_helloWorld
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
com_me_Native.cpp :
#include <com_me_Native.h>
#include <iostream>
#include <android/log.h>
#define LOG_TAG "HelloWorld"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#ifdef __cplusplus
extern "C" {
#endif
using namespace std;
/*
* Class: com_me_Native
* Method: helloWorld
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_me_Native_helloWorld
(JNIEnv *, jclass)
{
LOGD("Hello from c++");
}
#ifdef __cplusplus
}
#endif
As you see a use JNIEXPORT and JNICALL on my method. I also use extern "C" for C++ use. My .h was generated by javah. I checked the Android.mk and I didn't forgot to add my .cpp file to LOCAL_SRC_FILES. I statically loaded my library in the Native.java to use my static function.
Now I don't know where the error may come from... Any idea ?!
The include guard should only be in the .h file, not in the .cpp file.
So in your .cpp file, remove these lines:
#ifndef _Included_com_me_Native
#define _Included_com_me_Native
As well as the final #endif.
What happens with your curent code is that _Included_com_me_Native gets defined when you include your header file, so then the #ifndef _Included_com_me_Native in the .cpp file will be false, and none of that code gets compiled.

Using a mixed C/C++ header to use C++ object in C

I have been following this guide on how to call a member function of a C++ object from C. As I've understood it, the C code should interpret the class as a struct of the same name, and whenever it wants to call a function through an object of this class it should use an intermediate callback function. The header looks like this:
// CInterface.h
#ifdef __cplusplus
...
class CInterface
{
public:
...
void OnMessage(U8* bytes); // I want to call this function from C.
private:
...
};
#else
typedef
struct CInterface
CInterface;
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__STDC__) || defined(__cplusplus)
//extern void c_function(CInterface*); /* ANSI C prototypes (shouldn't be needed) */
extern CInterface* cpp_callback_function(CInterface* self, unsigned char * bytes);
#else
//extern void c_function(); /* K&R style (shouldn't be needed) */
extern CInterface* cpp_callback_function(unsigned char * bytes);
#endif
#ifdef __cplusplus
}
#endif
The C code that fails right now looks like this:
// main.c
#include "CInterface.h"
int main(int argc, char* argv[])
{
void* ptr;
int *i = ptr; // Code that only compiles with a C compiler
CInterface cinterface; // This should declare a struct
}
The error is: error C2079: 'cinterface' uses undefined struct 'CInterface'.
It sounds like the header is being read as c++ code as the struct is not defined, but main.c is being compiled by C according to Visual Studio (I also double checked this by adding some C-specific code). However, if I add parentheses like this:
CInterface cinterface();
the code compiles which makes no sense to me as it now is an object which shouldn't work in C.
The callback function is implemented in a third file, CInterface.cpp, which acts as the "intermediate".
So the question is how I solve this error message, or if I got the entire approach wrong. It's the first time I mix C/C++ code and I'm relatively new to both languages.
In your example CInterface is only defined for C++. If you take a closer look at the example you linked you'll notice that this is also the case for the Fred class.
From C you can only pass around pointers to CInterface and you have to rely on C++ functions defined with C linkage to actually manipulate CInterface instances.
Otherwise you could define a struct as a means to pass around data between C and C++. Just ensure its definition is declared as extern "C" when used from C++:
#ifdef __cplusplus
extern "C" {
#endif
struct CandCPlusPlus {
// ...
};
#ifdef __cplusplus
}
#endif

What is the purpose of _GLOBAL__I_?

I have two functions declared as following, using extern "C" aming to avoid name mangling.
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jobject JNICALL Java_com_trident_tv_si_SIManagerImpl_nGetServiceDetails
(JNIEnv *, jobject, jint);
JNIEXPORT jobject JNICALL Java_com_trident_tv_si_SIManagerImpl_nGetServiceCurrentEvent
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
Surprisingly, the second function still have a mangled name _GLOBAL__I_Java_com_trident_tv_si_SIManagerImpl_nGetServiceCurrentEvent , I was wondering what is the purpose of it and why the first function did not generate one?
00004d58 T Java_com_trident_tv_si_SIManagerImpl_nGetServiceCurrentEvent
0000533a T Java_com_trident_tv_si_SIManagerImpl_nGetServiceDetails
0000494f t _GLOBAL__I_Java_com_trident_tv_si_SIManagerImpl_nGetServiceCurrentEvent
EDIT:
Find something here. However, no clear answer yet.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12293
It looks to me like the two functions have the correct names ( the two preceded by T ), and that a third symbol (preceded by t) is created for gccs internal use.
They have been reordered though.
// SECOND FUNCTION, T = exported and in TEXT section
00004d58 T Java_com_trident_tv_si_SIManagerImpl_nGetServiceCurrentEvent
// FIRST FUNCTION, T = exported and in TEXT section
0000533a T Java_com_trident_tv_si_SIManagerImpl_nGetServiceDetails
// INTERNAL symbol, t = non-exported symbol in TEXT section
0000494f t _GLOBAL__I_Java_com_trident_tv_si_SIManagerImpl_nGetS