Replacing WCHAR and wchar_t with something generic - android

I'm trying to convert some VS 2015 C++ code to Android Studio C++ code.
My function looks like this:
int size = 0;
int len = 0;
fread(&size,sizeof(int),1,g_File);
#ifdef VERBOSE
printf("fullSentences size = %d\n",size);
#endif
int i1 = 0;
int i2 = 0;
int i3 = 0;
for(int i = 0; i < size; i++)
{
fread(&len,sizeof(int),1,g_File);
wchar_t *buff = new WCHAR[len+1];
fread(buff,sizeof(WCHAR),len,g_File);
buff[len]=0;
fread(&i1,sizeof(int),1,g_File);
fread(&i2,sizeof(int),1,g_File);
fread(&i3,sizeof(int),1,g_File);
fullSentences.Add(buff,i1,i2,i3);
delete buff;
#ifdef VERBOSE
FullSentence fs = fullSentences.Content().back();
printf("%s\t%d\t%d\n",fs.Text.c_str(),fs.ByteStart,fs.ByteCount);
#endif
}
I would like to get rid of the WCHAR and wchar_t in order to make the porting easier.
Can anybody suggestion a replacement for these?
I would like to avoid having to tell Android Studio what wchar_t is, if possible.
Thank you.
Edit:
Here is the class information:
//
// -------------------- clsFullSentences -----------------------------
//
vector<FullSentence> &clsFullSentences::Content()
{
return m_content;
}
vector<wstring> &clsFullSentences::CleanLower()
{
return m_sCleanLower;
}
void clsFullSentences::LoadSerializedFullSentences(string uFile)
{
if (!fileExists(stringToWString(uFile)))
{
DebugBreak();
}
FILE* inFile = fopen(uFile.c_str(), "rb");
wchar_t signature[2];
fread(signature, sizeof(wchar_t), 1, inFile);
wstring wline;
//read how many possibleresults we have
getLineW(inFile, wline);
unsigned int count=_wtoi(wline.c_str());
for (unsigned int i = 0; i < count; i++)
{
FullSentence st;
getLineW(inFile,wline);
st.Text = wline;
//getLineW(inFile,wline);
st.Emotion =0;// _wtoi(wline.c_str());
getLineW(inFile,wline);
st.ByteStart = _wtoi(wline.c_str());
getLineW(inFile,wline);
st.ByteCount = _wtoi(wline.c_str());
m_content.push_back(st);
}
fclose(inFile);
}
void clsFullSentences::Add(wstring text, int i1, int i2, int i3)
{
FullSentence fs;
fs.Text = text;
fs.Emotion = i1;
fs.ByteStart = i2;
fs.ByteCount = i3;
m_content.push_back(fs);
wstring sClean;
sClean=StripPuncToLower(text);
m_sCleanLower.push_back(sClean);
}
bool getLineW(FILE *inFile, wstring &result)
{
wchar_t data[2] = { 0, 0 };
result = L"";
do
{
fread(data, sizeof(wchar_t), 1, inFile);
if (data[0] > 0)
{
if (data[0] != 13)
{
if (data[0] != 10)
{
result += data;
}
else
{
break;//10 is the end of the line
}
}
}
} while (!feof(inFile));
if (result.size() > 0)
{
return true;
}
else
{
return false;
}
}

If you want this code to work on Windows and Android builds then you will find that on windows wchar_t is 2 bytes, while on android it is 4bytes. So you will not be able to write a file on windows, and later read it properly on android. In such case you would have to use char16_t on android and convert it to 'your
choosen' string type under android.
(edit: even better, if you can - make sure all the files are written as utf8 strings)
As for the 'choosen string type' I would suggest to use utf8, so instead of std::wstring use std::string (encoded using utf8). From my experience NDK team was discouraging use of wchar_t from the very begining (missing w* functions from c library etc.) I am not sure how it is now.
I work on a project which was originally coded with MFC, and then ported to android. We used from the very begining a TCHAR macro. Which as you know resolves to char on non unicode builds and to wchar_t on unicode builds. So the idea is to use TCHAR everywhere then under android TCHAR should resolve to char, while on windows use wchar_t (I assume you use unicode build under windows?).
I am not saying this is a best solution to share code between windows and android platform, there are lots of problems like conversions to utf8 on android, these are done with if-defs.

