C++ Erase-Remove Idiom not working properly? - c++

This post is a continuation on my post from yesterday: How to output a vector.
So I am in the process of writing a short program that will take a list of strings into a vector, export them to a file, view them before outputting them and lastly remove them if possible. I have managed to get all of the functions working properly except for case 3 (remove a book from the list). The errors I am getting in visual studio as follows:
1.) "No instance of the overloaded function "remove" matches the argument list. [LN 76]
2.) "'Remove': function does not take 2 arguments". [LN 76]
As you can probably tell, I am trying to remove by value instead of index. I am still learning here so be gentle, but why exactly am I getting these errors?
Here is my full code:
#include <iostream>
#include <cctype>
#include <ctype.h>
#include <algorithm>
#include <string>
#include <math.h>
#include <windows.h>
#include <iomanip>
#include <vector>
#include <iostream>
#include <sstream>
#include <fstream>
#include <istream>
// common namespace
using namespace std;
int main()
{
int option;
bool iWannaLeave = false;
vector<string> bookCollection;
string entryVal = " ";
int anotherOption;
do
{
cout << "Welcome to MyBook! - A text recorder framework application." << endl;
cout << "-----------------------------------------------------------" << endl;
cout << "Main Menu:" << endl;
cout << "1 - Add a book to the collection." << endl;
cout << "2 - Display all books currently in the collection." << endl;
cout << "3 - Remove books from the collection." << endl;
cout << "4 - Write stored collection to file." << endl;
cout << "5 - Quit" << endl;
cout << "Make your selection: ";
cin >> option;
cin.ignore();
switch (option)
{
case 1:
{
bool wannaMoreBooks = false;
// the next loop will execute at least one time so you could enter a book
do
{
wannaMoreBooks = false;
cout << "Add a book title to the collection: ";
getline(cin, entryVal);
bookCollection.push_back(entryVal);
cout << "Would you like to enter another book?(1 - yes, 0 - no): ";
cin >> anotherOption;
cin.ignore();
if (anotherOption == 1) wannaMoreBooks = true;
} while (wannaMoreBooks == true);
}
break;
case 2:
{
for (int i = 0; i < bookCollection.size(); i++)
cout << bookCollection[i] << " | ";
cout << endl;
break;
}
case 3:
{
string vecVal;
cout << "Enter the value you would like to remove: " << endl;
cin >> vecVal;
bookCollection.erase(remove(bookCollection.begin(), vecVal), bookCollection.end());
}
// remove a book from the collection
break;
case 4:
{
ofstream fileOut("Collection.txt");
fileOut << "Your MyBook Collection: [Begin] - | ";
auto first = true;
for (string x : bookCollection)
{
if (!first) { fileOut << " | "; }
first = false;
fileOut << x;
}
fileOut << " | - [End]" << endl;
cout << "Collection.txt has been successfully written." << endl;
break;
}
case 5:
{
//Nested IF to kill program properly
int quitVar;
cout << "Are you sure you want to exit the program?: ";
cin >> quitVar;
cin.ignore();
if (quitVar == 1)
{
cout << "The program will now be terminated." << endl;
iWannaLeave = true;
}
else if (quitVar == 0) cout << "Returning to the main menu." << endl;
}
break;
}
} while (iWannaLeave == false);
return 0;
}
I am aware that this is no where near perfect code so in addition to finding out why I am getting these errors I would also like some constructive criticism as to how I can improve.
Additionally: If I wanted to go about using functions in a header file as opposed to a switch, would I just move the case contents to a header file?
Thanks in advance! :)

Virtually all STL-functions take one or more pairs of iterators. Since you just pass begin, there is no viable overload. You need to call
remove(bookCollection.begin(), bookCollection.end(), vecVal)
It is always a good idea to check the reference, which typically also contains a basic usage example.

Related

Why isn't my condition working for my do/while loop? C++

