Conversion function for error checking considered good?
In C++03, you need to use the safe bool idiom to avoid evil things:
int x = my_object; // this works
In C++11 you can use an explicit conversion:
explicit operator bool() const
{
// verify if valid
return is_valid;
}
This way you need to be explicit about the conversion to bool, so you can no longer do crazy things by accident (in C++ you can always do crazy things on purpose):
int x = my_object; // does not compile because there's no explicit conversion
bool y = bool(my_object); // an explicit conversion does the trick
This still works as normal in places like if
and while
that require a boolean expression, because the condition of those statements is contextually converted to bool:
// this uses the explicit conversion "implicitly"
if (my_object)
{
...
}
This is documented in §4[conv]:
An expression
e
can be implicitly
converted to a typeT
if and only if
the declarationT t=e;
is well-formed,
for some invented temporary variablet
(§8.5). Certain language constructs
require that an expression be
converted to a Boolean value. An
expressione
appearing in such a
context is said to be contextually converted tobool
and is well-formed
if and only if the declarationbool t(e);
is well-formed, for some
invented temporary variablet
(§8.5). The effect of either
implicit conversion is the same as performing the
declaration and initialization and then using the temporary
variable as the result of the conversion.
(What makes the difference is the use of bool t(e);
instead of bool t = e;
.)
The places were this contextual conversion to bool happens are:
- the conditions of
if
,while
, andfor
statements; - the operators of logical negation
!
, logical conjunction&&
, and logical disjunction||
; - the conditional operator
?:
; - the condition of
static_assert
; - the optional constant expression of the
noexcept
exception specifier;
Object oriented design patterns for error checking
In the OO approach, I haven't used the
try!
macro as I don't want the method to return any value.
It's unclear why you think that "object oriented" means "doesn't return a value". If an error can occur, the code should indicate that.
Many languages have the equivalent of exceptions — out of band values that are thrown (also known as "returned") from a function or method. Note that this means that these languages allow for two disjoint types to be returned from a given function: the "normal" type and the "exceptional" type. That is a close equivalent for Rust's Result
: Result<NormalType, ExceptionalType>
.
Exceptional isn't a great term for this, as you should expect that opening a file should fail. There's an infinite number of ways that it could not work, but only a narrow subset of ways that it can succeed.
Panicking is closer to "kill the entire program / thread right now". Unlike C, you are forced to either deal with a problem, pass it back to the caller, or kill the program (panic).
If you would have thrown an exception in a language that supports them, use a Result
. If you would have killed the program, or don't want to handle an error, use a panic.
If you want to panic in your particular case, use unwrap
, or even better, expect
:
fn get_file_contents(&mut self) {
let mut f = File::open(&self.file_name).expect("Couldn't open file");
f.read_to_string(&mut self.file_contents).expect("Couldn't read file");
}
seems kind of clunky to have to deal with the
Result
for each method.
Which is why the Error Handling section of The Rust Programming Language spends a good amount of time discussing the try!
macro:
A cornerstone of error handling in Rust is the
try!
macro. Thetry!
macro abstracts case analysis like combinators, but unlike combinators, it also abstracts control flow. Namely, it can abstract the early return pattern seen above.
(this makes more sense in context of the page)
I don't want my code to try and recover from the error (most likely caused by the file not being found) - I want it to print a useful error message and then die
Then by all means, panic. There's more succinct AND more detailed ways to do it (as shown above).
How to convert string (char*) to number with error checking using standard library functions?
There's std::stoi
, or std::strtol
.
The first throws an exception (and is in C++11 and later), the other you have to manually check (as it's originally a standard C function).
And you can indeed use std::strtol
to check that a string is a valid number:
char some_string[] = "...";
char *endptr;
long value = std::strtol(some_string, &endptr, 10);
if (endptr == some_string)
{
// Not a valid number at all
}
else if (*endptr != '\0')
{
// String begins with a valid number, but also contains something else after the number
}
else
{
// String is a number
}
Any good idioms for error handling in straight C programs?
Two typical patterns:
int major_func()
{
int err = 0;
if (err = minor_func1()) return err;
if (err = minor_func2()) return err;
if (err = minor_func3()) return err;
return 0;
}
int other_idea()
{
int err = minor_func1();
if (!err)
err = minor_func2();
if (!err)
err = minor_func3();
return err;
}
void main_func()
{
int err = major_func();
if (err)
{
show_err();
return;
}
happy_happy_joy_joy();
err = other_idea();
if (err)
{
show_err();
return;
}
happy_happy_joy_joy();
}
Correct usage of strtol
You're almost there. temp
itself will not be null, but it will point to a null character if the whole string is converted, so you need to dereference it:
if (*temp != '\0')
How do I make sure that strtol() have returned successfully?
You need to pass a real pointer address if you want error checking, so you can distinguish 0 values arising from "0"
and similar from 0 values arising from "pqr"
:
char *endptr;
errno = 0;
long result = strtol(str, &endptr, 10);
if (endptr == str)
{
// nothing parsed from the string, handle errors or exit
}
if ((result == LONG_MAX || result == LONG_MIN) && errno == ERANGE)
{
// out of range, handle or exit
}
// all went fine, go on
Related Topics
Unsigned and Signed Comparison
.C VS .Cc VS. .Cpp VS .Hpp VS .H VS .Cxx
C++: Where to Initialize Variables in Constructor
Is #Pragma Once Part of the C++11 Standard
Using Std::Bind with Member Function, Use Object Pointer or Not for This Argument
Console Output in a Qt Gui App
How Portable Is End Iterator Decrement
What Happens When You Deallocate a Pointer Twice or More in C++
Why Isn't C/C++'s "#Pragma Once" an Iso Standard
Concat Two 'Const Char' String Literals
How Are Circular #Includes Resolved
What Is the Most Elegant Way to Read a Text File with C++
Default, Value and Zero Initialization Mess
Why Is Std::Min Failing When Windows.H Is Included
Initialization: Parenthesis VS. Equals Sign
Which Std::Async Implementations Use Thread Pools