user193272
user193272

Reputation: 984

how to modify the value of a jstring passed to a C++ routine using Java and JNI?

Can I pass a string from Java to my C++ routine using JNI function calls and modify its value in the C++ routine?

So far, I have seen examples of returning jstring, which I do not want to do. The other option that I know about is to get the ID of the string variable within C++ and set its value.

At present, I am playing with a function like the following:

JNIEXPORT void JNICALL Java_myexample_ChangeString
(JNIEnv *, jobject obj, jstring strJava) 

And I want to change strJava's value. So, essentially what I am asking is if it Java can pass variables by reference and not just by value.

Thanks.

Upvotes: 3

Views: 3418

Answers (3)

bleater
bleater

Reputation: 5499

You can't directly modify a String parameter, as Java Strings are immutable. What you can do however is place the string in an array, pass the array to the native code, and then in the native code (using some JNI interop methods) create a new Java String and replace the reference in the array with a reference to the new string:

Caller.java:

package com.mycompany.myapp;

// Prototype for native method.
class MyJNI {
    public static native void changeStringsNative(String[] stringArr);
}

void CallNative()
{
    String[] myStringArr = new String[1] {"Original"};
    System.out.println("Original (Java): " + myStringArr[0]);
    MyJNI.changeStringsNative(myStringArr);
    System.out.println("Changed (Java): " + myStringArr[0]);
}

Callee.c

#include <jni.h>
#include <stdio.h>

// Helper macro so we only need to declare class name once.
#define JNIFUNCTION(sig) Java_com_mycompany_myapp_MyJNI##sig

JNIEXPORT void JNICALL JNIFUNCTION(changeStringsNative(JNIEnv *env, jobject obj,
    jobjectArray myStringArr))
{
    int stringCount = env->GetArrayLength(myStringArr);
    for (int i = 0; i < stringCount; i++) {
        jstring myString = (jstring)(env->GetObjectArrayElement(myStringArr, i));
        const char *myStringOriginalC = env->GetStringUTFChars(myString, NULL);
        printtf("Original (native): %s\n", myStringOriginalC);
        env->ReleaseStringUTFChars(myString, myStringOriginalC);
        
        // Now do something in native to transform the string. For example, a printf involving the original.
        char *myStringChangedC = NULL;
        if (asprintf(&myStringChangedC, "Different from the %s!\n", myStringOriginalC) == -1) {
            // asprintf malloc error.
            return;
        }
        printtf("Changed (native): %s\n", myChangedStringC);

        // Create a new jstring and put into the array.
        env->SetObjectArrayElement(myStringArr, i, env->NewStringUTF(myChangedStringC));

        free(myStringChangedC); // must free after asprintf's malloc.
    }
    return true;
}

Output to stdout:

Original (Java): Original
Original (native): Original
Changed (native): Different from the Original!
Changed (Java): Different from the Original!

Upvotes: 0

Stuart Cook
Stuart Cook

Reputation: 4014

So, it seems that what you really want to do is return more than one value from a native method. There is no direct way to do this, but there are a few indirect ways:

  • Instead of returning a boolean, create a class that contains both a boolean and a String, and have your native code create and return an instance of that class.
  • Pass a one-element String[] as an argument, and have the native code set its string result as the first element of that array.
  • Create a class with a #setStringValue(String) method, and pass that into your native method, which will then call that method to supply the string result. Once the native call completes, the Java code that created the helper class can extract the string value.

Of these possibilities, the one-element array is probably the easiest to handle from JNI code because it doesn't involve looking up additional classes or methods. However, if I wanted to return extra information from a non-native method I would probably return an instance of a new class containing both results.

Upvotes: 3

K-ballo
K-ballo

Reputation: 81349

Java strings are immutable by design, you cannot change them, not even with JNI.

Upvotes: 6

Related Questions