redcap
redcap

Reputation: 53

About file input/ouput and exception in C++

I am reading Jesse Liberty's SAMS Teach Yourself C++ in 10 Minutes and testing the examples in the book. I find that there are some bugs in the code about file i/o but I don't know where they are. The book does not mention anything about them. I think the problem is caused by fstream and catch(...) but I don't know how to fix it. So I would like to seek help from experienced C++ users here to help me.

Problems:

I don't know why the program always call ErrorHandling::handleNotANumberError(); when the input file is empty.

Moreover, the program has more errors when the input file does not exist. Exceptions are caught and basic_ios::clear is read. Why are there errors?

When I delete the codes about exceptions, it can work well when the file does not exist.

And when I input from an empty file, the message "invalid operator" is present.

I think that invalid operator is the eof character. Is it?

I have searched in Google but still cannot figure out what is happening. I know the following code is a bit long but I really want to know what happens.

The major content in main.cpp :

ifstream tapeInputStream; //global variable for callback

char getOperator()
{
    char operatorInput;

    if
    (
        tapeInputStream.is_open() &&
        ( !tapeInputStream.eof() )
    )
    {
        tapeInputStream >> operatorInput;
    }
    else
    {
        cin >> operatorInput;
    }

    return operatorInput;
}

float getOperand()
{
    float operandInput = 1;

    if
    (
        tapeInputStream.is_open() &&
        ( !tapeInputStream.eof() )
    )
    {
        tapeInputStream >> operandInput;
    }
    else
    {
        cin >> operandInput;
    }

    return operandInput;
}

void showResult( float theResult )
{
    cout << endl << "Result: " << theResult << endl;
}

int main( int argc, char* argv[] )
{
    if ( argc > 1 ) //a filename is present
    {
        try
        {
            tapeInputStream.exceptions( tapeInputStream.failbit ); //cin or ofstream?
            tapeInputStream.open( argv[1], ios_base::in );
        }
        catch ( ios_base::failure &ioError )
        {
            ErrorHandling::handleInputStreamError( tapeInputStream, ioError );
            //the stream will be unopened but the failbit will not be set
        }
    }

    Calculator::aCalculatorExternalInterface CalculatorExternalInterface;

    CalculatorExternalInterface.getAnOperator = getOperator;

    CalculatorExternalInterface.getAnOperand = getOperand;

    CalculatorExternalInterface.showResult = showResult;

    int result = Calculator::calculateFromInput( CalculatorExternalInterface );

    tapeInputStream.close(); //close the file to make output stream in tape() possible

    Calculator::tape( '.', 0, argv[1] ); //stream and delete the tape

    return result;

}

Part of namespace Calculator in ExternalInterfaceModule.cpp :

bool nextCalculation
(
    const aCalculatorExternalInterface &theCalculatorExternalInterface
)
{
    char operatorInput = theCalculatorExternalInterface.getAnOperator();

    switch ( operatorInput )
    {
        case '.': //stop

            return true; //done will become true

        case '?': //show the tape

            tape( operatorInput );
            return false;

        case '=': case '@': //no operand sent to accumulator
        {
            anOperator operatorValue =
                ( operatorInput == '=' ? query : reset );

            float result = accumulator( operatorValue );

            if ( operatorValue == query )
            {
                theCalculatorExternalInterface.showResult( result );
            }

            return false;
        } //use curly brackets to define the scope
        case '+': case '-': case '*': case '/': //calculation
        {
            float number = theCalculatorExternalInterface.getAnOperand();

            anOperator operatorValue =
                operatorInput == '+' ? add :
                operatorInput == '-' ? subtract :
                operatorInput == '*' ? multiply :
                                       divide;

            accumulator( operatorValue, number );
            tape( operatorInput, number );

            return false;
        }
        case '!': //self-test

            selfTest();
            return false;

        default: //anythings else is an error

            throw runtime_error( "Error: Invalid operator.");

    }
}

int calculateFromInput
(
    aCalculatorExternalInterface &theCalculatorExternalInterface
)
{
    ErrorHandling::initialise();

    bool done = false;

    do
    {
        try
        {
            done = nextCalculation( theCalculatorExternalInterface );
        }
        catch ( runtime_error runtimeError )
        {
            ErrorHandling::handleRuntimeError( runtimeError );
        }
        catch (...)
        {
            ErrorHandling::handleNotANumberError();
        }
    }
    while ( !done ); //continue when undone

    return 0;
}

Upvotes: 0

Views: 1009

Answers (2)

redcap
redcap

Reputation: 53

I have no idea what the exceptions thrown are so I just add some if statements to prevent any errors due to an empty file or non-existing file. It may be better to handle the sources of problems before they causes problems.(I really don't know much about exceptions.)

Here are my new codes in main.cpp

if ( argc > 1 ) //a filename is present
{
    try
    {

        tapeInputStream.exceptions( tapeInputStream.failbit );

        if ( existFile( argv[1] ) ) //no need to read file if it does not exist
        {
            tapeInputStream.open( argv[1], ios_base::in );

            if ( emptyFile() ) //no need to read file if it is empty
            {
                tapeInputStream.close();
            }
        }
    }
    catch ( ios_base::failure &ioError )
    {
        ErrorHandling::handleInputStreamError( tapeInputStream, ioError );
    }
}

Upvotes: 1

pcbabu
pcbabu

Reputation: 2329

In line catch ( ios_base::failure &ioError ) you are just handling one type of error. try catch(...) for all the errors. Please mention what are the other error gives after running the code.

Also please be sure that you are providing the argument in proper way. Executable<space>file_name_as_argument.

Upvotes: 0

Related Questions