unpredictable runtime behavior while converting QString to const char* - c++

I have these following codes set up-
class ID3{
const char* fileName;
TagLib::FileRef *file;
public:
ID3(const char *);
QImage* artwork();
}
ID3::ID3(const char* fileNameStr){
this->fileName = fileNameStr;
this->file = new TagLib::FileRef(fileNameStr);
qDebug()<<fileNameStr; //OUTPUT 2
}
QImage* ID3::artwork(){
QString str = QString::fromLocal8Bit(this->fileName);
qDebug()<<str; //OUTPUT 3
//MORE CODES-------
}
const char * QstrTocChar(QString str){
QByteArray ba = str.toLocal8Bit();
qDebug()<<ba.constData(); //OUTPUT 1
return ba.constData();
}
int main(int argc, char *argv[]){
.
.
.
QString fileName = "C:/Qt/Qt5.0.2/Projects/taglib_test/music files/Muse_-_Madness.mp3";
file = new ID3(QstrTocChar(fileName));
QImage *image = file->artwork();
}
Now when I run the program, I get these strange outputs
OUTPUT 1
C:/Qt/Qt5.0.2/Projects/taglib_test/music files/Muse_-_Madness.mp3
OUTPUT 2
????p???e'2/
OUTPUT 3
"°í³àpµ˜Æe'2/"
Not sure about OUTPUT 2 but I expect OUTPUT 3 to be same as OUTPUT 1. I am a Qt newbie. Would really appreciate advice/help in understanding, these strange character encoding issues and how to get OUTPUT 3 fixed.
Thanks!

ba.constantData() is returning a pointer to data which will be invalid when QstrToChar finishes executing (the 8-bit converted QByteArray), when QstrToChar completes, all you have left is free'd junk.
What if you just did:
file = new ID3(fileName.toLocal8Bit().constData());
in your main routine?
Actually, you still probably need to keep your own copy of this data in your private ID3 char *, since it can go away with the destruction of these temporaries.
Your code should be this, instead:
class ID3{
std::string fileName;
std::smart_ptr<TagLib::FileRef> file;
public:
ID3(std::string);
QImage* artwork();
}
ID3::ID3(std::string fileNameStr) {
this->fileName = fileNameStr;
this->file.reset(new TagLib::FileRef(fileNameStr));
qDebug()<<fileNameStr; //OUTPUT 2
}
QImage* ID3::artwork(){
QString str = QString::fromLocal8Bit(this->fileName);
qDebug()<<str; //OUTPUT 3
//MORE CODES-------
}
std::string QstrToCppString(QString str){
QByteArray ba = str.toLocal8Bit();
qDebug()<<ba.constData(); //OUTPUT 1
return std::string(ba.constData());
}
int main(int argc, char *argv[]){
.
.
.
QString fileName = "C:/Qt/Qt5.0.2/Projects/taglib_test/music files/Muse_-_Madness.mp3";
file = new ID3(QstrToCppString(fileName));
QImage *image = file->artwork();
}
Notice that I've wrapped your TagLib::FileRef in a smart_ptr as well, since you are new-ing it, you'll need to manage the memory. An alternative would be to write a proper destructor for your ID3 class. You're definitely leaking these currently (unless you just didn't share your destructor code).

Related

lost value of object in c++ sqlite callback function

these are the functions, after building the correct object inside fillAlbum data gets lost in openAlbum.
/*
the function will fill the album with correct values (callback function)
*/
int fillAlbum(void* data, int argc, char** argv, char** azColName)
{
Album* album = new Album();
album->setName(argv[1]);
album->setCreationDate(argv[3]);
album->setOwner(std::stoi(argv[2]));
data = album;
return 0;
}
/*
the function return the asked album
*/
Album DatabaseAccess::openAlbum(const std::string& albumName)
{
Album album;
char** errMessage = nullptr;
std::string sqlStatement = "SELECT * FROM ALBUMS WHERE NAME LIKE '" + albumName + "';";
sqlite3_exec(db_access, sqlStatement.c_str(), fillAlbum, &album, errMessage);
return album;
}
It gets lost (in fact it is worse: you have a memory leak!) because you don't use the callback correctly. You pass &album and now you have to cast the void* pointer and fill it, not overwrite it (in fact, the data = album line has no effect at all outside the fillAlbum function, you just overwrite a local variable). Try this:
int fillAlbum(void* data, int argc, char** argv, char** azColName)
{
Album* album = static_cast<Album*>(data); // <-- this line is crucial
album->setName(argv[1]);
album->setCreationDate(argv[3]);
album->setOwner(std::stoi(argv[2]));
return 0;
}

