Boost logo

Boost :

Subject: Re: [boost] Noexcept
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2017-06-20 06:58:44


2017-06-20 3:38 GMT+02:00 Emil Dotchevski via Boost <boost_at_[hidden]>:

> On Mon, Jun 19, 2017 at 2:41 PM, Andrzej Krzemienski via Boost <
> boost_at_[hidden]> wrote:
>
> > 2017-06-19 20:33 GMT+02:00 Emil Dotchevski via Boost <
> > boost_at_[hidden]>
> > :
> >
> > > On Mon, Jun 19, 2017 at 2:58 AM, Andrzej Krzemienski via Boost <
> > > boost_at_[hidden]> wrote:
> > >
> > > > 2017-06-14 21:52 GMT+02:00 Richard Hodges via Boost <
> > > boost_at_[hidden]
> > > > >:
> > > >
> > > > > Exception return paths are not infinite. There are a finite number
> of
> > > > > places in code that an exception can be thrown.
> > > > >
> > > > > The exception path is one path, the non-exception path is another.
> > > That’s
> > > > > two in total. Exactly equivalent to an outcome<>.
> > > > >
> > > > > It is a fallacy to say that there are an indeterminate number of
> > paths.
> > > > >
> > > > > If developers do not understand RAII, then an afternoon of training
> > can
> > > > > solve that.
> > > > >
> > > > > RAII is the foundation of correct c++. It is the fundamental
> > guarantee
> > > of
> > > > > deterministic object state. A program without RAII is not worthy of
> > > > > consideration. The author may as well have used C.
> > > > >
> > > > > Perhaps there is an argument that says that RAII adds overhead to a
> > > > > program’s footprint. If things are that tight, fair enough.
> > > > >
> > > > > Otherwise there is no excuse to avoid exceptions. I’ve never seen a
> > > > > convincing argument.
> > > > >
> > > >
> > > > The above statement almost treats RAII and exception handling as
> > > > synonymous. But I believe this gives the false picture of the
> > situation.
> > > >
> > > > RAII is very useful, also if you do not use exceptions, but have
> > multiple
> > > > return paths. You want to acquire the resource in one place and
> > schedule
> > > > its future release in one place, not upon every return statement.
> > > >
> > >
> > > If you adopt this programming style as a rule, there is no downside to
> > > using exceptions.
> > >
> > >
> > > > In case of using things like Outcome, you still want to follow RAII
> > > idioms.
> > > >
> > > > People who choose to use Outcome do understand RAII and will still
> use
> > > it.
> > > > But RAII does not handle all aspects of failure-safety, and this is
> > about
> > > > these other aspects that people may choose to go with Outcome rather
> > than
> > > > exceptions. One example: propagating information about failures
> across
> > > > threads, or "tasks".
> > > >
> > >
> > > There is exception_ptr for transporting exceptions between threads,
> which
> > > was the only primitive that was missing for being able to accumulate
> > > results from multiple workers.
> > >
> > > Outcome and Noexcept are simply better alternatives for users who write
> > or
> > > maintain code that is not exception-safe -- better not compared to
> > > exception handling (which in this case can not be used), but compared
> to
> > > what they would likely do otherwise.
> > >
> >
> > To somewhat challenge this statement, The following is an example of how
> I
> > would use Boost.Outcome if I had it available at the time when I was
> > solving this parsing problem:
> > https://github.com/akrzemi1/__sandbox__/blob/master/outcome_
> > practical_example.md
> > This tries to parse (or match) the string input, where I expect certain
> > syntax, into a data structure.
> > It is not performance-critical, I do not mind using exceptions, but I
> still
> > prefer to handle situations where the input string does not conform to
> the
> > expected syntax via explicit contrl paths. A number of reasons for that:
> >
> > 1. I want to separate resource acquisition errors (exceptions are still
> > thrown upon memory exhaustion) from input validation.
> >
>
> Why?
>

I think the reason is that of a personal taste or personal sense of order.
Failure to acquire resources is a situation where I will not be able to
deliver what I have committed to. I will disappoint the user.

In case of validation failure, this is exactly what users are calling my
function for: sometimes their only goal is to get a true-false answer
whether their text file is valid. I do not even treat validation failure as
"error". But I still like to have the "short exit" behavior of errors.

>
>
> > 2. Some debuggers/IDEs by default engage when any exception is thrown. I
> do
> > not want this to happen when an incorrect input from the user is
> obtained.
> >
>
> "By default", so turn off that option.
>

But after a while I have concluded that it is a good default. Even if I am
debugging something else, if I get a "resource-failure", or "logic error"
(like invariant broken) I want to be alerted, and possibly stop what I was
debugging before. This default setting is my friend, provided I do not use
exceptions for just any "irregularity".

>
>
> > 3. I want validation failers to be handled immediately: one level up the
> > stack. I do not expect or intend to ever propagate them further.
> >
>
> You can catch exceptions one level up if you want to. Right? :)
>

I can. And it would work. But it just feels not the right tool for the job.
It would not reflect my intention as clearly as `outcome`.

> However, if you're only propagating errors one level up, it really doesn't
> matter how you're handling them. I mean, how much trouble can you get into
> in this case? It's trivial.
>

But t reflects my intentions clearly and gives me confidence that the error
information will not escape the scope if I forget to put a try-block, or if
I inadvertently add another return path form my validation routine.

>
> Error handling libraries are needed in more complex use cases where errors
> must be propagated across multiple levels, across threads, across API
> boundaries. The important design goals are:
>
> 1) The error object created by reporting code should be able to be
> propagated across (potentially many) error-neutral contexts which should
> not be required to "translate" it (that is, turn it into a different error
> object.) The idea of translation of errors gave us exception specifications
> which are notoriously one of the more embarrassing aspects of C++.
>
> 2) Error-neutral contexts should be able to ignore any errors reported by
> lower level code but also intercept _any_ error, augment it with relevant
> information (which may not be available at the point the error is detected)
> and let it propagate up the call stack, intact.
>
> 3) Error-handling contexts should be able to recognize the errors they can
> deal with but remain neutral to others.
>

I recognize these needs. And in the contexts where you require the above
characteristics (probably 97% of all code) exceptions are the tool for the
job.

For rare situations where I need different characteristics of error
reporting mechanism, I will need to resort to something else, like a
dedicated library.

>
> Your use of outcome is probably fine in this simple case but
>
> out::expected<Range, BadInput> parse_range (const std::string& input)
>
> looks much too close to exception specifications:
>
> Range parse_range(const std::string& input) throw(BadInput)
>

In some other language - yes. In a language, where such throw specification
is enforced statically, like in Java.

>
> While this doesn't matter if your error handling is only one level up, it's
> going to create a lot of problems in more complex cases because it makes
> error-neutral contexts impossible and,

Let me be clear: I do not claim that things like `outcome` are superior to
exceptions. What I claim is that there happen to be rare ("rare" is very
subjective here) situations where things like `outcome` fit better than
exceptions.

> as the exception specifications
> fiasco showed,

So yes, I would probably never agitate to put static exception checking
into C++ and impose it on everybody. But for solving local problems, or for
specific environments, I would like to have a library that offer different
error handling trade-offs and is superior to error codes.

> in general it is not possible for functions to even "know"
> what errors might propagate through them.

Agreed.

> Assuming we agree that it is not
> acceptable for error-neutral contexts to kill errors they don't recognize,
> this is a problem.
>

Ok. It is just that I have parts of the program that I do not want to be
exception neutral by accident.

Regards,
&rzej;


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