Use std::string that contains text in UTF-8 encoding. You may also want some lightweight library that helps to deal with checks and conversions of such text. Avoid using wchar_t and std::wstring in code that is meant to be portable.

Related

Android JNI - Reliable way to convert jstring to wchar_t

In my Android JNI code, I need to convert jstring to wchar_t. The closest reference I found was How do I convert jstring to wchar_t *.
One can obtain jchar* and the length using the following code:
const jchar *raw = env->GetStringChars(string, 0);
jsize len = env->GetStringLength(string);
wchar_t* wStr = new wchar_t[len+1];
It seems I cannot use wcncpy to copy "raw" into "wStr." Although jchar is 2-bytes long, wchar_t is 4 bytes long on all modern versions of Android OS.
One option is to copy one character at a time in a for loop:
for(int i=0;i<len;i++) {
wStr[i] = raw[i];
}
wStr[len] = 0;
The other option would be to call env->GetStringUTFChars() and use iconv_* routines to convert to wchar_t type.
Can someone please confirm if option 1 is valid? Hope I don't have to resort to option 2. Is there a better option? Regards.
wchar_t specifies an element size but not a character set or encoding. Since you are asking about a 32-bit element, can we assume you want to use Unicode/UTF-32? Regardless, once you decide which encoding you want, standard Java libraries are up to the task.
Use a String.getBytes() overload to get an array of bytes. (It is easier to do this in Java rather than JNI, if you have a choice.) Once you have a jbyteArray, you can copy it to a C buffer and cast to wchar_t *.
On Android, you might want Unicode/UTF-8. But that has an 8-bit code-unit so you probably wouldn't be asking about wchar_t. (BTW-a character in UTF-8 can need 1 or more bytes.)
One way would be to use String.getBytes("UTF-32LE"). Note this is making the ASSUMPTION that wchar_t is 4 bytes and little-endian, but this should be a fairly safe assumption to make.
Here's an example that passes a String from Java to C++, where it is converted to std::wstring, reversed, and passed back to Java:
class MyClass {
private native byte[] reverseString(byte[] arr);
String reverseString(String s) {
try {
return new String(reverseString(s.getBytes("UTF-32")), "UTF-32");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "";
}
}
}
On the C++ side you have:
std::wstring toWStr(JNIEnv *env, jbyteArray s)
{
const wchar_t *buf = (wchar_t*) env->GetByteArrayElements(s, NULL);
int n = env->GetArrayLength(s) / sizeof(wchar_t);
// First byte is BOM (0xfeff), so we skip it, hence the "buf + 1".
// There IS NO null-terminator.
std::wstring ret(buf + 1, buf + n);
env->ReleaseByteArrayElements(s, (jbyte*) buf, 0);
return ret;
}
jbyteArray fromWStr(JNIEnv *env, const std::wstring &s)
{
jbyteArray ret = env->NewByteArray((s.size()+1)*sizeof(wchar_t));
// Add the BOM in front.
wchar_t bom = 0xfeff;
env->SetByteArrayRegion(ret, 0, sizeof(wchar_t), (const jbyte*) &bom);
env->SetByteArrayRegion(ret, sizeof(wchar_t), s.size()*sizeof(wchar_t), (const jbyte*) s.c_str());
return ret;
}
extern "C" JNIEXPORT jbyteArray JNICALL Java_MyClass_reverseString(JNIEnv *env, jobject thiz, jbyteArray arr)
{
std::wstring s= toWStr(env, arr);
std::reverse(s.begin(), s.end());
return fromWStr(env, s);
}
I tested it both on my phone, which has Android 4.1.2 and ARM CPU, and on the Android Emulator - Android 4.4.2 and x86 CPU, and this code:
MyClass obj = new MyClass();
Log.d("test", obj.reverseString("hello, здравствуйте, 您好, こんにちは"));
Gave this output:
06-04 17:18:20.605: D/test(8285): はちにんこ ,好您 ,етйувтсвардз ,olleh
As long as all your data is UCS2, you can use option 1. Please see wchar_t for UTF-16 on Linux? for a similar discussion. Note that C++11 provides std::codecvt_utf16 to deal with the situation.
No need to convert. Cast const jchar to (wchar_t *). jni.h define jchar as typedef uint16_t jchar; /* unsigned 16 bits */ which is eventually wchar_t.
You can try this, it worked for me in old project.

