简体   繁体   中英

How to handle runtime errors in C++?

So, I'm kinda new to C++ and I wanted to know what are the good practices or even how do I handle runtime errors when programming, here is an example:

State s_toState(std::string state){
  if (state == "MG")
    return State::MG;
  else if (state == "PR")
    return State::PR;
  else if (state == "SP")
    return State::SP;
  else if (state == "SC")
    return State::SC;
  else if (state == "RJ")
    return State::RJ;
  else if (state == "RN")
    return State::RN;
  else if (state == "RS")
    return State::RS;

  // ???
}

So I have this function that transforms a string into a State . Without using exception, what is the ideal way for me to assert that the given state is an existing one (MG, PR, SP, etc...)?

Gave an example but I'm asking for the general rule. As far as I know i could use exceptions, assertions or just print the error. I do pretend to unit test this too (also new to unit testing and know nothing about it).

This looks like a good opportunity to use exceptions. The cplusplus.com guide is reasonable, but I found that playing around with it is a better way to learn.

The basic idea is this:

  • A function throw s an exception, terminating the function and passing the exception to whoever called the function.
  • If the caller calls the function in a try block, then the subsequent catch will be executed.
  • If the caller does not have a try / catch system, the caller is terminated as well and the process repeats down the function call stack until it either finds a try / catch or main() is terminated.

The answer depends on what s_toState "promises" to do.

If state being invalid is a fault of a programmer who the function or an error in other internal logic. Add assert and document state validity as a precondition (same as !=nullptr for pointers). For release build, add some default behaviour just so the program does not crash if possible. Or let it crash if you are not writing critical SW, it will at least make debugging easier.

If it is a recoverable fault of an user and plausible scenario (not a bug), then consider returning std::optional or throw . I prefer the former when the thrown exception would have always been caught by the direct caller, ie never propagated up the call stack. I also find it more clear as it forces the caller to explicitly handle it.

For unrecoverable faults, ie when the direct caller cannot handle the nullopt , just throw and let some other code deal with that.

I like the naming convetion of try_parse returning std::optional , while parse throwing.

It might be worth givin Exceptions and Error Handling FAQ a try.

Generally speaking, the way to handle such errors (like any errors) depends on the needs of your program as a whole - and you have not specified that. So there is no one-size-fits-all "general rule".

There are options and trade-offs.

One option is for your State enumerated type to provide an enumerator value that represents undetermined or an invalid state, such as

 enum class State {MG, PR, Undetermined};

Then, in your function, return the undetermined value, eg

State s_toState(const std::string &state)
{
    State return_value = State::Undetermined;
    if (state == "MG")
       return_value = State::MG;
    else if (state == "PR")
       return_value = State::PR;
    // etc

    return return_value;
}

With this approach the function always returns a valid value of type State . If the error conditions aren't critical (ie the program can continue if an invalid string is supplied) then the caller can decide if it needs to check the return value. Multiple types of error condition can be reported (eg by having multiple enum values representing different errors) A down-side is that the caller may forget to check the return value and behave incorrectly.

Another option is to throw an exception, for example;

State s_toState(const std::string &state)
{
    if (state == "MG")
       return State::MG;
    else if (state == "PR")
       return State::PR;
    // etc

    throw std::invalid_argument("Bad input string");
}

This option requires some sensible choice of what type of exception to throw (eg what information needs to be conveyed about the error state). This approach may be preferable if the caller (or the program as a whole) cannot sensibly continue if a bad string is provided since, by throwing an exception, the caller is forced to catch and take any recovery action to avoid being terminated. As such, this approach may not be suitable for non-critical errors - for example, if execution can sensibly continue if a bad string is provided.

Another option (C++17 and later) is to return std::optional<State> . This allows the caller to check if an error has occurred (eg std::option::has_value() return false ) occurs or, if the value is accessed without checking, cause an exception of type std::bad_optional_access to be thrown (which may be suitable for the caller, or may be uninformative).

It's also possible to use assert() - which forces termination if a specified condition is untrue. Generally, I prefer throwing an exception over using assert() but you may prefer otherwise.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM