$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
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); // wont 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