recording user input to string array from editLine - c++

Building a simple GUI in QT. I want to collect a list of strings every time a user inserts a name from the insert button. I got the raw logic to work for cli, however it is not the same when I convert logic to QT/C++ because the program just closes forcefully. I am using lineEdit from the input widgets. What am I doing wrong and what could have been a better alternative?
CLI Logic:
std::cout<<"Enter a name\tor enter nothing to quit"<<std::endl
<<">>>";
getline(std::cin, getNames);
listOfNames[enteredNames] = getNames;
while(getNames.length() > 0){
enteredNames++;
std::cout<<"Enter another name\tor enter nothing to quit"<<std::endl
<<">>>";
getline(std::cin, getNames);
listOfNames[enteredNames] = getNames;
}
QT Logic:
void MainWindow ... buttonClicked(){
v.namesEntered = 0;
v.listOfNames[50]={0};
v.getNames = ui->nameInputBox->text().toStdString();
while(v.getNames.length() > 0){
v.namesEntered++;
v.listOfNames[v.namesEntered] = v.getNames;
}
}
In case you are wondering the dot notations on v data is because I made a structure in mainwindow.h and needed my variables to be global for other functions.

You did not show what v.listOfNames is declared as, but it like a static array, is that right? You should use a std::vector<std::string> instead.
In any case, in your QT code, your while loop runs endlessly if the user enters a non-blank string, since you don't modify v.getNames so that its length() can decrease over time. You probably don't need the while loop at all if the user can enter only 1 string per button click.
Also, you are incrementing namesEntered too soon, and not validating when namesEntered exceeds the bounds of listOfNames if it is indeed a static array.
Try something more like this instead:
CLI
enteredNames = 0;
...
std::cout << "Enter a name\tor enter nothing to quit" << std::endl << ">>>";
while (getline(std::cin, getNames) && !getNames.empty())
{
listOfNames[enteredNames] = getNames;
++enteredNames;
std::cout << "Enter another name\tor enter nothing to quit" << std::endl << ">>>";
}
QT
void MainWindow ... initialize(){
v.namesEntered = 0;
}
void MainWindow ... buttonClicked(){
v.getNames = ui->nameInputBox->text().toStdString();
if (!v.getNames.empty()) {
if (v.namesEntered >= std::size(v.listOfNames)) {
// the list is full, do something else ...
}
else {
v.listOfNames[v.namesEntered] = v.getNames;
v.namesEntered++;
}
}
}

Related

C++ Clear cin buffer without user input