I have my loop set up as seen below:
#include <iostream>
#include <string>
#include <vector>
#include "Agent.h"
#include "Sage.h"
#include "Sova.h"
#include "Reyna.h"
using namespace std;
int main() {
int choice;
vector <Agent*> v;
do
{
cout << "Choose an Agent to Reveal Agent Ability" << endl;
cout << "---------------------------------------" << endl;
cout << "1. Sage" << endl;
cout << "2. Sova" << endl;
cout << "3. Reyna" << endl;
cout << "4. Display All" << endl;
cout << "5. Quit" << endl;
cin >> choice;
switch (choice)
{
case 1:
v.push_back(new Sage("Healing"));
break;
case 2:
v.push_back(new Sova("Sight"));
break;
case 3:
v.push_back(new Reyna("Blinding"));
break;
case 4:
v.push_back(new Sage("Healing"));
v.push_back(new Sova("Sight"));
v.push_back(new Reyna("Blinding"));
break;
default:
cout << "Bad choice! Please try again later.\n";
}
} while (choice <=0 || choice >=5);
for (const auto &Agent : v){
Agent->action();
}
return 0;
}
My condition is while (choice <=0 || choice >=5)
However, when I run this, after I make a choice, the information is output onto the screen and then the program ends. I tried other conditions, but when I selected a choice, the program will loop but will not output any information.
Is this a problem with the position for my for loop?
for (const auto &Agent : v){
Agent->action();
}
Edit: Here's an example of the output I get when I use something like (choice != 5):
Choose an Agent to Reveal Agent Ability
---------------------------------------
1. Sage
2. Sova
3. Reyna
4. Display All
5. Quit
1
Choose an Agent to Reveal Agent Ability
---------------------------------------
1. Sage
2. Sova
3. Reyna
4. Display All
5. Quit
As you can see, it loops, but it does not display the output.
You have a couple problems. For one, you don't handle the "quit" case, it just keeps looping then because the loop condition choice >= 5 is satisfied. Also you do not handle input that is not an integer. Additionally, you have a name collision with Agent. Also, you have what could potentially become a memory leak by not deleting the Agents. I know the memory is recouped when the process exits anyway, but it's good practice to delete raw pointers even when you don't have to, and it's even better practice not to use raw pointers in the first place, and to use smart pointers like shared_ptr instead, so the memory is automatically deleted when the objects go out of scope.
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include "Agent.h"
#include "Sage.h"
#include "Sova.h"
#include "Reyna.h"
using namespace std;
int main() {
int choice;
vector <std::shared_ptr<Agent> > v;
do
{
cout << "Choose an Agent to Reveal Agent Ability" << endl;
cout << "---------------------------------------" << endl;
cout << "1. Sage" << endl;
cout << "2. Sova" << endl;
cout << "3. Reyna" << endl;
cout << "4. Display All" << endl;
cout << "5. Quit" << endl;
if (!(cin >> choice)) {
std::cout << "Please enter an integer." << std::endl;
choice = -1; // so the loop condition is true and we reloop
continue;
}
switch (choice)
{
case 1:
v.push_back(std::make_shared<Sage>("Healing"));
break;
case 2:
v.push_back(std::make_shared<Sova>("Sight"));
break;
case 3:
v.push_back(std::make_shared<Revna>("Blinding"));
break;
case 4:
v.push_back(std::make_shared<Sage>("Healing"));
v.push_back(std::make_shared<Sova>("Sight"));
v.push_back(std::make_shared<Reyna>("Blinding"));
break;
case 5:
std::cout << "Quitting." << std::endl;
return 0;
break;
default:
cout << "Bad choice! Please try again later.\n";
}
} while (choice < 1 || choice > 5);
for (const auto & agent_ptr : v){
agent_ptr->action();
}
return 0;
}
Good practice is also to not use using namespace std;. See: Why is "using namespace std;" considered bad practice? . You can see where I edited your code, I added in std:: out of force of habit.

C++ FileIO to read and write into objects variables

