c++throwprogram-flow

Using throws to control program flow?


The program communicates with user through class Menu, so the main() looks like this:

int main() {
    try {
        Menu menu(/*...*/);
        while (true) {
            menu.printOptions();
            menu.chooseOption();
        }
    }
    catch (const char *error) { /*Resolve error.*/ }
    catch (int code) { /*Exit with code.*/ }
}

Current class Menu's option used for exiting is as such:

void exit() {
    // Release memory.
    throw 0;
}

Should constructs like this be avoided and do they have some unpredictable (or undesired) side-effects?


Solution

  • I would avoid using exceptions as a mean to control flow in that way. Instead you could change your loop, from while (true) to something that checks on the state of the menu class such as while (menu.isAlive()). Exiting is a matter of simply setting the alive attribute to false, which will end the while loop.

    Exceptions should be used for situations that the current flow cannot recover from, and that requires you to return control to a parent flow. Having said that, there is nothing that prevents you from using exceptions in that way, but personally I think it is bad enough to have exceptions for error handling, and thereby hidden jumps, but having them for regular flow control, is definitely something I would think polluted the code, and made it less predictable.

    Exceptions have the problems that you will also find in many other articles about anti patterns, that:

    I am not against exceptions in all situations, but I do not think that all handling of "error" situations should be done using exceptions, it depends on how the "error" affects the program flow. For instance it makes great sense in situations where the error (sorry for the bad wording) is truly an exceptional state, for instance when a socket dies, or you are unable to allocate new memory, or something similar. In these cases exceptions makes sense, especially since exceptions are hard to ignore. In summary in my opinion exceptions should be used for exceptional situations.

    There are other answers addressing this on SO and other Stack Exchange sites that might be of interest to you:
    Are exceptions as control flow considered a serious antipattern? If so, Why?
    Why not use exceptions as regular flow of control?


    A note on the performance of exceptions, it is implementation specific how exception handling is done, and it may differ from compiler to compiler. Often however exceptions add no extra cost when taking the non-exceptional path, however when taking the exceptional path, there are a number of reports that suggest that the cost is indeed significant (https://stackoverflow.com/a/13836329/111143 mentions 10x/20x the cost of a regular if on the exceptional path).

    Since implementations of exceptions are specific to the compiler, it can be difficult to give any general answer to this. But examining the code generated by the compiler may show what is added when using exceptions. For instance when catching exceptions you need the type information of the exception thrown, which adds additional costs to the throwing of an exception. This additional cost makes little difference when you are truly facing an exceptional state, which should happen rarely. But if used to control regular program flow it becomes a costly affair.

    For instance look at the following example of a simple code that exits by throwing an integer: https://godbolt.org/g/pssysL the global variable is added to prevent the compiler from optimizing the return value out. Compare this to a simple example where the return value is used: https://godbolt.org/g/jMZhT2 the exception has about the same cost when the non-exceptional path is taken, but becomes more expensive the exceptional path is taken. Again this can be fine, if the exception is used for truly exceptional situations, but when used in situations where the "exceptional path" is hit more frequently, the cost starts to become a factor.