3

I'm writing a pretty simple program for my first-year C++ class, where I utilize switch statements to get a correct output depending on what two numbers and what special character is used between them (adding, subtracting, multiplying, dividing). The program uses a nested if statement in the division case to check if the second number entered is zero, and a default statement if none of the correct special characters are used.

The problem lies in the fact that if the user divides by zero, or uses an incorrect symbol, the console will display the intended error, but also the result, when it's only supposed to show the message.

I understand why it's displaying both lines, but I don't know of any way to fix it. I'm not allowed to change the switch statement to an if statement, and I'm not allowed to use additional functions or arrays.

#include <iostream>
#include <iomanip>
#include <cstdlib>

using namespace std;

int main()
{
    char operatr;

    double operand1 = 0,
           operand2 = 0,
           result = 0;
    cout << "Enter a binary expression of the form: operand operator operand ";
    cin >> operand1 >> operatr >> operand2;
    cout << endl << endl
         << "C.S.1428.002" << endl
         << "Lab Section: L17" << endl
         << "10/14/20" << endl << endl;

    cout << fixed << setprecision(1);

    switch ( operatr )
    {
    case '+':
        result = operand1 + operand2;
        break;
    case '-':
        result = operand1 - operand2;
        break;
    case '*':
        result = operand1 * operand2;
        break;
    case '/':
        if ( operand2 == 0)
        {
            cout << operand1 << " " << operatr << " " << operand2 << " " << "Division by zero produces an undefined result" << endl;
            break;
        }
        else
        {
            result = operand1 / operand2;
            break;
        }
    default:
        cout << operand1 << " " << operatr << " " << operand2 << "  Encountered unknown operator." << endl;
        break;
    }

    cout << operand1 << " " << operatr << " " << operand2 << " = " << result << endl;

    system("PAUSE>NUL");

    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Chopskie
  • 31
  • 1
  • 1
    Incorporate your error message into result and populate it only when appropriate. Keep only one `cout` at the end. – PM 77-1 Oct 12 '20 at 22:16
  • Although [`goto`](https://en.cppreference.com/w/cpp/language/goto) should normally not be used if there is a better alternative, in this case, I do recommend using it. You can use it to skip the `cout` line which prints the result. – Andreas Wenzel Oct 12 '20 at 22:17
  • 1
    @AndreasWenzel No no no no. Why? This is easily solvable with a simple bool, error status or an exception, why resort to such measures? there are very few valid use cases for goto, e.g high performance parsers, this is definitely not one of them – Sebastian Hoffmann Oct 12 '20 at 22:19
  • @SebastianHoffmann: Personally, consider a `goto` to be the lesser evil than the introduction of an additional (unnecessary) variable. However, I guess this is a matter of taste (and performance requirements). – Andreas Wenzel Oct 12 '20 at 22:26
  • @AndreasWenzel Unnecessary? It makes the intent perfectly clear and has semantic meaning. You just suggested using `goto` to a first year beginner which struggles with simple control flow. Think about that. Do you want to maintain his code? – Sebastian Hoffmann Oct 12 '20 at 22:30
  • @AndreasWenzel Just to take away some performance anxiety: https://godbolt.org/z/6zd9vW Boolean flag gets translated to goto by gcc (i decided to replace iostreams with printf to reduce clutter abit) – Sebastian Hoffmann Oct 12 '20 at 22:44
  • @SebastianHoffmann: I see nothing wrong with teaching someone how to use `goto`, provided that sufficient warning is provided that it is considered bad programming style to use it in cases in which other control flow statements can do the job just as well. – Andreas Wenzel Oct 12 '20 at 23:04
  • @SebastianHoffmann: Yes, it is impressive what compilers are able to optimize away nowadays. However, I believe compilers are only able to analyze a program up to a certain depth, so they are better at optimizing shorter code and not so good at optimizing larger code. Even if performance were not an issue, I personally consider the code with the `goto` to be a bit cleaner than introducing an additional variable. However, as stated before, this is probably a matter of taste. I understand that many other programmers feel differently. – Andreas Wenzel Oct 12 '20 at 23:30

3 Answers3

4

A simple solution would be to use a bool flag to signal a successful operation which would then be used as a condition to print the result:

//...

bool flag = true;

switch (operatr)
{
case '+':
    result = operand1 + operand2;
    break;
case '-':
    result = operand1 - operand2;
    break;
case '*':
    result = operand1 * operand2;
    break;
case '/':
    if (operand2 == 0)
    {
        cout << operand1 << " " << operatr << " " << operand2 << " "
             << "Division by zero produces an undefined result" << endl;
        flag = false;
    }
    else
    {
        result = operand1 / operand2;
    }
    break;
default:
    cout << operand1 << " " << operatr << " " << operand2 << "  Encountered unknown operator." << endl;
    flag = false;
    break;

}
if (flag)
    cout << operand1 << " " << operatr << " " << operand2 << " = " << result << endl;

//...

You could also use a try block, which I suspect can fall into the category of things you can't do, nonetheless it's worth taking a look at for future reference.

Or as suggested by @VladFeinstein in his answer just return from the switch in case an error occurs given that it's a one of execution.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • 1
    Simple and elegant. But if `true` is the norm why not initialize the flag to true and set it to false in the sole case where it's needed? – Christophe Oct 12 '20 at 22:43
  • @Christophe, advice taken. – anastaciu Oct 12 '20 at 22:51
  • See my answer for a solution which avoids introducing an additional variable by using the (highly controversial) `goto` statement instead. – Andreas Wenzel Oct 12 '20 at 22:55
  • 1
    @AndreasWenzel forget about goto unless you have to write C code for the unix kernel. Instead use a try { your switch+ print result} catch() {error processing} – Christophe Oct 12 '20 at 23:10
  • 1
    @AndreasWenzel You make a subrutine where you can return from instead of falling for the quicker, easier more seductive `goto`. – Surt Oct 12 '20 at 23:51
1

In such a simple case, where there are no recovery from those input errors, why not just return 1; instead of break;?

Vlad Feinstein
  • 10,960
  • 1
  • 12
  • 27
-1

Although the goto statement should generally not be used if there is a better alternative, in this case, I believe it is appropriate to use it. The alternatives would be code duplication or the introduction of an additional variable. See the other answer for a solution which avoids using goto by using an additional variable.

Here is my solution which uses goto:

int main()
{
    [...]

    default:
        cout << operand1 << " " << operatr << " " << operand2 << "  Encountered unknown operator." << endl;
        goto bad_input;
    }

    cout << operand1 << " " << operatr << " " << operand2 << " = " << result << endl;

bad_input:
    system("PAUSE>NUL");

    return 0;
}

Just remember not to make it a habit to use goto everywhere, as this is considered very bad programming style. Always look for better alternatives, such as for, while, do...while, break, continue, etc. That will make your code easier to read, both for yourself and for other people. There are only very rare cases in which the use of goto is appropriate.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • Downvoting for the suggestion of `goto`. Modern C++ guidelines have many workarounds and this day and age has no place for it. – Casey Oct 13 '20 at 00:42
  • @Casey: Yes, there are, as you say, many "workarounds" for `goto`. It is good that these workarounds are being posted as answers. However, why should only workaround solutions be upvoted and non-workaround solutions be downvoted? Would it not be more appropriate that both types of solutions be presented as answers, in which the advantages and disadvantages of both types of solutions are highlighted? – Andreas Wenzel Oct 13 '20 at 01:40
  • 1
    It's like suggesting using `volatile` for use as a thread-safe construct that can be presented as an alternative to the modern `std::atomic` and `std::condition_variable`. It was the old way of doing things in C land that was highly error prone and "just happened to work" even though now-a-days it should never be used and the modern solutions are better. – Casey Oct 13 '20 at 04:00
  • @Casey: I don't think it is appropriate to compare the use of `goto` with using undefined behavior. The use of `goto` has always been well-defined by the ISO C++ standard. I believe the main question at hand is: "Is it cleaner to introduce an extra variable or to introduce a goto label?" I tend to favor the `goto` label in this case, but I respect your opinion if you favor the opposite. – Andreas Wenzel Oct 13 '20 at 15:39