Boost logo

Boost :

Subject: [boost] [gsoc-2013] Boost.Expected
From: Pierre T. (ptalbot_at_[hidden])
Date: 2013-04-13 02:38:35


Hello,

My name is Pierre Talbot, I participated to GSoC 2011 and began the
implementation of the Boost.Check library (for checking the validity of
any number having a check digit — credit card number, ISBN, …). It was
two years ago and the design is still evolving due to many variation
points in the code. One of the function looks like :

template <typename check_algo,
           typename range>
boost::optional<typename check_algo::checkdigit_type>
compute_checkdigit(const range &x);

Using boost::optional tell us that: "A check digit should be computed if
'x' is a valid sequence". Since 'x' has many reasons to be incorrect,
many errors could be raised. Based on a policy template class, it
launches exception or returns with an empty optional . Then I though a
lot about a better way to do it, allowing the user to get an exception
or an error code. But it was quite complex for a so little part of my
library… I decided that the policy "throw exception or nothing" should
be enough.

Yesterday, I watched the video of Mr. Alexandrescu on Boost.Expected and
I think it would be a very useful library. It could mainly be useful in
system programming (where many error codes can arise from a single
call), but in any code where many exception errors can be thrown (like
in Boost.Check).

As you suspected, I'm interested in coding Boost.Expected during the
summer as a GSoC student.

Firstly, it could be very useful to list the resources on the subject
(aside the talk), I have several articles that I will talk about later.

Secondly, and hoping you'll debate, I would like to ask your opinion
about several ideas and facts:

1) In the Boost project description, we can read: "adding a class
expected-or-error_code". Obviously, the main design decision made by
Alexandrescu is to consider that error code are exception. Do we need an
abstraction of exception/error code ? Why do you think a
"expected-or-error_code" class is interesting ?

2) Consider this code (from Alexandrescu slides) :

// Caller
string s = readline();
auto x = parseInt(s).get(); // throw on error
auto y = parseInt(s); // won’t throw
if (!y.valid()) {
// handle locally
if (y.hasException<std::invalid_argument>()) { // ------------------ <
The flagged line.
// no digits
...
}
y.get(); // just "re"throw
}

The flagged line has some tastes of "return to the past" flavor. Back to
the C procedural language, the basic error code handling system was a
lot criticized because:

* Readability of the code decrease ;
* Error handling occurs in the middle of the execution flow ;
* <Add your favourite reason here>.

Several links on the subjects (if you have others, I'm interested)

http://www.joelonsoftware.com/articles/Wrong.html
http://www.joelonsoftware.com/items/2003/10/13.html
http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx
http://nedbatchelder.com/text/exceptions-vs-status.html

Basically, only the last one is clearly for exception. The main argument
against the procedural approach is the readability. I would say that the
Expected approach just differ by allowing to rethrow exception. But if
you want to handle it, you must code multiple if-than-else statements.

So I considered a complementary approach working with Expected to handle
multiple error cases:

string visa_number = readline();
expected<char> expected_checkdigit = compute_checkdigit<visa>(visa_number);
if(expected_checkdigit.valid(visa_error_resolver))
{
visa_number += expected_checkdigit.get();
std::cout << visa_number << std::endl;
}

With this code, there is only a if statement, and no more multiple error
cases handles. But what is this error_resolver ?

It may be declared as :

// Somewhere in visa.hpp. A type list.
typedef error_list<size_error_exception, unknown_character_exception,
checkdigit_encoding_exception, …> visa_errors;

// Somewhere in the user code.
error_resolver<visa_errors, expected_type> visa_error_resolver; // in
this case, expected_type is a char.

// Initialize error handler on specific exception/errors.
visa_error_resolver.on<size_error_exception>(size_error_handler)
.on<unknown_character_exception>(unknown_character_exception)
...

Now we are agree that visa_error_resolver can be reused everywhere we
want to resolve an error on a visa number.

What are the handlers ? There are [Function|Functor|Lambda] (pick up
your favourite) with this form :

expected<ExpectedType> size_error_handler(const size_error_exception&)
expected<ExpectedType> unknown_character_exception(const
unknown_character_exception&)

Now you can understand for what the type list "error_list" stands for,
we can store these handlers into the error_resolver and call them
without any virtual cost.

Why the return type of error handler is expected<ExpectedType> ?

Consider this size_error_handler code :

expected<ReturnType> size_error_handler(const size_error_exception& e)
{
std::cout << "The number you gave has a bad size." << std::endl;
std::cout << "Enter it again : " << std::endl;
return read_visa_checkdigit();
}

read_visa_checkdigit can call recursively valid() until it's valid.
Though there are some ways to make this treatment iterative.

A basic treatment could be to print an warning message and just returns
(in this case, valid returns false):

expected<ReturnType> size_error_handler(const size_error_exception& e)
{
std::cout << "Warning: the VISA field is incorrect." << std::endl;
return expected<ReturnType>::fromException(e);
}

Results:
* The error code handling is delegated to specific functions ;
* The readability is still excellent ;
* You can easily re-use your error handler function ;
* If you don't like it, you can still throw exception on failure with
"get()".

I can code a "proof of concept" if you think this is a good idea.
Do not hesitate to comment it, point out programming pitfalls, request
further clarification, or anything you judge useful.

Thank you for reading it !
Pierre Talbot


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk