Conversion Function for Error Checking Considered Good

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 type T if and only if
the declaration T t=e; is well-formed,
for some invented temporary variable t
(§8.5). Certain language constructs
require that an expression be
converted to a Boolean value. An
expression e appearing in such a
context is said to be contextually converted to bool and is well-formed
if and only if the declaration bool t(e); is well-formed, for some
invented temporary variable t (§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, and for 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. The try! 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



Leave a reply



Submit