Search code examples
c++command-line-interfacefstream

Simple choice function does not seem to work (C++)


I have been working on an exercise in my c++ programming book and it basically is asking me to create a program that will take 3 files, an old master file of client data (account, name, balance), a transaction file (of account names and credits or debits), and then a function to merge this files and output a new master file of client data. I am pretty sure I know how to do this, or even if I don't I think I can figure it out, the problem I am having is with a simple choice selection interface which is implemented exactly the same as another program that works just fine. When I first run the program it will let me input a choice (int) and then run the appropriate function (of which I only have 1 implemented completely, that being the createMasterFile function. After creating entries in the file and having them read back to the screen just for debugging and verification purposees, it will return to the while statement below but if I watch the choice function in both the main function and the enterChoice() function when I make the first selection (the one that works) those variables (according to gdb) do not take on any values until AFTER createMasterFile runs, before that it states that it is not in scope, after it runs through once choice will take on the original value 1 as it hits while, then seems to skip the condition of the while statement, then hits the switch statement which somehow changes choice to 0 which then hits the default case which then returns to the while statement and choice takes on a value of 32767 and again seems to skip the conditional part of the while statement and gets stuck in an infinite loop at this point with choice never changing in value. In a very similar program (the only difference is that the program that works uses binary files instead of sequential files) this works just fine. The interface looks like this:

enum Choices {CREATE_MASTER =1, CREATE_TRANSACTION, SORT_MASTER, UPDATE_MASTER, SORT_TRANS, UPDATE_TRANS, MERGE, END  };

int choice;

while ( ( choice = enterChoice()) != END )
{
    switch( choice )
    {
        case CREATE_MASTER:
            createMasterFile( inOutOldMasterFile );
            break;
        case CREATE_TRANSACTION:
            break;
        case SORT_MASTER:
            break;
        case UPDATE_MASTER:
            break;
        case SORT_TRANS:
            break;
        case UPDATE_TRANS:
            break;
        case MERGE:
            break;
        default:
            cerr << "Incorrect choice" << endl;
            break;
    }
}

and the enterchoice() function is:

int enterChoice()
{
cout << "\nEnter your choice below" << endl
    << "1 - Create Master Record" << endl
    << "2 - Create Transaction Record" << endl
    << "3 - Sort Master File In Descending Order" << endl
    << "4 - Update Master File" << endl
    << "5 - Sort Transaction File" << endl
    << "6 - Update Transaction File" << endl
    << "7 - Merge Transaction and Old Master File" << endl
    << "8 - End Program\n?";

    int choice;
    cin >> choice;
    return choice;
}

If you need to see all the code (just for clarity) here it is (for the functions that are currently being used)

#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <cstdlib>
using namespace std;

void outputLine( int, const string, double );
void createMasterFile( fstream & );
int numberOfAccounts = 0;
void swap(int * const, int * const);
void swap( string * const, string * const);
void swap( double * const, double * const);
bool descending( int, int);
void sortAccounts(int, fstream &, bool (*)(int, int));
int enterChoice();

int main()
{
// create a fstream object for input and output to be passed to
// the functions that need to manipulate it.
fstream inOutOldMasterFile( "oldmaster.dat", ios::in | ios::out );
fstream inOutTransaction( "trans.dat", ios::in | ios::out );
ofstream outNewMaster( "newmaster.dat", ios::out);

enum Choices {CREATE_MASTER =1, CREATE_TRANSACTION, SORT_MASTER, UPDATE_MASTER, SORT_TRANS, UPDATE_TRANS, MERGE, END  };

int choice;

while ( ( choice = enterChoice()) != END )
{
    switch( choice )
    {
        case CREATE_MASTER:
            createMasterFile( inOutOldMasterFile );
            break;
        case CREATE_TRANSACTION:
            break;
        case SORT_MASTER:
            break;
        case UPDATE_MASTER:
            break;
        case SORT_TRANS:
            break;
        case UPDATE_TRANS:
            break;
        case MERGE:
            break;
        default:
            cerr << "Incorrect choice" << endl;
            break;
    }
}
return 0;
}

int enterChoice()
{
cout << "\nEnter your choice below" << endl
    << "1 - Create Master Record" << endl
    << "2 - Create Transaction Record" << endl
    << "3 - Sort Master File In Descending Order" << endl
    << "4 - Update Master File" << endl
    << "5 - Sort Transaction File" << endl
    << "6 - Update Transaction File" << endl
    << "7 - Merge Transaction and Old Master File" << endl
    << "8 - End Program\n?";

    int choice;
    cin >> choice;
    return choice;
}

void createMasterFile( fstream &clientFile )
{
if(!clientFile)
{
    clientFile.open("oldmaster.dat", ios::in | ios::out);
}

cout << "Enter the account, name and balance." << endl
    << "Enter end-of-file to end input (^d)\n? ";

int account;
string name;
double balance;

clientFile.seekp( 0, ios::beg );

// read account, name and balance from cin then place in file
while (cin >> account >> name >> balance )
{
    numberOfAccounts++;
    clientFile << account << ' ' << name << ' ' << balance << endl;

    cout << "?";
} //end while

clientFile.seekg( 0, ios::beg );

cout << left << setw(10) << "Account" << setw(13)
    << "Name" << "Balance" << endl << fixed << showpoint;

while( clientFile >> account >> name >> balance)
{
    outputLine( account, name, balance );
}

clientFile.close();
}

If this is really obvious I am sorry but I have tried to find some answer for almost 2 days now searching online.

Thanks in advance.

-Bobby


Solution

  • It seems your createMasterFile() reads from std::cin until std::cin goes into failure mode, e.g., because a wrong value was encountered or std::cin reached its end. This function correctly checks the input after reading.

    After that, your enterChoice() function is entered without first clearing the failure setting for std::cin and this function doesn't check whether the read is actually successful and it won't because the stream is already in failure mode. The function should probably read the value and return a result something like this:

    std::cin >> choice;
    return std::cin? choice: ERROR;
    

    Since createMasterFile() clearly leaves std::cin in failure mode, you should probably reset its state before trying to read your choice and probably also ignore the rest of the current line:

    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    

    Of course, if there is no further input from std::cin, reading a choice will still fail.