I have a loop running that displays data read from a serial port on a console screen, the same screen lists some options for the user, such as the option to enter a file name that data would be logged to, or exit. The user presses the required key to enter the desired option.
After the key press and the option function entered any key presses can be ignored/discarded but I am not able to find a way to discard the key presses/ clear the cin buffer resulting in the key press being shown on the console.
Is there any way to clear the cin buffer without the user having to take any action? Or possibly some better way to provide the same functionality?
A stripped down version of code here:
(this should display the local time while giving the user the option to enter a "file name" or exit, no serial port stuff included)
#include <iostream>
#include <limits>
#include <chrono>
#include <windows.h>
using namespace std;
// global variables
char SavedFileName[20];
HANDLE hConsole;
CONSOLE_SCREEN_BUFFER_INFO csbi;
// functions
void GetTime(void){
char buffer [20];
time_t now = time(NULL);
strftime(buffer , 20, "%H:%M:%S %d/%m/%Y", localtime(&now));
cout << "local time: " << buffer << endl;
}
bool is1keypressed(void){
if(GetAsyncKeyState(49) & 0x8000){
return true;
}
return false;
}
bool is9keypressed(void){
if(GetAsyncKeyState(57) & 0x8000){
return true;
}
return false;
}
int ClearConsole(void){
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SMALL_RECT scrollRect;
COORD scrollTarget;
CHAR_INFO fill;
// Get the number of character cells in the current buffer.
if (!GetConsoleScreenBufferInfo(hConsole, &csbi))
return 0;
// Scroll the rectangle of the entire buffer.
scrollRect.Left = 0;
scrollRect.Top = 0;
scrollRect.Right = csbi.dwSize.X;
scrollRect.Bottom = csbi.dwSize.Y;
// Scroll it upwards off the top of the buffer with a magnitude of the entire height.
scrollTarget.X = 0;
scrollTarget.Y = (SHORT)(0 - csbi.dwSize.Y);
// Fill with empty spaces with the buffer's default text attribute.
fill.Char.UnicodeChar = TEXT(' ');
fill.Attributes = csbi.wAttributes;
// Do the scroll
ScrollConsoleScreenBuffer(hConsole, &scrollRect, NULL, scrollTarget, &fill);
return 0;
}
int main(){
bool exit = false;
// clear the console
ClearConsole();
// main loop
while(!exit){
// Move the cursor to the top left corner too.
csbi.dwCursorPosition.X = 0;
csbi.dwCursorPosition.Y = 0;
SetConsoleCursorPosition(hConsole, csbi.dwCursorPosition);
// prints the local time each on iteration of loop
GetTime();
// user options
cout << "\npress (1) enter file name - saved file name is: " << SavedFileName;
cout << "\npress (9) to exit." << endl;
// test user options
if(is1keypressed()){
cout << "option 1 - press ENTER to proceed " << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
bool validentry = false;
while(!validentry){
cout << "Enter a file name, max 19 characters: ";
cin.getline(SavedFileName, 20, '\n');
if(cin.fail()){
validentry = false;
cout << endl << "File name is too large, enter 19 characters or less. " << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}else
validentry = true;
}
ClearConsole();
}
exit = is9keypressed();
}
cout << "\n end program" << endl;
return 0;
}
I have experimented with cin.ignore, this leaves the user to press Enter to proceed. Without cin.ignore the user has to delete the "option" characters themselves.
Okay, Windows console stuff. Only works on Windows.
Playing with this stuff is never as easy as advertised, but for what you want to do, we can make some pretty stripped-down Windows-only code. (If, at some future point, you wish to port to Linux, the design here can be pretty easily swapped-out for similar code on Linux.)
Minimal Windows Console Functions
#include <ciso646>
#include <utility>
#include <windows.h>
namespace console
{
inline auto hStdIn () { return GetStdHandle( STD_INPUT_HANDLE ); }
inline auto hStdOut() { return GetStdHandle( STD_OUTPUT_HANDLE ); }
auto CSBI()
{
CONSOLE_SCREEN_BUFFER_INFO _csbi;
GetConsoleScreenBufferInfo( hStdOut(), &_csbi );
return _csbi;
}
void clear_screen()
// Function
// Clears the screen to blanks using the current text color and puts the
// cursor in the upper-left cell.
{
DWORD _count;
DWORD _cell_count;
COORD _home_coords = { 0, 0 };
auto _csbi = CSBI();
_cell_count = _csbi.dwSize.X * _csbi.dwSize.Y;
if (!FillConsoleOutputCharacter( hStdOut(), (TCHAR)' ', _cell_count, _home_coords, &_count )) return;
if (!FillConsoleOutputAttribute( hStdOut(), _csbi.wAttributes, _cell_count, _home_coords, &_count )) return;
SetConsoleCursorPosition( hStdOut(), _home_coords );
}
void goto_xy( int x, int y )
// Function
// Position the text cursor on the screen.
//
// Arguments
// x - column coordinate, starting at zero from the left.
// y - row coordinate, starting at zero from the top.
{
COORD _coords = { (SHORT)x, (SHORT)y };
SetConsoleCursorPosition( hStdOut(), _coords );
}
bool is_key_pressed( int timeout_ms = 0 )
// Function
// Wait for key input to become available.
//
// Argument
// The maxiumum amount of time to wait, expressed in milliseconds.
// May also be one of the following:
// 0 - return immediately.
// -1 - wait indefinitely.
//
// Returns
// true : If key input is available
// false : If not
{
auto is_key_event_waiting = []()
{
DWORD _n;
INPUT_RECORD _rec;
while (PeekConsoleInputW( hStdIn(), &_rec, 1, &_n) and _n)
{
if (_rec.EventType != KEY_EVENT) continue;
if (_rec.Event.KeyEvent.bKeyDown) return true;
if ((_rec.Event.KeyEvent.wVirtualKeyCode == VK_MENU) and _rec.Event.KeyEvent.uChar.UnicodeChar)
return true;
ReadConsoleInputW( hStdIn(), &_rec, 1, &_n );
}
return false;
};
// Get rid of all unwanted events
if (is_key_event_waiting()) return true;
// Else wait until event available or timeout
if (WaitForSingleObject( hStdIn(), (DWORD)timeout_ms ) != WAIT_OBJECT_0) return false;
// We only want key events
return is_key_event_waiting();
}
char32_t read_key()
// Function
// Wait for and return the next key input available.
//
// Returns
// 0 : next read_key() returns a Windows Virtual Key Code, like `VK_F1` or `VK_UP`
// Unicode code point : any normal input key
{
static char32_t _vkey = 0;
if (_vkey) return std::exchange( _vkey, 0 );
DWORD _n;
INPUT_RECORD _rec;
while (true)
{
ReadConsoleInputW( hStdIn(), &_rec, 1, &_n );
if (_rec.EventType == KEY_EVENT)
{
// Key release events for the ALT/MENU key where UnicodeChar != 0
// means an Alt-code was entered on the numeric keypad. We'll report that.
// All other key release events are ignored.
if (!_rec.Event.KeyEvent.bKeyDown)
if ((_rec.Event.KeyEvent.wVirtualKeyCode != VK_MENU) or !_rec.Event.KeyEvent.uChar.UnicodeChar)
continue;
// Normal Unicode code point
if (_rec.Event.KeyEvent.uChar.UnicodeChar)
return _rec.Event.KeyEvent.uChar.UnicodeChar;
// Special keys and function keys
_vkey = _rec.Event.KeyEvent.wVirtualKeyCode;
return 0;
}
}
}
} // namespace console
Notes:
It works on both the Windows Terminal and on both current and past versions of the Windows Console.
There is no initialization or finalization necessary to use this particular magic.
Assumes a human is present. More robust code would check that the standard streams are attached to a console/terminal at initialization and complain (and quit) if not.
Only one thread should ever be messing with the console, so if you are doing anything multi-threaded, only console:: from one of them, ever. (This is pretty much true of any user-application UI.)
Your program, updated
#include <ctime>
#include <iostream>
#include <string>
std::string SavedFileName;
void DrawMenu(){
console::clear_screen();
console::goto_xy(0, 1);
std::cout << "\npress (1) enter file name - saved file name is: " << SavedFileName;
std::cout << "\npress (9) to exit.\n";
}
void DrawTime(){
char buffer [20];
time_t now = time(NULL);
strftime(buffer , 20, "%H:%M:%S %d/%m/%Y", localtime(&now));
console::goto_xy(0, 0);
std::cout << "local time: " << buffer << " ";
}
int main(){
DrawMenu();
bool done = false;
while(!done){
DrawTime();
if (console::is_key_pressed(500))
switch (console::read_key()){
case '1':
console::goto_xy(0, 5);
std::cout << "option 1\nEnter a file name: ";
getline(std::cin, SavedFileName);
DrawMenu();
break;
case '9':
console::goto_xy(0, 5);
std::cout << "option 2\nReally quit (y/[n])? ";
{
auto c = console::read_key();
if (!c) console::read_key();
else done = (c == 'y') or (c == 'Y');
}
if (!done) DrawMenu();
break;
case 0:
// Ignore special and function keys
console::read_key();
break;
}
}
std::cout << "\n\nGood-bye\n";
}
Notes:
I tend to alphabetize #includes, which makes it easier when the list gets long. Also, <chrono> is for time-based C++ functions, but you use the C date-time stuff from <ctime>. Be careful to include the correct headers.
It is a good idea to get used to writing std:: in front of everything. It is generally a bad idea to simply dump the entire Standard Library into your program’s namespace[citation needed].
Strings in C++ are, in general, best handled with the std::string class. Please use it. It’ll make your life infinitely easier.
The Draw*() functions are carefully organized to work together. This requires you to have a good idea of how your output is going to look.
DrawMenu() clears the screen and draws only the menu options
DrawTime() only draws the time
We do not clear the display every frame. Clearing the console is an expensive operation, often producing a noticeable blink. Avoid it if possible. In our case, we only want to update the time every frame, and redraw everything only after we have finished with some user interaction.
The frame rate is set by how long we are willing to wait for input to be available. In this example I choose 500 ms, which is a reasonably snappy response time. Avoid going below 150–250 ms, as that bogs the processor down with your wait loop. (This is why we don’t use _kbhit() from <conio.h>.)
Be careful with names. The way you name things makes a difference.
DrawTime() is a better name than GetTime() because it better informs you what it is doing — drawing the time for the user. It doesn’t actually get (and return) the time (to the caller).One way to think about it is that the fact that getting the time is a subtask of showing the time.
done is a better name than exit because it is a nominative state, not an action. (And also because it does not clash with the Standard Library exit() function name.)
The general rule of thumb is to use nouns for objects and verbs for functions.
Commentary should only exist to clarify code, not to describe it. For example, commentary in the console:: namespace describes how user functions are to be used. The only commentary in the main program reminds us why we are calling console::get_key() again, which is otherwise non-obvious.
Compiling and running the code is as easy as concatenating the two code-blocks together and running it through the compiler.
MSVC
cl /EHsc /W4 /Ox /std=c++17 example.cpp /D_CRT_SECURE_NO_WARNINGS
Clang (or MinGW)
clang++ -Wall -Wextra -Werror -pedantic-errors -O3 -std=c++17 example.cpp -o example.exe -D_CRT_SECURE_NO_WARNINGS

