Boost logo

Boost :

Subject: [boost] [err] RFC
From: Domagoj Saric (dsaritz_at_[hidden])
Date: 2015-11-16 19:45:24


https://github.com/psiha/err README.md copy-paste:

---------------------------------------------
err - yet another take on C++ error handling.

We have throw and std::error_code, what more could one possibly want?
---------------------------------------------

std::error_code&co tried to solve or at least alleviate the problem of
overhead of using exceptions (in terms of both source code verbosity and
binary code size&speed) with functions which need to report failures
that need not be 'exceptional' depending on the context of the call
(thereby requiring try-catch blocks that convert exceptions to 'result
codes'). Unfortunately, the way it was finally implemented,
std::error_code did not quite accomplish what it set out to do:

   â€¢ in terms of runtime efficiency: 'manual'/explicit EH (try-catch
blocks) is no longer needed but the compiler still has to insert hidden
EH as it has to treat the functions in question as still possibly
throwing (in fact the implementation/codegen of functions which use
std::error_code for error reporting becomes even fatter because it has
to contain both return-on-error paths and handle exceptions)
   â€¢ in terms of source code verbosity: again, try-catch blocks are gone
but separate error_code objects have to be declared and passed to the
desired function.

Enter boost::err: 'result objects' (as opposed to just result or error
codes) which can be examined and branched upon (like with traditional
result codes) or left unexamined to self-throw if they contain a failure
result (i.e. unlike traditional result codes, they cannot be
accidentally ignored). Thanks to latest C++ language features (such as
r-value member function overloads) incorrect usage (that could otherwise
for example lead to a result object destructor throwing during a
stack-unwind) can be disallowed/detected at compile time.

Discussions where the concept (or a similar one) was first concieved of
and/or so far discussed:
•
http://boost.2283326.n4.nabble.com/system-filesystem-v3-Question-about-error-code-arguments-td2630789i40.html
• http://cpptips.com/fallible
• https://github.com/ptal/std-expected-proposal
• https://github.com/ptal/expected
•
https://github.com/adityaramesh/ccbase/blob/master/include/ccbase/error/expected.hpp
•
http://stackoverflow.com/questions/14923346/how-would-you-use-alexandrescus-expectedt-with-void-functions
•
https://svn.boost.org/trac/boost/wiki/BestPracticeHandbook#a8.DESIGN:Stronglyconsiderusingconstexprsemanticwrappertransporttypestoreturnstatesfromfunctions
• http://2013.cppnow.org/session/non-allocating-stdfuturepromise

****************************************************************

For starter's, to reduce the length of the first post, I'll assume that
everyone as at least somewhat familiar with similar/related proposals
from Andrei Alexandrescu (std::expected) and Niall Douglas (std::monad)
to which I gave links above.

What, primarily, makes (Boost.)Err different from other proposals is the
ability to (using latest C++ features) detect/distinguish between
temporaries and 'saved' return values by using two different class
templates (result wrappers), fallible_result (for rvalues) and
result_or_error (for lvalues) and thus minimise or often completely
eliminate any extra verbosity:
If we had a function foo() that produces bar_t objects but can fail with
an err_t, up till now we had two options:
* bar_t foo() throw( err_t );
* optional<bar_t> foo( err_t & );

Err enables the library writer to write a single API and a single
implementation:
     err::fallible_result<bar_t, err_t> foo();
where fallible_result is the class template that wraps
temporaries/rvalues returned from functions and its member functions are
all declared with && (i.e. callable only on rvalues) so you get a
compiler error if you save it to an auto value and try to do anything
with it. The two exceptions are the implicit conversion operators to:
- bar_t, which will either return bar_t or throw err_t and which is used
for the 'EH code path'
     bar_t my_bar( foo() );
- err::result_or_error<bar_t, err_t> which is used for the 'oldsk00l
nothrow error code path'
     err::result_or_error<bar_t, err_t> maybe_bar( foo );
     if ( maybe_bar ) { print( *maybe_bar ); }
     else { log( maybe_bar.error() ); }

Also, if the fallible_result rvalue is left uninspected and contains an
error its destructor will throw (which AFAICT should be safe considering
it is an rvalue and no other exception can possibly be active at the
same time) - this makes code that uses Err enabled libraries/APIs but
relies on EH almost indistinguishable from 'classic EH' APIs (almost
because the one difference that remains is with the immediate use of the
return value: because of the wrapper class one can no longer write
foo().do_something() but has to implicitly use the -> operator and write
foo()->do_something() where the -> operator will check-and-throw if
foo() did not succeed).

To wrap up, this approach gives the user the immediate and fine grained
control over which error handling mechanism he/she wants to use while
allowing the developer to write a single API and implementation - AFAICT
it is a no brainer replacement for <system_error>, it is less verbose
and much more efficient ;-)

ps. the small library @ https://github.com/psiha/err is by no means a
finished product, it contains no tests or documentation but 'it works'
(i.e. it is used in the, also upcoming,
https://github.com/psiha/mmap)...I'm bringing it to public view for
scrutiny, discussion and guidance lest I steer in the wrong direction ;)

-- 
"What Huxley teaches is that in the age of advanced technology, 
spiritual devastation is more likely to come from an enemy with a 
smiling face than from one whose countenance exudes suspicion and hate."
Neil Postman

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