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).
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, e.g.
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 (i.e. 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 (e.g. 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 (e.g. 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 (e.g. 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.