0

I feel that this should have a simple answer as I can do it easily with a java String, but something is going wrong with the CharSequence. My java code initializes a cppGenerator, passes a functionBlock object to its generateImplementation method, and returns a CharSequence. Here is my code:

jobject fBlock = env->CallObjectMethod(structuredTextModel, getFunction);
jclass CPPGenerator = lookForClass(env, "Path/CPPGenerator");
jobject CPPGeneratorImpl = invokeClassObj(env, CPPGenerator);
    jmethodID generateImplementation = env->GetMethodID(CPPGenerator, "generateImplementation", "(LPath/FunctionBlock;)Ljava/lang/CharSequence;");
jobject mid = env->CallObjectMethod(CPPGeneratorImpl, generateImplementation, fBlock);
jstring jresult = (jstring)mid;
char* result = (char*)env->GetStringUTFChars(jresult, 0);

This logic has worked for me when using String but this function returns a CharSequence. No classes, objects, or methods are returning null. The program crashes in jni.h attempting to call GetStringUTFChars. So i know I cannot use this method for CharSequence. Is there just a jni method I am missing or do I have to do something completely different for a CharSequence?

Zannith
  • 429
  • 4
  • 21

2 Answers2

1

You can't just cast mid to jstring and call GetStringUTFChars (since mid might not be a String, which seems to be the case). There is no convenience method like GetStringUTFChars for CharSequences like there is for Strings.

You'd have to do it the inconvenient way of calling charAt:

jclass cCharSequence = env->FindClass("java/lang/CharSequence");
jmethodID mLength = env->GetMethodID(cCharSequence, "length", "()I");
jmethodID mCharAt = env->GetMethodID(cCharSequence, "charAt", "(I)C");

int length = env->CallIntMethod(mid, mLength);
std::string str;
str.reserve(length);
for(int i = 0; i < length; i++) {
    str += env->CallCharMethod(mid, mCharAt, i);
}

// use 'str.c_str()'...
Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
  • ahah, charAt. That's what I was looking for. I figured I would have to do something like this if there was no method built in. Thank you this works perfectly for my code. – Zannith Jul 07 '17 at 15:38
  • 1
    Are you sure you don't get a warning here: jchar is wider than char? In any case, narrowing is both lossless and accurate only if the target character set is ASCII or ISO 8859-1. You could use `std::wstring` to stay with UTF-16 (jchar). – Tom Blodget Jul 07 '17 at 17:02
  • @TomBlodget I had forgotten about that fact. But having a compatible encoding is a pre-condition for using `GetStringUTFChars` any ways, so this should still be a correct equivalent. – Jorn Vernee Jul 07 '17 at 17:07
  • Yes surely the question asker should know what they are doing but GetStringUTFChars will result in valid _modified_ UTF-8 for all CharSequences (UTF-16), without lost or garbage data. – Tom Blodget Jul 07 '17 at 17:16
  • @TravisHerbert: Why not convert the `CharSequence` to a `String` using [`CharSequence::toString()`](https://docs.oracle.com/javase/7/docs/api/java/lang/CharSequence.html#toString())? Then you can use `String::GetStringUTFChars()` as before. Yes, it uses more memory, but it also avoids any issues with lossy data conversions. Using `CharSequence::charAt()` with `std::string` is lossy for non-ASCII characters (and yes, that includes ISO-8859-1). Any character > #127 is at risk. – Remy Lebeau Jul 07 '17 at 17:51
  • 2
    @TomBlodget: `std::wstring` uses UTF-16 only on Windows, it uses UTF-32 on other platforms. This is why `wchar_t` is not a portable data type for Unicode handling. You can use `std::basic_string` or C++11's `std::u16string` to assure a UTF-16 string on all platforms. Or use C++11's `std::wstring_convert` to convert UTF-16 input to `std::wstring` in its native format – Remy Lebeau Jul 07 '17 at 17:56
  • @TomBlodget Yes i tested the code and it works properly. And the encoding will be consistent. – Zannith Jul 07 '17 at 18:18
  • @RemyLebeau I may suggest converting to a string to the people working on the java side. There is however a small range of characters legal to be passed into these functions anyways and would result in an error within the java side before being called by the c++, so I am not worried about non-ASCII characters. – Zannith Jul 07 '17 at 18:18
  • @TravisHerbert: Could you post a reference to such character set, please: _"a small range of characters legal to be passed into these functions anyways and would result in an error within the java side"_ – Same old guy... Jan 06 '18 at 22:22
1

Why not use toString directly?

jclass clzCharSequence = env->GetObjectClass(mid);
jmethodID toString = env->GetMethodID(clzCharSequence, "toString", "()Ljava/lang/String;");
jobject s = env->CallObjectMethod(mid, toString);
const char* cstr = env->GetStringUTFChars(static_cast<jstring>(s), NULL);
// do something with cstr
env->ReleaseStringUTFChars(static_cast<jstring>(s), cstr);
alijandro
  • 11,627
  • 2
  • 58
  • 74