Setting a temporary environment variable for Batch from C++ - 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.

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 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);

How can I return vector<vector<float>> from JNI?

I have a function in C++:
std::vector<std::vector<float>> const &GetVertices() { return m_Vertices; }
I need to return this value to Java through JNI.
So, because of the fact that I need to return vector of vector, I think that I have to use jobjectArray, like this:
extern "C" JNIEXPORT jobjectArray JNICALL
Java_com_google_ar_core_examples_
java_helloar_HelloArActivity_fillListWithData(
JNIEnv *env,
jobject /* this */
)
In Java, I have this method:
public native Object[] fillListWithData();
So, my question is, how to convert vector<vector<float>> to jobjectArray?
I know that there is a method that could create jobjectArray:
jobjectArray verticesArr = env->NewObjectArray(verticesVec.size(), WHAT CLASS SHOULD BE HERE?,NULL);
Then how can I put in the values ?
Full class implementation
extern "C" JNIEXPORT jobjectArray JNICALL
Java_com_google_ar_core_examples_java_
helloar_HelloArActivity_fillListWithData(
JNIEnv *env,
jobject /* this */
) {
//verticesVec
vector<vector<float>> verticesVec = initializer->GetVertices(); // THIS VECTOR I NEED TO CONVERT TO JOBJECTARRAY
jobjectArray verticesArr = env->NewObjectArray(verticesVec.size(), WHAT CLASS SHOULD BE HERE?,NULL);
//HOW TO FILL THE ARRAY HERE??
return verticesArr;
}
You have to play with java.util.Vector. This way, you can make a very simple mapping of
(C++ side) vector<vector<float> > ---> Vector<Vector<Float>> (Java side)
The code itself will be little bit ugly. Remember that playing with JNI is not quite pleasant experience (due to esoteric syntax).
Anyway, what you want to do is to create all the stuff on C++ side and pass it back to Java.
Just an excerpt
vector<vector<float> > vect {
{ 1.1, 1.2, 1.3 },
{ 2.1, 2.2, 2.3 },
{ 3.1, 3.2, 3.3 }
};
...
...
jclass vectorClass = env->FindClass("java/util/Vector");
...
jclass floatClass = env->FindClass("java/lang/Float");
...
jmethodID mid = env->GetMethodID(vectorClass, "<init>", "()V");
jmethodID addMethodID = env->GetMethodID(vectorClass, "add", "(Ljava/lang/Object;)Z");
// Outer vector
jobject outerVector = env->NewObject(vectorClass, mid);
...
for(vector<float> i : vect) {
// Inner vector
jobject innerVector = env->NewObject(vectorClass, mid);
for(float f : i) {
jmethodID floatConstructorID = env->GetMethodID(floatClass, "<init>", "(F)V");
...
// Now, we have object created by Float(f)
jobject floatValue = env->NewObject(floatClass, floatConstructorID, f);
...
env->CallBooleanMethod(innerVector, addMethodID, floatValue);
}
env->CallBooleanMethod(outerVector, addMethodID, innerVector);
}
env->DeleteLocalRef(vectorClass);
env->DeleteLocalRef(floatClass);
You can find full sample code here:
https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo045
Once you run the test, you can see that C++ based data was passed to Java
> make test
/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/bin/java -Djava.library.path=:./lib -cp target recipeNo045.VectorOfVectors
library: :./lib
[1.1,1.2,1.3]
[2.1,2.2,2.3]
[3.1,3.2,3.3]
Depends a bit on how you want to operate on the data in Java, e.g. add more elements? You didn't mention this so the answer can't be more specific. But perhaps you want "java/util/ArrayList" of float.
The following answer should point you in the right direction:
return a jobjectArray which contains lists of data.
The code would look similar for other types of lists. A bit of web search will provide several solutions.
In case speed (CPU/latency) and data volume are not important, instead of all the messy handling of Java datastructures I would generally recommend serializing your data to JSON and return as string. Using e.g. JsonCpp it's easy to serialize C++ data types including objects to JSON, and on the Java side there are several packages for deserialization (web search again) and many Java frameworks come with builtin tools for this.

JNI / Android NDK - Maintaining a global object reference

I know I can't keep a reference to the internals of an array so I was wondering if it is OK to keep a global pointer to a java array object or indeed any java object. And whether it makes any difference that I create it from the C++.
It works, but I was worried the garbage collector could potentially relocate the memory (which I understand it the reason for Get... and Release... methods on JNIEnv).
//global jfloatArray
jfloatArray jarray;
//called once
JNIEXPORT void Java_com_example_test1_Main_Init
(JNIEnv *env, jclass thiz){
//create once
jarray = env->NewFloatArray(10); //if valid, would it be as valid to pass it in?
}
//called repeatedly
JNIEXPORT void JNICALL
Java_com_example_test1_Main_loop(JNIEnv* env, jobject thiz) {
//use jarray in here
}
Edit:
Here is the correct code.
//global jfloatArray
jfloatArray jarray;
//called once
JNIEXPORT void Java_com_example_test1_Main_Init
(JNIEnv *env, jclass thiz){
//create once
//create it - this gives a local reference
jfloatArray local_jarray = env->NewFloatArray(10);
//get a global reference, cast it and set to the global "jarray"
jarray = (jfloatArray) env->NewGlobalRef(local_jarray);
//delete the local reference
env->DeleteLocalRef(local_jarray);
}
//called repeatedly
JNIEXPORT void JNICALL
Java_com_example_test1_Main_loop(JNIEnv* env, jobject thiz) {
//use jarray in here
}
Your reference is merely that -- a reference. It will not prevent the object it refers to from being relocated. It will prevent the object from being recollected; local references are automatically destroyed after returning, but since you're using a global variable, you should use a global reference, which necessitates manual management. See NewGlobalRef and DeleteGlobalRef.

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.