Boost logo

Boost :

Subject: Re: [boost] [variant2] Formal review
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2019-04-19 08:00:44


czw., 18 kwi 2019 o 19:57 Nevin Liber via Boost <boost_at_[hidden]>
napisał(a):

> On Thu, Apr 18, 2019 at 2:41 AM Andrzej Krzemienski via Boost <
> boost_at_[hidden]> wrote:
>
> > x1 = std::move(x2);
> >
>
> > This throws, and leaves the objects in an unspecified (albeit "valid")
> > state, an exception handling is stopped prematurely, so that the objects
> > remain in scope and the programmer makes no attempt to put their values
> > into a known state. The only guarantee we have now is that no operation
> on
> > `x1` or `x2` will cause an UB.
>
>
> Not true. The only operations you can perform on those objects are either
> (a) ones with no preconditions or (b) those where the preconditions are
> checked first and found to be valid.
>

To put more context to this remark, the quoted text is me trying to explore
Peter's variant or another type with strong invariant, where the guarantee
is fulfilled by inventing values on the fly.

I agree with your statement, that in practically any type we will have
functions with narrow contract. But I think it misses the core of the
disagreement, which I believe to be: can you still do a lot of things with
this object or only a very narrow subset of things? The goal of the
never-empty basic guarantee is to have less UB's in the code (or less
defensive checks).

>
> The problem with an illegal-to-observe zombie state is that it adds an
> unexpected precondition to copy/move construction, and that precondition is
> viral. For instance, having such a precondition breaks the following:
>
> void ByRef(vector<PossiblyZombieObject>& v)
> {
> v.resize(v.capacity() + 1); // error if v contains any zombie objects
> }
>
> or even
>
> void ByValue(vector<PossiblyZombieObject> v) { /* ... */ } // error if v
> contains any zombie objects
>
> And even if callees decided that want to protect against this (and let me
> reiterate, that should never be a requirement), they cannot, precisely
> because the zombie state is illegal-to-observe.

>
> A std::variant in the valueless_by_exception state is copy/move
> constructible because that state is observable, and that is by design.
> Specifically, valueless_by_exception is not a zombie state, but it is a
> weakening of the invariants from "exactly 1 of n types" to "at most 1 of n
> types". How much that matters is ultimately what we are debating.
>

Thanks for this observation. Maybe my adapting the word "zombie state" was
unfortunate as it may imply things that I never wanted to imply. In the
model that I would like to propose with "strict invariant" and an
"effective invariant" there is basically no problem with copying and moving
the "special state".

The special state is the difference between the "effective invariant" and
"strict invariant" , in case of variant it is .valueless_by_exception().

This state can simply be propagated during move/copy. This preserves the
information about the bug. However, any situations were I can imagine this
would be needed would be again in the places that one should not have in
the program: where you allow the objects that threw from the
basic-guarantee operation to survive, or their state to survive. Even your
examples above. If you are manipulating a PossiblyZombieObject in a vector
and it throws, the object gets "corrupted" and unless you reset it to a
known state it corrupts the entire vector, so either you have to reset the
vector or destroy it. Resizing at this point is surely a bug.

A second thought. I say the special state should never be observed, but it
is a bit of a simplification. There is one situation where observing the
special state makes sense: it is in the function valueless_by_exception()
for variant, and in general case it could be called is_in_weak_state() or
some such. If not anything else, it would be used to describe preconditions
of other functions, and it could be used in clever ways for assisting
static analysis or debugging. However correct programs should never have a
need for the users of a given type to call this function to "check" if an
object is in special state: in correct programs it is never observed.

Regards,
&rzej;


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