C++ code reduction for identical submenus

I am coding my way to the last project of the semester and I have a code duplication issue.
I am using ncurses or pdcurses to make a menu to interact with the user.
The problem: For each choice of the menu(Five in total) I need a submenu. The submenu's only difference from the main menu is, the array of Items to be printed, and the parameters that go into some functions, as a result of the Items array size. Since I need five submenus, I need five times the same code(six if you add the main menu).
Could any of you help me make a function that does the same thing, which I'll then call six times to create my menu?
Here's my code
void Menu(){
const char* Items[]={
"[1]...New tax declaration",
"[2]...Modify tax declaration",
"[3]...Cancel tax declaration",
"[4]...Additional Information",
"[5]...Exit"
};
int Cur=0;
int ch, i;
int flag=0;
do{
werase(wm);
mvwaddstr(wm, 2, 16, "MENU");
for(int i=0; i<5;i++){
if(i==Cur)
wattr_on(wm, A_REVERSE, 0);
mvwaddstr(wm, 4+i, 4, Items[i]);
wattr_off(wm, A_REVERSE, 0);
}
mvwaddstr(wm, 14, 3, "Choice: ");
wprintw(wm, "%1d", Cur+1);
wrefresh(wm);
ch=wgetch(wm);
switch(ch){
case '1':Cur=0;Sub2();break;
case '2':Cur=1;Sub1();break;
case '3':Cur=2;break;
case '4':Cur=3;break;
case '5':flag=1;break;
case KEY_UP:
case KEY_LEFT: Cur--; if (Cur<0) Cur=4; break;
case KEY_DOWN:
case KEY_RIGHT: Cur++; if(Cur>4) Cur=0; break;
case 27: flag=1; break;
case 32:
case 13:
switch (Cur){
case 0:Sub2();break;
case 1:Sub1();break;
case 2:break;
case 3:break;
case 4:flag=1;break;
}
}
}while(!flag);
}
Thank you.
p.s The code is from a book. I have little experience with ncurses
A simple menu-driven program. This is based on using std::map instead of conditional logic. This map stores a list of menuitem structures that define what the menu looks like and what each option does.
This is best explained as we work through the code, so let's dive in!
// headers for everything used in this example
#include <iostream>
#include <string>
#include <map>
#include <functional>
#include <cctype>
// function to perform menu option B sub option 1
void optionB1()
{
std::cout << "perfoming B1" << std::endl;
}
// function to perform menu option B sub option 2
void optionB2()
{
std::cout << "perfoming B2" << std::endl;
}
// function to perform menu option A
void optionA()
{
std::cout << "perfoming A" << std::endl;
}
// defines a menu item. Good naming can often save the need to comment
struct menuitem
{
std::function<void()> doIt; // function to run if option chosen
std::string description; // pretty message describing option
};
// draw menu and wait for the user to select an option.
void domenu(const std::map<char, menuitem> & menu)
{
while (true) // loop until user gives a good option. Or use a retry count.
// You decide.
{
for (auto &items : menu)
{ // for all items in the menu, print out the item and it's description text
// for what first and second mean, read up on std::map and std::pair
std::cout << items.first << ") " << items.second.description << std::endl;
}
char ch;
std::cin >> ch; // get the user's choice
// often you may want to eliminate one of the cases to reduce the amount
// of possible inputs you need to provide handling code for.
// the line below allows us to use the same code for input of A and a.
ch = std::tolower(ch); // convert input to lower case
try
{
menu.at(ch).doIt(); // call the function mapped to user's choice.
// this may do produce something or it may
// display another menu. It could end the wor--
return; // done.
}
catch (...)
{ // print error message on unsupported input
std::cout << "Error. Invalid option!" << std::endl;
}
}
}
// the B menu
std::map<char, menuitem> bmenu
{ // User input doIt function Description
{'1', {optionB1, "Option B1"}},
{'2', {optionB2, "Option B2"}}
// add more options here. Or don't. Up to you.
};
// the main menu
std::map<char, menuitem> mainmenu
{ // User input doIt function Description
{'a', {optionA, "Option A"}},
{'b', {std::bind(domenu, bmenu), "Option B"}}
// OK, so that last one was a bit weird. std::bind makes a function and
// specifies the arguments with which it will be called. This takes
// domenu binds it with bmenu so that std::function<void()> is
// satisfied. As far as the world is concerned, the bound function
// returns nothing and takes no parameters. Very complicated functions
// can be bound so long as the end result returns nothing and requires
// no parameters.
// what it's doing here is allowing us to call domenu to draw the B
// submenu, wait for valid input, and call the chosen function.
};
// good 'ol trusty main so we can test that the above code isn't utter BS.
int main()
{
while (true) // loop forever. Or use whatever exit logic is required.
{
domenu(mainmenu); // kick-start by calling do menu to run the main menu
}
return(0);
}
This will keep the code down to a minimum. All of the duplicated code is reduced to the domenu function and a smurfload of code hidden from sight in the standard library and written by folks who likely have far more experience in getting this stuff right than you or I. Whenever possible, stand on the shoulders of giants.
domenu is driven by lists of options and execution instructions for the option. Want another option? Add an item to a list and possibly provide a new function to fulfill the obligations of that option.
All you have to do is fill in the blanks.