How to get output from Linux command via C/C++? and suitable for android?

I try to run a Linux command and read the output from it by using C/C++ code.
I search for exec but this don't deal with input/output.
What I am trying to achieve is to get information about wireless LAN by using this command iwconfig, invoking it from C/C++ code.
also i need a suitable code to use it as lib for android using NDK.
i see in android open source they called this function
what do you think about this code ?
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
char *reply, size_t *reply_len,
void (*msg_cb)(char *msg, size_t len))
{
DWORD written;
DWORD readlen = *reply_len;
if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL))
return -1;
if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL))
return -1;
*reply_len = readlen;
return 0;
}
this is the link
You could try running the command and outputting the results to a file, then reading it
system("iwconfig > temp.txt");
FILE *fp=fopen("temp.txt","w");
i see in android open source they called this function
what do you think about this code ?
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
char *reply, size_t *reply_len,
void (*msg_cb)(char *msg, size_t len))
{
DWORD written;
DWORD readlen = *reply_len;
if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL))
return -1;
if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL))
return -1;
*reply_len = readlen;
return 0;
}
this is the link

NSNonLossyASCIIStringEncoding equivalent for Android

I got to port some chat code from iOS to Android. Before sending the chat message to the socket, the iOS code uses the NSNonLossyASCIIStringEncoding class as parameter of the NSString::dataUsingEncoding.
How would you do it in Android? Same question about the opposite decoding.
Without doing that, for instance, the line breaks disappear in the message received on the other mobile.
Code on iOS:
NSData *data1 = [myStringTosend dataUsingEncoding:NSNonLossyASCIIStringEncoding];
NSString *goodValue = [[[NSString alloc] initWithData:data1 encoding:NSUTF8StringEncoding] autorelease];
And decoding:
NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
So far (and not correct), encoding on the Android side:
OutputStream os = socket.getOutputStream();
os.write(request.getBytes("UTF-8"));
os.flush();
And decoding:
while ((bytesRead = is.read(buffer, 0, BUFFER_SIZE)) >= 0) {
if (bytesRead > 0) response.append(new String(buffer, 0, bytesRead, "UTF-8"));
if (bytesRead < BUFFER_SIZE) break;
}
#portforwardpodcast is absolutely correct that you should, if possible, avoid ASCII encoding your utf8 and instead set up your stack to handle/store utf8 directly. That said, if you don't have the ability to change the behavior, the following code may be helpful.
While there's no published explanation of how NSNonLossyASCIIStringEncoding works, based on its output it looks like:
Bytes in the extended ASCII range (decimal values 128 - 255) are escaped using an octal encoding (e.g. ñ with decimal value 241 -> \361)
Non-ASCII code points are escaped in two byte chunks using a hex encoding (e.g. 😥 which takes up 32 bits with decimal value 128549 -> \ud83d\ude25)
So to encode:
public static String encodeToNonLossyAscii(String original) {
Charset asciiCharset = Charset.forName("US-ASCII");
if (asciiCharset.newEncoder().canEncode(original)) {
return original;
}
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < original.length(); i++) {
char c = original.charAt(i);
if (c < 128) {
stringBuffer.append(c);
} else if (c < 256) {
String octal = Integer.toOctalString(c);
stringBuffer.append("\\");
stringBuffer.append(octal);
} else {
String hex = Integer.toHexString(c);
stringBuffer.append("\\u");
stringBuffer.append(hex);
}
}
return stringBuffer.toString();
}
And to decode (this can be made more efficient by parsing the two types of encodings in lock step, rather as two separate passes):
private static final Pattern UNICODE_HEX_PATTERN = Pattern.compile("\\\\u([0-9A-Fa-f]{4})");
private static final Pattern UNICODE_OCT_PATTERN = Pattern.compile("\\\\([0-7]{3})");
public static String decodeFromNonLossyAscii(String original) {
Matcher matcher = UNICODE_HEX_PATTERN.matcher(original);
StringBuffer charBuffer = new StringBuffer(original.length());
while (matcher.find()) {
String match = matcher.group(1);
char unicodeChar = (char) Integer.parseInt(match, 16);
matcher.appendReplacement(charBuffer, Character.toString(unicodeChar));
}
matcher.appendTail(charBuffer);
String parsedUnicode = charBuffer.toString();
matcher = UNICODE_OCT_PATTERN.matcher(parsedUnicode);
charBuffer = new StringBuffer(parsedUnicode.length());
while (matcher.find()) {
String match = matcher.group(1);
char unicodeChar = (char) Integer.parseInt(match, 8);
matcher.appendReplacement(charBuffer, Character.toString(unicodeChar));
}
matcher.appendTail(charBuffer);
return charBuffer.toString();
}
Don't use NSNonLossyASCIIStringEncoding, use utf-8 encoding. I just solved this problem myself on ios+android+java spring backend, and it took me around 4 full days to figure everything out. Android can't display emojis, but this gives me full character support in almost all (or all not sure) languages. Here are the articles that helped me:
Must Read: http://blog.manbolo.com/2012/10/29/supporting-new-emojis-on-ios-6 http://blog.manbolo.com/2011/12/12/supporting-ios-5-new-emoji-encoding
See the hex bytes of a string inside the DB: How can I see raw bytes stored in a MySQL column?
Details about how to setup MySQL: http://technovergence-en.blogspot.com/2012/03/mysql-from-utf8-to-utf8mb4.html
In depth FAQ of utf8- http://www.unicode.org/faq/utf_bom.html#utf8-4
Details about the difference from notation: \ud83d\udc7d and hex value in memory: 0xF09F91BD http://en.wikipedia.org/wiki/UTF-8#Description
Use this to copy and paste characters in to see real hex byte values (works for emojis): http://perishablepress.com/tools/utf8-hex/index.php
Get Spring to support utf8 in urls (for GET params) http://forum.springsource.org/showthread.php?93728-RequestParam-doesn-t-seem-to-be-decoded Get Parameter Encoding http://forum.springsource.org/showthread.php?112181-Unable-to-Override-the-Spring-MVC-URL-decoding-which-uses-default-quot-ISO-8859-1-quot
My answer code is equivalent to IOS NSNonLossyASCIIStringEncoding for Android.
In your gradle put below depandancy.
compile 'org.apache.commons:commons-lang3:3.4'
then Put method to your Utils Class Like this
public static String encode(String s)
{
return StringEscapeUtils.escapeJava(s);
}
public static String decode(String s)
{
return StringEscapeUtils.unescapeJava(s);
}
then Simply call this method where you want to encode string or decode String like this
//for encode
String stencode = Utils.encode("mystring");
//for decode
String stdecode = Utils.decode("mystring")

