I'm new in c++ and jni. I need to return java exception as jobject, not jint. I have a method which returns jobject. So I get compilation error when trying to return ThrowNew:
JNIEXPORT jobject JNICALL Java_com_my_get(JNIEnv* env, jobject obj) {
some::SomeObject returnObject;
try {
//... trying to get returnObject by calling lib method
returnObject = some::lib::getSomeObject();
} catch (...) {
// report problem back to Java.
jclass Exception = env->FindClass("com/my/MyClientException");
return env->ThrowNew(Exception, "Error");
}
//... return returnObject mapped to jobject
}
java native part:
public native MyJavaObject get(); // need object or exception
Compilation error:
error: invalid conversion from ‘jint’ {aka ‘int’} to ‘jobject’ {aka
‘_jobject*’} [-fpermissive]
How to do this? I need to stop the method when an error occurs.
Don't try to return the result of ThrowNew back to Java. Its return value only tells you whether the call succeeded. The documentation for ThrowNew states:
RETURNS:
Returns 0 on success; a negative value on failure.
Calling ThrowNew will cause control flow in the JVM to go to an exception handler upon return from the native method. Return any value you want after calling it; the value is ignored. For methods with a return type of jobject, simply return nullptr.
env->ThrowNew(Exception, "Error");
return nullptr;
Related
I tried the following code to convert QString to jstring according to PaulMcKenzie's answer.
jstring CreateJStringFromQString(JNIEnv *env, const QString& str)
{
jstring js = env->NewString(reinterpret_cast<jchar *>(str.data()), str.size());
return js;
}
But , I have the following error
/home/runner/work/jni/jni/hello/src/main/cpp/HelloJNIImpl.cpp:46:69:
error: reinterpret_cast from type ‘const QChar*’ to type ‘jchar*’
{aka
‘short unsigned int*’} casts away qualifiers
46 | jstring js = env->NewString(reinterpret_cast<jchar *>
(str.data()), str.size());
^
FAILURE: Build failed with an exception.
I tried that answer . But it comes the above error.
How to convert QString to jstring ?
I don't use Qt, so I am going by the documentation here with regards to QString.
Given that, you can try the following:
jstring CreateJStringFromQString(JNIEnv *env, const QString& str)
{
jclass string_class = env->FindClass("java/lang/String");
jmethodID string_constructor = env->GetMethodID(string_class, "<init>", "(Ljava/lang/String;)V" );
jstring js = env->NewObject(string_class, string_constructor, env->NewString(str.data(), str.size()));
return js;
}
I use code similar to the above in the JNI interfaces I've written.
There is no error checking to see if the string_class and string_constructor are valid, but assume they are.
Basically you have to construct a java String object, and populate it with the data.
Since Qt 6.1 you can use QJniObject
If you need only temporary jstring e.g. to pass argumment to jni function call, then you can use such a conversion:
QJniObject::fromString(your_QString).object<jstring>()
But as mentioned in Qt documentation you need to be aware of string lifetime being bound to scope of owning QJniObject.
This is very similar to:
Turning GetLastError() into an exception
I also want to be able to add an std::string to the error:
class Win32Exception : public std::system_error
{
public:
Win32Exception(std::string ErrorDesc) : std::system_error(GetLastError(), std::system_category(), ErrorDesc)
{
}
};
The problem is that at least in DEBUG builds of VS2015 the construction of std::string resets GetLastError(). So by the time the Win32Exception calls GetLastError() it always gets zero/no error.
I could use const char*, but I also want to use std::wstring, which means I need to convert it to std::string or const char* (because the std::system_error expects std::string or const char*), and that leads me back to the same problem that the error gets reset.
Is there some elegant solution on how to throw Win32 errors easily, having the exception class capture GetLastError() and being able to add arbitrary info with std::string's?
You need to rewrite exception class constructor so it will take error code as parameter so error code:
Win32Exception
(
char const * const psz_description
, ::DWORD const error_code = ::GetLastError()
)
: std::system_error(error_code, std::system_category(), psz_description)
{
}
I create a Java object by calling native code which return a jobject value.
Java code:
Object myObj = nativeCreateObject();
Native code:
jobject* hold_ref;
JNIEXPORT jobject JNICALL
nativeCreateObject(JNIEnv *env ...) {
.....
result = env->NewGlobalRef(jobj);
hold_ref = &result;
return result;
}
my question is: whether I can use hold_ref later to release the myObj by reference in native layer?
e.g.
native code:
*hold_ref = NULL;
Then the myObj in Java layer is null?
if not, how can I release this object by native code?
I am not sure, what you want to achieve, but here is how it basicly works:
whether I can use hold_ref later to release the myObj by reference in native layer?
Yes, you can and should use env->DeleteGlobalRef(myObj) to free the global reference you have created so the garbage collector can clean up and finaly destroy the object.
Then the myObj in Java layer is null? if not, how can I release this object by native code?
There is no way your Java-variable will turn null magicaly when you delete the reference from jni native code. Java itself holds a reference to prevent the object is removed by the garbage collection.
You may want to use it this way:
C++
jobject* hold_ref;
JNIEXPORT jobject JNICALL nativeCreateObject(JNIEnv *env ...) {
.....
result = env->NewGlobalRef(jobj);
hold_ref = &result;
return result;
}
JNIEXPORT void JNICALL nativeDestroyObject(JNIEnv *env ...) {
.....
env->DeleteGlobalRef(jobj);
hold_ref = nullptr;
}
JAVA
// Creates two references (native and local variable)
Object myObj = nativeCreateObject();
// Deletes the native reference, but not the local one
nativeDeleteObject(myObj);
// myObj != null
myObj = null;
// now there is no reference to your created object
// the garbage collector may destroy it any time
If you want to invalidate your object in someway I suggest to manage the state and throw an exception, if the object was invalidated like this:
class MyInvalidateableObject {
private boolean invalidated = false;
public void invalidate() {
this.invalidated = true;
}
public void foo() {
if (invalidated)
throw new IllegalStateException("Object has been invalidated");
... // do the normal stuff
}
}
Simply call invalidate() on your object from native code to prevent it from being used anymore.
I'm trying to use C++ to wrap some simple C++ and encountered an issue when trying to wrap even the most basic stuff.
SWIG doesn't seem to try and catch and exception that might occurr inside a typemap.
For example for the following SIWG interface file:
%module badswig
std::string func();
SWIG Generates this:
SWIGINTERN PyObject *_wrap_func(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
PyObject *resultobj = 0;
std::string result;
if (!PyArg_ParseTuple(args,(char *)":func")) SWIG_fail;
result = func();
resultobj = SWIG_NewPointerObj((new std::string(static_cast< const std::string& >(result))), SWIGTYPE_p_std__string, SWIG_POINTER_OWN | 0 );
return resultobj;
fail:
return NULL;
}
Notice that new std::string is unprotected at all so that if std::bad_alloc will be thrown it will propogate to Python and crash.
There are likely other cases as well not only SWIG using new.
There is %exception which handles this for the wrapped function (If func() throws) but I couldn't find anything to handle this for typemaps, especially for ones supplied by SWIG.
Am I missing something? is there someway to correctly handle this?
EDIT:
In response to an answer and to clarify my question, %exception does not achive what I want:
%module badswig
%exception %{
try {
$action
} catch (const std::bad_alloc&) {
PyErr_NoMemory();
SWIG_fail;
}
%}
std::string func();
Generates this:
SWIGINTERN PyObject *_wrap_func(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
PyObject *resultobj = 0;
std::string result;
if (!PyArg_ParseTuple(args,(char *)":func")) SWIG_fail;
try {
result = func();
} catch (const std::bad_alloc&) {
PyErr_NoMemory();
SWIG_fail;
}
resultobj = SWIG_NewPointerObj((new std::string(static_cast< const std::string& >(result))), SWIGTYPE_p_std__string, SWIG_POINTER_OWN | 0 );
return resultobj;
fail:
return NULL;
}
Notice how new std::string has no try catch around it.
The typemap just tells SWIG what interface code to generate to convert call parameters and return value of the function. The %exception generates code immediately before and after the function to be wrapped, to transition exceptions from C++ to target language or to convert the C++ exception to the target language equivalent. If you use %exception as per the SWIG docs, you should find that your wrapper function contains the typemap code you specified for that function, and the exception code around your C++ function:
void swig_function(PyObj*) {
typemap-related code to convert args from Python to C++
exception-related code
your-c++-func(args)
exception-related code (catch clauses etc)
typemap-related code to convert return value from C++ to Python
}
I have a c++ class called androidGPS with public methods and variables. Inside this class I am using JNI to calls some java code. But I am stuck in the registerNatives function.
My c++ class has this void method:
void LocationChanged(JNIEnv* env, jobject object, jstring paramsString);
Implemented in this way:
void androidGPS::LocationChanged(JNIEnv*, jobject, jstring paramsString)
{
const char * nativeString = currEnv->GetStringUTFChars(paramsString, 0);
QString qstring(nativeString);
QStringList res;
res << qstring;
emit newReadAvailable(res);
}
The Java class that I try to link to has the following method:
public static native void sndonLocationChanged(String currLocation);
I am declaring the JNINativeMethods as a local variable before calling the register natives like:
JNINativeMethod methods[] =
{
{
"sndonLocationChanged",
"(Ljava/lang/String;)V",
(void *)&androidGPS::LocationChanged
}
};
The register natives call is this:
int numMethods;
numMethods = sizeof(methods) / sizeof(methods[0]);
if (currEnv->RegisterNatives(listenerClass, methods, numMethods) < 0)
{
if (currEnv->ExceptionOccurred())
{
classError = true;
emit error("JNI--Error running RegisterNatives");
stopGPSService();
return false; //Return with error
}
else
{
emit error("JNI--Error running RegisterNatives");
stopGPSService();
return false; //Return with error
}
}
The compiler gives me the warning:
warning: converting from 'void
(androidGPS::)(JNIEnv, _jobject*,
_jstring*)' to 'void*'
The c++ application crashes when I call the function sndonLocationChanged() in the class through JNI. I cannot see the error in the c++, but the Dalvik does not generate an error.
I guess is because I am not calling properly registerNatives or not declaring properly the methods (hence the warning).
If I declare LocationChanged static as:
static jboolean LocationChanged(JNIEnv* env, jobject object, jstring paramsString);
It does not fail but I cannot pass paramsString into class members!
Any idea how can I fix this?
Many thanks,
Carlos.
One option is that you give your Java code a means of getting a pointer to your native androidGPS object and passing it down to the non-class-function LocationChanged(), and it calls androidGPS::LocationChanged().
Callbacks into non-static C++ objects are problematic and typically are solved using a static helper function coupled with either a pointer to the object that the callback should be invoked on, or use of a static pointer to a singleton object.