I'm trying to make a small library system where the user can add new book details(name, author & price). When implementing the FileIO system to read from a file the details of every book using the getline functions it becomes harder to separate my variables when I try to store them in temporary variables.
Ex:
"No Is a four letter word,Chris Jericho,17.67,"
"Harry Potter,JK Rowling,23.98,"
PS:Is there a better solution than adding the comma?
I tried to add a ',' character to separate every my strings but I need a better and more efficient solution that works with the getLine function.
int main(){
vector<Book> library;
//----Loading File Data_START
string line;
int nbArg=0;
string tempName, tempAuthor, tempPrice;
ifstream myfileL("List.txt");
if (myfileL.is_open())
{
while (getline(myfileL, line))
{
tempPrice=tempAuthor = tempName = "";
for (int j = 0; j < line.size(); j++){
if (line.at(j) == ','){
nbArg++;
}
else{
switch (nbArg){
case 0:
tempName += (line.at(j));
break;
case 1:
tempPrice += (line.at(j));
break;
case 2:
tempAuthor += (line.at(j));
break;
}
}
}
cout << tempName << endl << tempAuthor << endl << tempPrice << endl;
cout << "End of Line"<< endl;
nbArg = 0;
}
cout << "---------------------------" << endl;
myfileL.close();
}
else cout << "Unable to open file";
//----Loading File Data_END
char inputKey = 's';
cout << "-----------------WELCOME----------------" << endl;
while (inputKey != 'q')
{
cout << "---------------------------------------" << endl;
cout << "Click \"1\" to add a book to your library" << endl;
cout << "Click \"2\" to show how the number of books your possess" << endl;
cout << "Click \"3\" to show details about your books" << endl;
cout << "Click \"q\" to quit" << endl;
cin >> inputKey;
switch (inputKey)
{
case '1':
addElem(library);
break;
case '2':
cout << "You now own " << libSize(library) << " books !" << endl;
break;
case '3':
showDetails(library);
break;
case 'q':
cout << "GOODBYE!" << endl;
Sleep(2000);
break;
}
}
return 0;
}
Use specialized file format, which can contain structure (for example json, xml). This will prevent many, many problems.
If you can't, then add a separator character (just like you did), but pick one, that has least chances to occur in real strings. So for example end of line (every variable would go on it's own line) or \0 or \t.
Here is a solution on a smooth way to load the library. You can let the BookList.txt file be delimited by TABs \t between Name, Author and Price instead of a comma , and then use getline() to separate with a TAB as in my example below.
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
const char TAB = '\t';
struct StructBook {
string Name, Author;
double Price;
};
vector<StructBook> Library;
void GetBookDetails(string LineBookDetails, StructBook & Book) {
string string_Price;
stringstream StringStreamBookDetails(LineBookDetails);
getline(StringStreamBookDetails, Book.Name, TAB);
getline(StringStreamBookDetails, Book.Author, TAB);
getline(StringStreamBookDetails, string_Price, TAB);
stringstream(string_Price) >> Book.Price;
}
bool LoadLibrary() {
ifstream FileBookList("BookList.txt");
if(FileBookList.is_open()) {
string LineBookDetails;
StructBook Book;
while(getline(FileBookList, LineBookDetails)) {
GetBookDetails(LineBookDetails, Book);
Library.push_back(Book);
cout << Book.Name << ", " << Book.Author << ", " << Book.Price << endl;
}
FileBookList.close();
return true;
} else {
return false;
}
}
int main(){
if(!LoadLibrary())
cout << "Error: Unable to load library";
//Rest of your program goes here
return 0;
}
Your BookList.txt should look like this:
No Is a four letter word Chris Jericho 17.67
Harry Potter JK Rowling 23.98

Error expected constructor, destructor, or type conversion before '(' token

I have searched a ton of threads and cannot find a solution to this error. It occurs on line 8.
The BranchStaff.cpp file is as follows. It acts as a parent class for another class.
#include "BranchStaff.h"
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
BranchStaff::BranchStaff(userIDIn, passwordIn)
:userID(userIDIn), password(passwordIn)
{
menuChoice = 0;
over = false;
while (!over) {
cout << "=======================================================" << endl;
cout << "| Teller Terminal System - Branch Staff |" << endl;
cout << "=======================================================" << endl;
cout << "1) Client and Account Management" << endl;
cout << "2) Change password" << endl;
cout << "3) Exit"
cout << "\tPlease choose an option: ";
cin >> menuChoice;
while (menuChoice != 3 && menuChoice != 2 && menuChoice != 1) {
cout << "\tPlease enter a valid option: " << endl;
cin >> menuChoice;
}
switch (menuChoice) {
case 1:
clientManagement()
break;
case 2:
passwordChange()
break;
case 3:
exit();
}
}
}
void BranchStaff::changePassword() {
}
void BranchStaff::clientManagement() {
}
The .h file is as follows
#ifndef BRANCHSTAFF_H
#define BRANCHSTAFF_H
#include <string>
#include <iostream>
using namespace std;
class BranchStaff
{
public:
BranchStaff();
BranchStaff(string userIDIn, string passwordIn);
protected:
void clientManagement();
void changePassword();
private:
string userID;
string password;
int menuChoice;
bool over;
};
#endif // BRANCHSTAFF_H
Possibly due to not including data types in the implementation. Try
BranchStaff::BranchStaff(string userIDIn, string passwordIn)
I would also suggest passing in the strings by reference as using them in the initialization list should copy them.
BranchStaff::BranchStaff(const string& userIDIn, const string& passwordIn)
BranchStaff::BranchStaff(string userIDIn, string passwordIn)

My function is being seemingly being skipped for no reason