Using glutIdleFunc to get out of glutMainLoop?

Im writing a programme that opens and openGL windows with an image and connects to my android device where the user uses the device as a sort of trackpad to pan and zoom in and out. All is working fine however the programme gets stuck in the glutMainLoop and will not proceed with accepting data from the device. Apparently glutIdleFunc is the solution to my problem however i cant see how to implement this in my code without getting a memory error? Could someone show me how to put the function into my code so it runs the connection code as well as the opengl stuff?
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <strings.h>
#include <vrpn_Shared.h>
#include <vrpn_Analog.h>
#include <vector>
#include <GL/freeglut.h>
#include <imageviewer.h>
using namespace std;
int done = 0;
int accepted = 0; // Signals that the program should exit
unsigned tracker_stride = 1; // Every nth report will be printed
//-------------------------------------
// This section contains the data structure that holds information on
// the devices that are created. For each named device, a remote of each
// type analog is created.
class device_info {
public:
char *name;
vrpn_Analog_Remote *ana;
};
const unsigned MAX_DEVICES = 2;
//-------------------------------------
// This section contains the data structure that is used to determine how
// often to print a report for each sensor of each tracker. Each element
// contains a counter that is used by the callback routine to keep track
// of how many it has skipped. There is an element for each possible sensor.
// A new array of elements is created for each new tracker object, and a
// pointer to it is passed as the userdata pointer to the callback handlers.
class t_user_callback {
public:
char t_name[vrpn_MAX_TEXT_LEN];
vector<unsigned> t_counts ;
};
//Callback handlers
void VRPN_CALLBACK handle_analog (void *userdata, const vrpn_ANALOGCB a)
{
int i;
const char *name = (const char *)userdata;
printf("Input from %s:\n \n %5.0f", name, a.channel[0]);
for (i = 1; i < a.num_channel; i++) {
printf(" %5.0f \n", a.channel[1]);
}
printf(" \n");
}
int main (int argc, char * argv [])
{
int print_for_tracker = 1; // Print tracker reports?
int print_for_button = 1; // Print button reports?
int print_for_analog = 1; // Print analog reports?
int print_for_dial = 1; // Print dial reports?
int print_for_text = 1; // Print warning/error messages?
device_info device_list[MAX_DEVICES];
unsigned num_devices = 0;
int i;
// Parse arguments, creating objects
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-notracker")) {
print_for_tracker = 0;
} else if (!strcmp(argv[i], "-nobutton")) {
print_for_button = 0;
} else if (!strcmp(argv[i], "-noanalog")) {
print_for_analog = 0;
} else if (!strcmp(argv[i], "-nodial")) {
print_for_dial = 0;
} else if (!strcmp(argv[i], "-notext")) {
print_for_text = 0;
} else if (!strcmp(argv[i], "-trackerstride")) {
if (tracker_stride <= 0) {
fprintf(stderr, "-trackerstride argument must be 1 or greater\n");
return -1;
}
} else { // Create a device and connect to it.
device_info *dev;
// Name the device and open it as everything
dev = &device_list[num_devices];
dev->name = argv[i];
dev->ana = new vrpn_Analog_Remote(dev->name);
if (print_for_analog) {
printf(" Analog");
dev->ana->register_change_handler(dev->name, handle_analog);
}
printf(".\n");
num_devices++;
}
}
// main interactive loop
printf("Press ^C to exit.\n");
while ( ! done ) {
unsigned i;
// Let all the devices do their things
for (i = 0; i < num_devices; i++) {
device_list[i].ana->mainloop();
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowSize(400,300);
glutInitWindowPosition(200,100);
glutCreateWindow("ImageViewer");
init();
glutDisplayFunc(display);
glutMotionFunc(drag);
glutMouseFunc(mouse);
// glutIdleFunc(IdleFunc);
glutMainLoop();
}
}
return 0;
}
glut is fine if it can manage all of the input devices, and everything is event-driven from the inputs that it manages. Once you have unmanaged input devices or non-event-based processing, you probably want to use something other than glut. Your other alternative is to fork and run your asynchronous stuff in a separate process (or thread).

