
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