Attempting a basic C++ challenge, (beginner at C++) and I produced this code. I understand that calling a value in an array starts from zero but I wanted the user to type from 1-5 instead of 0-4 because I didn't want to go the easy route and wanted to see if I could do it.
Here is my problem, I made a basic function to subtract 1 from the int choice to allow the user to enter 1-5 but the array see the value as 1-4. However as shown in this image it seems to ignore my function and skip to the next part of the code. I have included my code below.
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;
string drink[5] = { "Coke", "Water", "Sprite", "Monster", "Diet Coke" };
int choice;
int correct = 0;
void AdjArray()
{
choice--;
};
int main()
{
while (correct != 1)
{
cout << "Enter the number of the beverage you would like." << endl;
cout
<< " Coke = 1\n Water = 2\n Sprite = 3\n Monster = 4\n Diet Coke = 5"
<< endl;
cin >> choice;
AdjArray;
if (choice >= 0 && choice <= 4)
{
cout << "You have chosen " << drink[choice] << "." << endl;
correct = 1;
}
else
{
system("cls");
cout << "Error, you entered: " << choice
<< ". Please enter a number between 1 and 5.\n" << endl;
}
}
return 0;
}
You're not calling your function. Change AdjArray; to AdjArray();

C++ reading/writing binary mode

I'm learning C++ at school and in my opinion it's a beautiful language, but I have this annoying problem. In the text book it's written with FILE *text and scanf and printf, and I personally don't like it; I got used to cin and cout or with the << >> better say with the fstream.
So here is my problem:
I have to make an application that writes data in binary mode (I have done it on half of it but it doesn't write in binary mode for some reason)
After I write the city (orasul) the coordinates (x and y) I have to search for them and get those values. (Here I tried to use string.find) but I have to use seekg to search in "binary mode" and get those values separate in a structure.
If you guys can guide me somehow cause I am pretty lost here. And is there a way I can get the sizeof(struct) ?
#include <iostream>
#include <conio.h>
#include <fstream>
#include <string>
#include <limits>
using namespace std;
struct oras {
std::string orasul;
int x;
int y;
} ora;
void functiaPrincipala();
void calculator(float coordonate_x1, float coordonate_y1, float coordonate_x2, float coordonate_y2);
void adaugaOras();
void stergeLocatie();
void repetare();
void main() {
functiaPrincipala();
}
void functiaPrincipala() {
// variabile
int obtiune;
// ofstream fisierOut;
// ifstream fisierIn;
cout << "1) Adauga localitate: " << endl;
cout << "2) Stergerea unei localitati existente: " << endl;
cout << "3) Stergerea tuturor localitatilor existente: " << endl;
cout << "4) Afisarea tuturor localitatilor existente: " << endl;
cout << "5) Calculul distantei a doua localitati: " << endl;
cout << "Introduceti obtiunea: " << endl;
cin >> obtiune;
switch (obtiune) {
case 1:
adaugaOras();
break;
case 2:
stergeLocatie();
break;
case 3:
break;
case 4:
break;
case 5:
break;
}
getch();
}
void calculator(float coordonate_x1, float coordonate_y1, float coordonate_x2, float coordonate_y2) {
float rezultat;
rezultat = sqrt((coordonate_x2 * coordonate_x1) - (coordonate_x2 * coordonate_x1) + (coordonate_y2 * coordonate_y1) - (coordonate_y2 * coordonate_y1));
cout << "Distanta de la orasul 1 la orasul 2 este de: " << rezultat;
}
void adaugaOras() {
int n;
ofstream fisierOutt("textttt.txt", ios::app | ios::binary);
// fisierOutt.open("textttt.txt");
cout << "Cate orase doresti sa introduci: ";
cin >> n;
if (fisierOutt.is_open()) {
for (int i = 0; i < n; i++) {
cout << "Introdu numele orasului: ";
cin >> ora.orasul;
cout << "Introdu coordonatele x: ";
cin >> ora.x;
cout << "Introdu coordonatele y: ";
cin >> ora.y;
fisierOutt << ora.orasul << " " << ora.x << " " << ora.y << endl;
cout << endl << endl;
}
} else {
cout << "Nu am putut deschide fisierul";
}
fisierOutt.close();
cout << endl;
// repetare();
}
void stergeLocatie() {
}
void repetare() {
char obtiune;
cout << "Doriti sa mai adaugati ceva sau sa iesiti?(d/n)";
cin >> obtiune;
if (obtiune == 'd') {
functiaPrincipala();
} else {
exit;
}
}
Like I said in my comment, you can't really seek to a specific entry as all entries are of different sizes.
It can be solved by having a separate index file, where each index entry contains of the position of the entry in the real file. This way, when you need entry X you first seek in the index file to the correct position, read the position, and then use that to seek in the real data file.
This is how many DBM database-managers handle their data.
The entries in the index-file has to be fixed size, for example each entry in the index is of type std::ostream::pos_type, and you use write to write the index, and read to read it.
Check https://stackoverflow.com/a/15452958/2156678 . There asker wanted a way to do search (and update) on binary file and I proposed a simple way to achieve the same.