Pointer to pointer comparision breaks when while(1) removed - why?

Trying to build a menu system but running into some issues with pointers - which I don't have much experience with.
I don't understand why removing the while(1) makes the comparison fail between mainmenu_table[1][i] == &option6 but for some reason it does.
What am I doing wrong? Using visual studio and an atmega328p. Thanks
Serial output with original code:
Serial begins
MeNu6
MeNu6
Starting compare loop
it worked
Serial output with while(1) removed.
Serial begins
MeNu6
MeNu6
Starting compare loop
the end
Original code (with while(1) included)
const char option1[] PROGMEM = "Menu1";
const char option2[] PROGMEM = "MEnu2";
const char option3[] PROGMEM = "MeNu3";
const char option4[] PROGMEM = "Menu4";
const char option5[] PROGMEM = "MEnu5";
const char option6[] PROGMEM = "MeNu6";
const char option7[] PROGMEM = "menu7";
const char* const submenu1_table[] PROGMEM = { option1, option2, option3 }; // array of pointers to chars stored in flash
const char* const submenu2_table[] PROGMEM = { option4, option5, option6, option7 };
const char** const mainmenu_table[] PROGMEM = { submenu1_table, submenu2_table }; //array of pointers to pointers to chars in flash
// The setup() function runs once each time the micro-controller starts
void setup()
{
Serial.begin(9600);
delay(100);
Serial.println("Serial begins");
Serial.println((const __FlashStringHelper*)(mainmenu_table[1][2])); // prints "Menu6" as expected
Serial.println((const __FlashStringHelper*)option6); // also prints "Menu6"
Serial.println("Starting compare loop");
for (int i = 0; i < 4; i++) {
if ( mainmenu_table[1][i] == &option6 ) { //
Serial.println("it worked");
while (1); // COMMENTING THIS OUT MEANS DOESN'T COMPARE SUCCESSFULLY.
}
}
Serial.println("the end");
}
// Add the main program code into the continuous loop() function
void loop()
{
}
According to Arduino description of PROGMEM, you cannot access the data through pointers to it directly as with plain pointers. You need to use the proper macros/functions to access the data.
In your code, the pointer tables themselves are located in the PROGMEM, so, to extract the individual pointers, you are supposed to do something like:
const char** submenu = (const char**)pgm_read_word(&(mainmenu_table[1]));
const char* option = (const char*)pgm_read_word(&(submenu[i]));
if (option == option6) {
//...
This code is based on the string table example from the first link.

Loss of non ascii characters from QString to char *

I have a QString with non ascii characters . I am passing this QString to another process . However when I debug the receiving process the argv arguments do not receive a correct string that was intended from the source . My pseudo code would be
process.start( proc, QStringList() << "-a" <<param );
process.waitForFinished(m_timeout);
Here param is the QString that contains the non ascii text
I solved the problem with the following code
LPWSTR *szArglist;
int argC;
szArglist = CommandLineToArgvW(GetCommandLineW(),&argC);
if(NULL==szArglist) {
throw;
}
QString qstrConvertFile = QString::fromUtf16((const ushort*)(szArglist[4]));

AT command list as char * array

,,I need to provide an AT command to a modem which looks like this: AT^SRPN=1,99991,"Download_URL","Image";^SMSO
How can I insert the variable download_url and the variable image into the commands string array? Is the right way to declare the commands array not as const and to use strcpy() to insert the two variables into the commands list?
The function at_send_commands() needs the commands list as const.
Function proto: at_resp_t at_send_commands(TickType ticks_to_wait, const char *commands[]);
at_resp_t at_send_download_url_and_image(const char *download_url, const char *image)
{
static const char *commands[] =
{
"AT^SRPN=1,99991,",
download_url,
",",
image,
";^SMSO\r",
NULL
};
at_resp_t err = at_send_commands(AT_TIMEOUT, commands);
if (err)
return err;
}
Try this:
at_resp_t at_send_download_url_and_image(const char *download_url, const char *image)
{
std::string str("AT^SRPN=1,99991,");
str += download_url;
str += ",";
str += image;
str += ";^SMSO\r";
const char* command = str.c_str();
const char* commands[] =
{
command,
NULL
};
at_resp_t err = at_send_commands(AT_TIMEOUT, commands);
if (err)
return err;
}
In C the simplest way is IMO
void send_command(const char *download_url, const char *image) {
char buf[1000];
sprintf(buf, "AT^SRPN=1,99991,\"%s\",\"%s\";^SMSO",
download_url, image);
...
}
in buf you will end up having the final command to send to the modem.
If this code can be used in an hostile environment then you should also pay attention that no overflow can happen when passed large strings as url/image (e.g. add a check on strlen first or use snprintf instead).

Using string pointers in C++ and Objective-C

I have a sample project here on github where I created a c++ wrapper class for an external C++ library that I want to use in Objective-C.
I don't understand why my returned pointers are sometimes correct and sometimes wrong. Here's sample output:
Test Data = 43343008
In Compress 43343008
Returned Value = 43343008
Casted Value = 43343008
Test Data = 2239023
In Compress 2239023
Returned Value = 2239023
Casted Value = 2239023
Test Data = 29459973
In Compress 29459973
Returned Value = 29459973
Casted Value = l.remote
Test Data = 64019670
In Compress 64019670
Returned Value =
Casted Value = stem.syslog.master
In the above output you can see that the 1st and 2nd click of the button outputs the results I was expecting. In each of the other clicks either the returned value or casted value are invalid. I'm assuming this is because my pointer is pointing to an address I wasn't expecting. when running the app multiple times, any button click could be right or wrong.
I also tried with a single thread but experienced similar results.
The complete code is on github but here are the important bits.
ViewController.m
#import "ViewController.h"
extern const char * CompressCodeData(const char * strToCompress);
#implementation ViewController
...
// IBAction on the button
- (IBAction)testNow:(id)sender
{
[self performSelectorInBackground:#selector(analyze) withObject:nil];
}
- (void)analyze
{
#synchronized(self) {
const char *testData = [[NSString stringWithFormat:#"%d",
(int)(arc4random() % 100000000)] UTF8String];
NSLog(#"Test Data = %s", testData);
const char *compressed = CompressCodeData(testData);
NSLog(#"Returned Value = %s", compressed);
NSString *casted = [NSString stringWithCString:compressed
encoding:NSASCIIStringEncoding];
NSLog(#"Casted Value = %#\n\n", casted);
}
}
#end
SampleWrapper.cpp
#include <iostream>
#include <string.h>
#include <CoreFoundation/CoreFoundation.h>
using namespace std;
extern "C"
{
extern void NSLog(CFStringRef format, ...);
/**
* This function simply wraps a library function so that
* it can be used in objective-c.
*/
const char * CompressCodeData(const char * strToCompress)
{
const string s(strToCompress);
// Omitted call to static method in c++ library
// to simplify this test case.
//const char *result = SomeStaticLibraryFunction(s);
const char *result = s.c_str();
NSLog(CFSTR("In Compress %s"), result);
return result;
}
}
You are returning a pointer to at object that has been deallocated.
const string s(strToCompress);
…
const char *result = s.c_str();
NSLog(CFSTR("In Compress %s"), result);
return result;
s does not exist after CompressCodeData() function is over, so the pointer to it's internal memory is invalid.
You could allocate a chunk of memory to hold the response, but it would be up to the caller to release it.
char *compressed = CompressCodeData(testData);
NSLog(#"Returned Value = %s", compressed);
NSString *casted = [NSString stringWithCString:compressed
encoding:NSASCIIStringEncoding];
free(compressed);
NSLog(#"Casted Value = %#\n\n", casted);
…
const char * CompressCodeData(const char * strToCompress)
…
char *result = strdup(s.c_str());
Another solution is to pass in the memory to store the data into.
char compressed[2048]; // Or whatever!
CompressCodeData(testData, compressed, sizeof(compressed));
NSLog(#"Returned Value = %s", compressed);
NSString *casted = [NSString stringWithCString:compressed
encoding:NSASCIIStringEncoding];
NSLog(#"Casted Value = %#\n\n", casted);
…
void CompressCodeData(const char * strToCompress, char *result, size_t size)
…
s.copy(result, size - 1);
result[s.length() < size ? s.length() : size-1] = '\0';