How to convert QString to jstring? - c++

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.

Related

ReleaseStringUTFChars with non-original jstring argument?

In order to minimize JNI marshalling, I want to store some strings on the C++ side as static variables via a setup method, use them in a different JNI method call rather than passing them each time, and then release the strings later with yet another JNI method call. For example,
C++ code:
static const char *stringValue1;
extern "C" JNIEXPORT void JNICALL
Java_mypackage_myclass_setValue1(JNIEnv* env, jclass jobj, jstring javaString) {
jboolean isCopy;
if(!env->IsSameObject(javaString, NULL)) {
const char *stringValue1= env->GetStringUTFChars(javaString, &isCopy);
}
}
extern "C" JNIEXPORT void JNICALL
Java_mypackage_myclass_execute(JNIEnv* env, jclass jobj, jint javaInt) {
// ... use the stringValue1 static variable - otherwise, this method would need another jstring argument.
}
extern "C" JNIEXPORT void JNICALL
Java_mypackage_myclass_releaseValue1(JNIEnv* env, jclass jobj, jstring javaString) {
if(stringValue1 != 0) {
env->ReleaseStringUTFChars(javaString, stringValue1);
}
}
Java code:
myclass.setValue1("hello")
for(int i = 0; i < 10; i++) {
myclass.execute(i); // execute needs the "hello" string, but we try to store it in the C++ side rather than pass it in each loop iteration.
}
myclass.releaseValue1("hello");
Is this a valid JNI pattern? In all of the JNI examples I have seen, both calls to GetStringUTFChars and ReleaseStringUTFChars occur in the same method/scope. In this example, they do not occur in the same scope, and the jstring argument could possibly not be the same Java String either.
Yes, the documentation states
This array is valid until it is released by ReleaseStringUTFChars().
This is corroborated by the implementation in Hotspot, which just allocates off-heap memory for a copy.

how to throw java exception as jobject in JNI

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;

How to obtain the name of a Java class from its corresponding jclass?

I have a jclass and I need to find out the name of the corresponding Java class. There is a well-received answer on SO for a similar question, however, it requires an object that's an instance of this class: https://stackoverflow.com/a/12730789/634821
I don't always have an instance where having the name of a jclass would be very useful, and I find it hard to believe that Java has no way to find a class's name, which is obviously a static property, with only static methods. So, is it doable, and how?
A jclass is a jobject subclass that references a java Class object.
So, cut the first few lines off the answer you found:
jclass input = ...; // Class<T>
jclass cls_Class = env->GetObjectClass(input); // Class<Class>
// Find the getName() method on the class object
mid = env->GetMethodID(cls_Class, "getName", "()Ljava/lang/String;");
// Call the getName() to get a jstring object back
jstring strObj = (jstring)env->CallObjectMethod(input, mid);
// Now get the c string from the java jstring object
const char* str = env->GetStringUTFChars(strObj, NULL);
// Print the class name
printf("\nCalling class is: %s\n", str);
// Release the memory pinned char array
env->ReleaseStringUTFChars(strObj, str);

Setting a temporary environment variable for Batch from C++

I am implementing a small pack of programs for batch users to use.
Almost all things in this pack is made in C++ and is called from Java.
How would I set a environment variable for the Batch file to use?
I have tried using this:
JNIEXPORT void JNICALL METHOD_NAME(JNIEnv *env, jclass theclass, jstring key, jstring value) {
const char* thekey = env->GetStringUTFChars(key, false);
const char* thevalue = env->GetStringUTFChars(value, false);
std::string envvar;
envvar.append(thekey);
envvar.append("=");
envvar.append(thevalue);
_putenv(envvar.c_str());
env->ReleaseStringUTFChars(key, thekey);
env->ReleaseStringUTFChars(value, thevalue);
}
However the Batch file did not see any new variable.
Should I use system("set thing=value");?
After some research I came to the conclusion that a child process cannot modify the parent process' environment.

What is obj in MonitorEnter?

I am not sure what obj is in MonitorEnter JNI function. Is it obj passed as parameter in native function or shared variable which I want to synchronize?
I have a variable called buffer which is shared by two threads.
This is my code.
JNIEXPORT void JNICALL Java_company_com_HelloActivity_setBuffer(JNIEnv *env, jobject obj, jstring jstr)
{
char buf[256];
int len = (*env)->GetStringLength(env, jstr);
(*env)->GetStringUTFRegion(env, jstr, 0, len, buf);
(*env)->MonitorEnter(env, obj); // I don't think this is correct.
strcat(buffer, buf); // buffer is declared as global char buffer[256];
(*env)->MonitorExit(env, obj);
}
EDIT:
How about this? syncobj is defined in Activity as static Object and shared with another thread.
JNIEXPORT void JNICALL Java_company_com_HelloActivity_setBuffer(JNIEnv *env, jobject obj, jstring jstr, jobject syncobj)
{
char buf[256];
int len = (*env)->GetStringLength(env, jstr);
(*env)->GetStringUTFRegion(env, jstr, 0, len, buf);
(*env)->MonitorEnter(env, syncobj);
strcat(buffer, buf);
(*env)->MonitorExit(env, syncobj);
}
It is exactly the same as in this Java code:
synchronized (syncobj) // = MonitorEnter(env, syncobj)
{
// ...
} // = MonitorExit(env, syncobj)
Your first snippet is an equivalent of synchronized(this), the second snippet is synchronized(syncobj). But both snippets make sense only if the other thread accessing your buffer can see either this or syncobj. I am afraid that the other thread is a native one. Why else you would create the buffer in native code then? If i am correct, then MonitorEnter/Exit is unnecessarily arcane - you could use it but the other native thread would need to hold a global reference to this or syncobj. Native locking seems like much cleaner solution. You are locking native resource, not JVM resource.