Android NDK and __android_log_print

I using this lib: https://github.com/mysolution/hyphenator In JNI I create this function:
int main2()
{
//load russian hyphenation patterns
struct pattern_list_t* plist = create_pattern_list();
size_t i = 0;
while (patterns[i])
{
struct pattern_t* p = create_pattern(patterns[i], isdigit_func, ismarker_func, char2digit_func);
add_patern(plist, p);
++i;
}
sort_pattern_list(plist);
//hyphenate test words
size_t word_index = 0;
while (test_words[word_index])
{
struct word_hyphenation_t* wh = hyphenate_word(test_words[word_index], plist, marker);
i = 0;
while (test_words[word_index][i])
{
__android_log_print(ANDROID_LOG_INFO, "HelloNDK!", "%c", test_words[word_index][i]);
++i;
}
destroy_word_hyphenation(wh);
++word_index;
}
//cleanup
destroy_pattern_list(plist);
return 0;
}
In Android NDK this work, but I get in LogCat:
02-21 16:15:18.989: INFO/HelloNDK!(403): �
How to solve this problem? I think that problem in encoding, but i don't know how to solve this.
What is your expected output? If the character falls outside the realm of ASCII you'll of course need to have something to view logcat that supports it. Assuming you're outputting UTF-8, Terminator is nice on Linux and Mintty (In combination with Cygwin/etc.) on Windows.
I worked it out, and this seems very wrong to me.....
So for char* concatenation in __android_log_vprint and __android_log_print it would appear you need to use the escape %s not %c.
This totally scuppers my plans for making a cross platform char* log between iOS, Android and Blackberry as printf("%s",myString.c_str()); is illegal. Will have to get funky with the args and parse the string. Anyway that's another problem and there is your fix ....

Categories

Resources