In a readline buffer I want to provide an initial value the user can edit. This can either be a default value or the old value of the field already saved to my database. The entered value shall not be added to the readline-history as it is only "data" not a command.
From the samples here and on the internet I have learned that this code should do the job:
char *deftext;
int MatCustom::set_deftext ()
{
// cout << "set_deftext called ... deftext: " << deftext << endl;
if (deftext)
{
// cout << "inserting..." << endl;
rl_insert_text(deftext);
// rl_line_buffer = deftext;
// cout << rl_stuff_char('J') << endl;
rl_redisplay();
deftext = (char *)NULL;
rl_startup_hook = (rl_hook_func_t *)NULL;
// rl_pre_input_hook = (rl_hook_func_t *)NULL;
};
return 0;
}
string MatCustom::getlineWithoutHistory(const string &field) {
string returnString;
const char *buffer;
char *cstr = new char[field.length() + 1];
strcpy(cstr, field.c_str());
deftext = cstr;
// cout << "Deftext in getLineWithoutHistory: " << deftext << endl;
rl_startup_hook = set_deftext;
// rl_pre_input_hook = set_deftext;
buffer = :: readline("");
if (buffer != nullptr) {
returnString = buffer;
} else {
returnString = "";
}
free((void *) buffer);
delete [] cstr;
return returnString;
}
It works perfectly well to convert my old value to a char* and pass it to the global variable deftext, because a parameter cannot be given to the readline-hook-functions.
I am experiencing problems: Whenever I use the rl_startup_hook, which is said to be called before the prompt gets printed, I find this this hook never gets executed. However, when I use the rl_pre_input_hook, which is called prior to the input to begin, my function set_deftext actually gets called. This functions finds deftext set.
Still, I don't get any text inserted into the input buffer. The function rl_insert_text produces no change – even when I use rl_redisplay(). Directly changing the buffer with rl_line_buffer = deftext; also has no effect. When I try cout << rl_stuff_char('J') << endl; for debugging just to insert a single char, nothing happens and the function returns 0 indicating an unsuccessful insert.
Both functions are static member functions. I usually call my function with
input = MatCustom::getlineWithoutHistory(rawValue);
to edit to provided value (rawValue).
Related
So I am writing a main function that passes two char values into five different char* functions one by one, but when I return the value of it, it completely changes my value; how would I fix this?
For example:
Passing char one into my function, myStrCat, returns world! since that is what my function does (btw, the assignment is how to code specific library functions without any library imports). But not only does it return it, it changes it. So when I pass one into the next function down, it will return the wrong value as one (Hello ) isn't being passed, instead two (World!) is.
char one[size] = "Hello ";
char two[size] = "World!";
cout << "strlen: " << myStrLen(one) << endl;
cout << "strcat: " << myStrCat(one, two) << endl;
Word function is being passed into
char* myStrCat(char inputOne[], char inputTwo[]){
int sizeOne = myStrLen(inputOne);
int sizeTwo = myStrLen(inputTwo);
for(int i = 0; i < sizeTwo; i++){
inputOne[i + sizeOne] = inputTwo[i];
}
return inputOne;
}
In C++ string literals "Hello" are const and are immutable. I wanted to make a custom string class whose strings are not const chars, so they can be changeable
Here is a snippet of code that might illustrate what I'm trying to do:
#include <iostream>
class String {
public:
char * p_start;
String(char * strSourc) // Constructor
{
p_start = strSourc;
}
};
int main()
{
String myString("Hello");
// Create object myString, send "Hello" string literal as argument
std::cout << myString.p_start << std::endl;
// Prints "Hello"
*myString.p_start = 'Y';
// Attempt to change value at first byte of myString.p_start
std::cout << myString.p_start << std::endl;
// Prints "Hello" (no change)
myString.p_start = "Yellow";
// Assigning a string literal to p_start pointer
std::cout << myString.p_start << std::endl;
// Prints Yellow, change works. I thought myString "Hello" was const chars, immutable
return 0;
}
So, I'm confused. I've looked everywhere and it says that string literals, like "Hello", are immutable, each of their char bytes are unchangeable. Though I managed to assign Yellow to the p_start pointer, changing the first letter. Though changing the single letter H to a Y through dereferencing the H pointer didn't do anything.
Any insights would help me, thanks.
I think you're confusing about pointer and pointee.
p_start = "Yellow", you're changing the value of pointer, to point to "Yellow". *p_start = 'Y', you're changing the value of pointee, the content p_start points to, not itself. As you said, "Yellow" are const chars, so the behaviour try to modify them is UB.
You can make a copy of it in the ctor, then you can modify the chars, which are managed by the class. Yes, it will be a new copy, but it won't be a waste of memory. And you have no choice if you want them to be changable.
not sure if anyone finds this helpful after so long but since I am learning myself I took your above code and made it work by copying. It now has a member variable for size and cleans up using delete when you assign a new string literal to it (const char*).
#include <iostream>
class String {
public:
char * p_start;
int m_size;
String(const char * strSourc) // Constructor
{
//The following will get the size of the parameter.
int SizeParameter=0;
while (*(strSourc+SizeParameter) != '\0')
{
SizeParameter++;
}
// size of string saved.
m_size = SizeParameter;
// allocate enough memory so we can copy strSourc
p_start = new char[SizeParameter+1];
//copy the contents strSourc
for(int i=0; i<SizeParameter+1; i++)
{
*(p_start+i) = *(strSourc+i);
}
}
//Handle change of string value.
char* AssignNewString (const char* newtext)
{
//clean
delete p_start;
int SizeParameter=0;
while (*(newtext+SizeParameter) != '\0')
{
SizeParameter++;
}
// size of string saved.
m_size = SizeParameter;
// allocate enough memory so we can copy strSourc
p_start = new char[SizeParameter+1];
//copy the contents strSourc
for(int i=0; i<SizeParameter+1; i++)
{
*(p_start+i) = *(newtext+i);
}
return p_start;
}
char* operator=(const char* newtext)
{
AssignNewString(newtext);
}
};
int main()
{
String myString("Hello");
// Create object myString, send "Hello" string literal as argument
std::cout << "string size: " << myString.m_size << std::endl;
std::cout << myString.p_start << std::endl;
// Prints "Hello"
*myString.p_start = 'Y';
// Attempt to change value at first byte of myString.p_start
std::cout << myString.p_start << std::endl;
// Prints "Hello" (no change)
myString = "yellow";
// Assigning a string literal to p_start pointer
myString = "THIS IS A LONGER STRING";
std::cout << myString.p_start << std::endl;
std::cout << "string size: " << myString.m_size << std::endl;
return 0;
}
Note I am learning myself so do let me know if I am doing something wrong. But so far, it seems to work.
I wrote a simple function to perform in place reversal:
void in_place_reverse(char *str){
if(!str || !(*str)){
return;
}
char *str_end = str+strlen(str)-1;
int temp;
while(str < str_end){
temp = *str;
*(str++) = *str_end;
*(str_end--) = temp;
}
}
I'm just wondering why when I do something like this:
char str[] = "Reverse me!";
cout << "Original: " << str << endl;
in_place_reverse(str);
cout << "Reversed: " << str << endl;
str wasn't changed inside of the function. The reason I ask is because the line *(str++) is incrementing the pointer that points to str. So what I'm really asking is why something like this isn't necessary:
char *str_beg = str;
char *str_end = str+strlen(str)-1;
int temp;
while(str_beg < str_end){
temp = *str_beg;
*(str_beg++) = *str_end;
*(str_end--) = temp;
}
So that we're not actually changing the pointer that points to the first position of str.
You actually are doing this implicitely because 'str' is passed by value (read: 'as a copy in a temporary variable').
To clarify this without the (distracting) pointer: consider
void increment(int x) {
x++;
}
int i = 1;
cout << i << endl;
increment(i);
cout << i << endl;
This will print '1' twice. The x that is seen inside the increment routine has the same value like the passed i. But it is not the same variable i. In fact it is a copy of i. When we return from the routine, the copy is discarded. Further reading: This would be different if we'd pass x by reference, like so:
void increment(int &x) {
x++;
}
The declaration of the function void in_place_reverse(char *str) results in a copy of the pointer being created when the function is called, in a variable called str that is private and local to the in_place_reverse. You can modify this value all you like without affecting the original that exists in the scope of the calling function.
I have wrote an API as defined below. This API is used to find the index of a filename in a file system. The filesystem is coming from an Android device through mtp. What I'm doing is to request a list of files stored on the Android device and compare each file listed to the one I'm looking for 'name2look'
I have created a vector table to store what I'm doing but it's not mandatory. My concerns is that the variable name2look contain the right name I'm looking for "Pictures"
uint32_t USBDevice::GetIndexFromName(LIBMTP_mtpdevice_t *dev, uint32_t storage,const char *name2look)
{
uint32_t idx_fold = 1;
std::vector<MyFileTreeItem*> FSimage;
LIBMTP_file_t *files;
LIBMTP_file_t *file;
std::cout << "NAME : " << name2look << "\n";
files = this->GetFileAndFolder(dev, storage,0);
file = files;
while (file != NULL) {
MyFileTreeItem* FSitem = new MyFileTreeItem();
FSitem->filename = file->filename;
FSitem->index = file->item_id;
FSitem->FileType = file->filetype;
FSimage.push_back(FSitem);
std::cout << "NAME : " << name2look << "\n";
std::cout << "FS NAME : " << file->filename << "\n";
if(std::strcmp(file->filename, name2look)==0) {
std::cout << "FIND IDX : " << file->item_id << "\n";
return file->item_id;
}
file = file->next;
}
return 0;
}
The Log is showing that the first display 'std::cout' is ok. the variable name is still 'Pictures' but when I ask to display it after in the "while" the variable name2look change and is not the same anymore.
First display
NAME : Pictures
second one in the while
NAME : Martin).mp3
FS NAME : Music
How is it possible to be corrupted ??
The function is called by a Qt C++ code:
void MyButtonGroup::buttonClick(QAbstractButton *button)
{
uint32_t status;
QList<QTreeWidgetItem *> itemList;
uint32_t index = 0;
if (button->text() == "Create Folder") {
itemList = this->MyTree->selectedItems();
QString str = itemList[0]->text(0);
char *utf8_text = str.toLatin1().data();
if(utf8_text != NULL)
{
std::cout << "A CHERCHER " << utf8_text << "\n";
index = this->MyDevice.GetIndexFromName(this->dev_id, storage->id, utf8_text);
}
else
index = 0;
CreateFolderDialog *dialog = new CreateFolderDialog(this->MyTree, this->MyDevice, this->dev_id, this->storage, index);
dialog->show();
}
utf8_text report the right value.
Any idea ?
This might be the problem. I am not sure. Check it out.
This line:
char *utf8_text = str.toLatin1().data();
What does the documentation say toLatin1() does? It creates a new QByteArray object and then you call data() on that and you get a pointer to character and then the QByteArray is destroyed at the end of the line because it was TEMPORARY.
And now you have an invalid pointer to freed memory that you then pass into your function. It probably gets overwritten by the first call to new() in your function.
I think you should change it to something like this:
QByteArray latin_str = str.toLatin1();
char *utf8_text = latin_str.data();
Your name utf8_text is weird since you just converted it to Latin1 which isn't UTF8.
I'm working on a project that deals with creating two strings, a username and a password. The two elements make an object of an Account. In the main, there is an Array of Accounts that is initialized at 10.
I have a Save & Quit option, which saves the Username on one line and the Password on the next in the same file. A pair of lines signifies another account.
My question is, how do you properly save the data from the Array of Accounts, then load the data from the previous Array of Accounts?
I get a std::bad_alloc memory error every time I try the loadAccounts() function. I've several different methods, but to no avail.
So far I've come up with this for saving the array (works just as it should so far) :
void saveAccounts(Account accs [], int numIndexes)
{
std::ofstream savefile("savedata.sav", std::ofstream::binary); // By re-initializing the file, the old contents are overwritten.
for (int i = 0; i < numIndexes; i++)
{
savefile << accs[i].getUsername() << endl;
savefile << accs[i].getPassword() << endl;
}
savefile.close();
}
As for my loading function I have :
Account* loadAccounts() // Load the data from the file to later print to make sure it works correctly.
{
cout << "LOADING ACCOUNTS!" << endl;
std::ifstream loadfile("savedata.sav", std::ifstream::binary);
Account * acc_arr; // The "Array" to be returned.
Account tmp_arr [10]; // The array to help the returned "Array."
acc_arr = tmp_arr; // Allowing the "Array" to be used and returned because of the actual array.
if (loadfile.is_open())
{
int i = 0;
while (loadfile.good())
{
cout << "Loadfile is good and creating Account " << i+1 << "." << endl; // For my own benefit to make sure the data being read is good and actually entering the loop.
std::string user;
std::getline(loadfile, user);
std::string pass;
std::getline(loadfile, pass);
Account tmpAcc(user, pass);
tmp_arr[i] = tmpAcc;
++i;
}
Account endAcc = Account(); // The default constructor sets Username to "NULL."
tmp_arr[i] = endAcc;
}
loadfile.close();
cout << "ACCOUNTS LOADED SUCCESSFUL!" << endl;
return acc_arr;
}
I've gathered that I can return an array by using a pointer and an actual array to do that same, since an array can't actually be returned.
I try to use the returned array here, which I'm trying to "copy" over the loaded array to the Array that will actually be printed. Later, I'll print the array (acc_arr) to ensure that the loaded array was loaded successfully :
else if (selection == 'l' || selection == 'L')
{
Account * tmp_acc_arr = new Account [10];
tmp_acc_arr = loadAccounts();
_getch();
for (size_t i = 0; i < size_t(10); i++)
{
if (tmp_acc_arr[i].getUsername() == "NULL")
{
break;
}
acc_arr[i] = tmp_acc_arr[i];
cout << "Added Account " << i << " succesfully." << endl;
}
}
The error is caused by this last block of code. I've checked to make sure the data copied correctly by using
EDIT: Awkward... by using an if statement to make sure the data within the tmp_acc_arr actually has data stored once it was returned and initialized in the main.
tmp_arr ist local in loadAccounts and on the stack. It will be invalid once loadAccounts() returns. Your return value is an invalid stack-pointer.
You could hand your pointer tmp_acc_arr to the function as an argument and fill it with the values from your file.
You should also check for overflow or better use STL containers like std::vector.
edit
void loadAccounts(Account * acc_memory, std::allocator<Account> alloc, size_t acc_array_size) // Load the data from the file to later print to make sure it works correctly.
{
Account *end_of_construction = acc_memory;
try
{
cout << "LOADING ACCOUNTS!" << endl;
std::ifstream loadfile("savedata.sav", std::ifstream::binary);
if (loadfile.is_open() && loadfile.good())
{
size_t i = 0;
for (size_t i=0; i<acc_array_size; ++i)
{
if (loadfile.good())
{
cout << "Loadfile is good and creating Account " << i+1 << "." << endl; // For my own benefit to make sure the data being read is good and actually entering the loop.
std::string user, pass;
std::getline(loadfile, user);
if (loadfile.good())
{
std::getline(loadfile, pass);
alloc.construct(end_of_construction++, user, pass);
}
else alloc.construct(end_of_construction++);
}
else alloc.construct(end_of_construction++);
}
}
loadfile.close();
cout << "ACCOUNTS LOADED SUCCESSFUL!" << endl;
}
catch (...)
{
size_t num_constructed = end_of_construction-acc_memory;
for (size_t i=0; i<num_constructed; ++i) alloc.destroy(acc_memory + i);
throw;
}
}
Used like
size_t const num_elements = 10;
std::allocator<Account> acc_alloc;
Account * tmp_acc_arr = acc_alloc.allocate(num_elements);
loadAccounts(tmp_acc_arr, acc_alloc, num_elements);
// do stuff
for (size_t i=0; i<num_elements; ++i) acc_alloc.destroy(tmp_acc_arr + i);
acc_alloc.deallocate(tmp_acc_arr, num_elements);
You return a pointer that points to an array that is destroyed as soon as you exit the function. Using that pointer leads to undefined behavior, that is BAD.
As you observed, array is not a valid return type, so to return it actually you shall put it inside a struct, and return the struct.