How to make a function change a global array permanently

Recently I've been working on an inventory system for a text-based game that uses a global array for the inventory system and a corresponding function to read true or false in said array. The problem I've run into is this, the function I'm using to modify the array
void playerGet(bool items[], int itemNumber) //this function takes an assigned argument of the array indices variable, and changes that array indices from true, to false.
{
items[itemNumber] = true;
}
only modifies the array within the scope of the function its housed in. The array is defined in a .cpp file like this:
void inventoryArray(bool items[]) //This function establishes all the items in the game, the true false statement expresses whether or not the item is in the player's inventory.
{
items[WEAPON_RELIC_RIFLE] = false;
items[WEAPON_SCALPEL] = false;
items[MISC_ACTION_FIGURE] = false;
items[MISC_FIRE_EXTINGUISHER] = false;
items[MISC_LIFE_RAFT] = false;
}
and is then declared in a .h file like this:
void inventoryArray(bool items[]);
the enums used in the array are defined in a header file like this:
enum equipment //This declares a list of enums for each item in the game, consumables, not included.
{
WEAPON_RELIC_RIFLE, // = 0
WEAPON_SCALPEL, // = 1
MISC_ACTION_FIGURE, // = 2
MISC_FIRE_EXTINGUISHER, // = 3
MISC_LIFE_RAFT, // = 4
MAX_EQUIPMENT
};
the function that reads the inventory array is this:
void twoScavengerCombat(bool items[])
{
for (int item = 0; item < MAX_EQUIPMENT; ++item)
{
if (items[item] == true) //if true proceed
{
switch (item)
{
case 0: //if array indices identifier = 0, print relic rifle
cout << "1: Use the Relic Rifle\n";
break;
case 1:
cout << "2: Use the Scalpel\n";
break;
case 2:
break;
case 3:
cout << "3: Use the Fire Extingusher\n";
break;
case 4:
cout << "4: Use the Life Raft\n";
break;
default:
cout << "Error";
break;
}
}
else
cout << "Option Unavailible\n"; //if false print Option Unavailible
}
compiled, with the array and enums headers declared the main file would look like this:
int toolSearch()
{
bool items[MAX_EQUIPMENT];
inventoryArray(items);
playerGet(items, 0);
}
void twoScavengerCombat(bool items[])\\ declared in this file, but since its just above here i left it as a forward declaration to save space
int main()
{
toolSearch();
twoScavengerCombat(items);
return 0;
}
Ideally this would produce the result: Use Relic Rifle
Option Unavailable
Option Unavailable
Option Unavailable
Option Unavailable
but instead it produces 5 Option Unavailable's. What am I missing?
You would want
//bunch of #include<> directives
bool items[MAX_EQUIPMENT];
int toolSearch()
{
inventoryArray();
playerGet( 0);
}
void twoScavengerCombat()
...
// other functions here
int main()
{
toolSearch();
twoScavengerCombat();
return 0;
}
Note that bool items[MAX_EQUIPMENT]; is not defined in a function. It is off on it's own at the top of the file in plain view of anything defined below it. This is what it means to be global. Anyone and everyone can access it, if they know where it is or you tell them where it is with an extern statement. It is created when the program starts (even before main and that can cause some really fun debugging if the initialization logic of the variable is faulty) and dies only when the program does.
Lightness Races in Orbit delves a bit deeper here, but is more concerned with making a global variable extend past a single file
There is no need to pass items into any function because everyone can see items The downside is there is one and only one items so if you have multiple players each with different item lists, you're going to have problems.
You might want to look into std::vector (resizable array) and std::map (which will allow you to look items up by name items["sword"].attackFoe(foe);) and std::set (which makes it really easy to see what a player has (if (items.find("Vorpal Hand Grenade") != items.end()) BlowStuffUp();) rather than having to search through each item every time.

Why are my variables giving a different value when they are printed outside of the class I set them in?

I am making fairly new to C++ and I am using it to make a text based game for a school project. However during the first section of the game the player answers questions by entering the number shown beside the answer they choose. However when I tested the variables the input going to using std::cout they return different values depending on where they are outputted. If I outputted them in the class I am using to set them (Introduction) the they return the correct value such as 1 or 3 etc. However when I output them in any file other than Introduction.cpp, the value displayed is -858993460 for all of the values. I get the same result from Main.cpp when I call them in my main function and if I call them from another function in a different class to Introduction.
This is an example of some of the code used to get input from the user:
void Introduction::CharacterCreation()
{
Universal universal;
std::fstream creation("Introduction_CharacterCreation.txt");
universal.line = 5;
for (int i = 0; i < universal.line; i++)
{
if (i > 0)
{
std::getline(creation, universal.displayText);
std::cout << universal.displayText << std::endl;
}
if (i == 4)
{
std::cout << std::endl;
std::cin >> universal.gender;
while (universal.gender <= 0 || universal.gender >= 3)
{
std::cout << "Please make a valid choice" << std::endl;
std::cin >> universal.gender;
}
}
}
// Code cut out here
}
The gender variable is an int declared in the Universal class, and the user is prompted to enter 1 for male or 2 for female by text pulled from a separate file. If the input is not 1 or 2 then a while loop forces the player to keep re-answering the question until they enter 1 or 2. The line variable is also an int however that is used for the for loops to ensure the right lines are read by the program.
To output the gender variable this is the code I use:
std::cout << gender << std::endl;
There is no universal. as it is being called within the Universal class itself.
This has confused me massively and I can't get my head around what is causing the problem. Any help or explanation would be great, thanks in advance.
Short answer: you're declaring a Universal object in your CharacterCreation() method. When this function exits since the scope of the universal variable was local so the entire object is destroyed.
Whatever you are outputting is just uninitialized garbage. It could really be any number depending on what system is compiling / running the program. To test this right after you input the gender, while still inside the function, try running
std::cout << universal.gender << std::endl;
This should output just fine.
There are a lot of ways you can go about fixing this. Since you didn't post where you call this method or your Universal class I can't say for sure. But I can speculate one such solution which is to declare the Universal object outside the method and then pass it in as a parameter:
Universal universal = Universal();
Introduction::CharacterCreation(universal);
std::cout << universal.gender << std::endl;
And just declare your function header to accept a Universal object:
void Introduction::CharacterCreation(Universal & universal)
{
//code here
}

Search vector of objects by object data member attribute

I'm writing a Jukebox simulator and I'm trying to search a vector of Album objects by album title and return the index for use in other functions. The function is to be used for a number of different things such as deleting an album, printing an album etc.
I have gotten it to work in a previous application when the function was in the same Class as the data member to search for. I can however for some reason not get it to work using getters. No matter what I input as search key the idx returns 3 although the vector only contains indexes 0, 1 and 2 (only 3 albums right now).
The lambda function seem to be able to access data by using the getAlbum()-getter but somehow the comparison doesn't work.
My approach might be entirely wrong and I'd be grateful for any pointers in the right direction, or suggestions on how to accomplish the desired result using some other technique.
int Jukebox::findAlbumIdx(string key)
{
// Get search predicate
auto it = find_if(albvec.begin(), albvec.end(), [key](Album const &a)
{
return (a.getAlbum() == key);
});
int idx = it - albvec.begin();
return idx;
}
void Jukebox::delAlbum()
{
cin.get();
string key;
cout << "Input title of album to delete: ";
getline(cin, key);
int idx = findAlbumIdx(key);
if(idx > albvec.size() - 1)
cout << "No hits." << endl;
else
albvec.erase(albvec.begin() + idx);
}
getAlbum is just a simple inline getter as such:
string getAlbum() const {return album_title;}
Following Jonathan Wakely's suggestion to add std::cout << a.getAlbum() << " == " << key << std::endl; in the lambda the output is this:
Input title of album to delete: Abbey Road
== Abbey Road
== Abbey Road
== Abbey RoadLonely Hearts Club Band
No hits.
Obviously the getter isn't actually getting much to use for comparison. Not sure why it only gets the last entry and on the right hand side of the comparison.
If I add this to any of the functions above it gets and displays the Album titles correctly. The vector seems to be fine just before calling findAlbumIdx(key); and also inside the findAlbumIdx function.
for(unsigned int i = 0; i < albvec.size(); ++i)
cout << albvec[i].getAlbum() << endl;
The original playlist file that is read into the vector to be searched had dos newlines, after converting it using dos2unix (since I'm running Linux) the search, and I presume a lot of other things, is working correctly.
I suppose trimming newline characters while reading the file into the vector would be the more correct approach.