Boost logo

Boost :

Subject: Re: [boost] [outcome] Exception safety guarantees
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2017-05-27 22:26:47

2017-05-28 0:13 GMT+02:00 Niall Douglas via Boost <boost_at_[hidden]>:

> > This is in connection with Vicente's question: what exception safety
> should
> > we expect of copy assignment of `expected`?
> Vicente's paper promises a never empty guarantee. So if during a
> transition of state during assignment the new state's assignment
> operator throws, the previous state in the Expected is preserved.
> Outcome master branch doesn't implement that. Outcome develop branch does.
> > First, let's consider the use case for `expected`, where we want to
> disable
> > exception handling altogether. This means `T` or `E` cannot throw on any
> > operation.
> Vicente's paper allows T to throw during move or copy. E must be nothrow
> move. This makes possible the never empty guarantee.
> > So my view, as of today, is not to strive for a strong or even
> never-empty
> > guarantee. Provide a conditional guarantee, if types T and E don't throw,
> > you get no-fail guarantee. If they do, you only have a basic guarantee:
> you
> > can destroy, assign to, or maybe call valueless_by_exception(). Nothing
> > more. I think `std::variant` made the optimal choice.
> std::variant could provide a never empty guarantee without increasing
> storage required if it imposed restrictions on its types e.g. all but
> one of the possible types must have nothrow move construction.
> If it were allowed to double the storage required, it could use Anthony
> Williams' double buffer technique to ensure never empty guarantee. This
> eliminates the need for valueless_by_exception() entirely.
> None of this affects Expected nor Outcome. Outcome hard codes EC and E,
> and both those types are guaranteed nothrow move constructible, so we
> can guarantee we never lose a previous state if assignment of a new
> state throws.

I'll ask the same I asked Peter, because I no longer see the value in
never-empty guarantee.

If I have two objecte of type variant<A, B, C>, where A, B can throw on
copy/move, and C is trivial:

variant<A, B, C> a = A{}, b = B{};

try {
  a = b; // throws
catch(...) {}

// at this point a holds a C

What good does it make to me that I had an A, wanted to assign a B and got
a C?

(unless C represents empty_state.)

Same with outcomes:

if I assign `o1 = o2` and because of an exception I get value different
than `o1` or `o2` had initially, what good does it make?

Of course, the bug in my examples is that I try to catch exceptions
prematurely. If I let the stack unwinding continue, all these objects are
gone and I can start anew.


Boost list run by bdawes at, gregod at, cpdaniel at, john at