Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2003-10-24 07:30:57


"David B. Held" <dheld_at_[hidden]> writes:

> "Pavol Droba" <droba_at_[hidden]> wrote in message
> news:20031023211216.GC22941_at_lenin.felcer.sk...
>> [...]
>> An algorithm has no internal state, so its integrity as a
>> component is always preserved.
>
> You have to remember the context in which Dave wrote those
> definitions. He was looking at the standard library in particular,
> so he was looking at a lot of member functions. However, the
> only thing special about a member function is the hidden 'this'
> parameter. I think a better way to say it is that the basic
> guarantee ensures that a function preserves the invariants of
> its arguments and any global state it modifies.

In other words, the invariants of the whole program are preserved.

> Phrased this way, it becomes more obvious that non-member functions
> don't automatically give the basic guarantee.

Right.

>> Algorithms can only provide exception guaranitie for
>> arguments they work with.
>
> Exactly.

No, not exactly. They provide guarantees for the whole program. If
they touch any global state, they can provide guarantees for that.
Any global state they don't touch, directly or indirectly... well,
it's easy to provide a guarantee for that.

>> In other words, this guarantie is always relative.
>
> False. The guarantee is defined in terms of the invariants
> of every argument that could be passed to the function.

And all global state that it works with.

> If the function's interface allows an argument with an invariant
> that the function does not preserve, then the function does not give
> the basic guarantee. You could argue that arguments could have
> contrived invariants that would make the basic guarantee impossible
> to ensure, but then, you could argue that the function's interface
> is documented to not allow those as valid arguments.

Further, each class should ensure that its interface can't be used to
break its invariants anyway.

>> [...]
>> 1. The library alway manages the external components only
>> through operations defined for them (i.e. it can never access
>> their internal state, if no operation allows it).
>> If all such operations provide "basic exception guarantie",
>> naturaly this guarantie is preserved by an algorithm
>
> That doesn't follow. The arguments to a function might allow
> operations that all give the basic guarantee, but that does not
> mean that the function itself gives the basic guarantee.

Only in the case where the function is managing some more-refined
invariant than is known at the level where each of the operations was
defined. For example:

          void f(Foo& x, Foo& y);

f can't break the invariants of x and y if Foo's interface is
invariant-preserving. If the whole program has an invariant that (x
== y), however, then f must take extra care to preserve it and ensure
the basic guarantee.

>> 2. None of the algorithms in this library throws an exception
>> on its own, therefore if all operations used by a particular
>> algorithm provide "no-throw" guarantie, this guarantie is
>> preserved.
>
> This is a useful and important thing to note.
>
>> 3. "Strong" guarantie is always preserved for constant and
>> pass-by-value arguments. (this is obvious, but important to
>> mention)
>
> But arguments don't give exception safety guarantees. Only
> functions do, and if a function contains persistent state or
> has access to global or otherwise external data, then just
> because the arguments are const or pass-by-value does not
> mean that the function gives the strong guarantee.

He probably should've said that "arguments passed by const& are not
modified by functions in the library". That's a more useful and
precise statement.

> This may sound pedantic, but the whole point of exception safety is
> to make sure you don't get caught by one overlooked item. That's
> why it's important to explicitly state whether function f() gives
> the strong guarantee or not. Don't make the user look at the
> signature and go: "Oh, that only takes arguments by value, so I
> assume this functions gives the strong guarantee."

It's perfectly OK to make a blanket statement like "all functions in
this library which satisfy condition X give the Y guarantee". The
standard does that for swap, for example.

> And definitely don't make the user look at the implementation to
> determine the guarantee.

Definitely.

>> [...]
>> So this is the summary. It would be possible to specify for
>> each algorithm a table with specific requirements to give the
>> specific guarantie. It would be quite chalenging to do it
>> properly,
>
> Indeed!
>
>> but the question is if it would be usable at all?
>
> Yes! I think this type of rigor is increasingly important.

I agree. It may not be neccessary to actually write a table for each
and every function (something simpler may well suffice), but the way
Pavol has formulated the foregoing material doesn't quite give me
confidence that the appropriate rigor has been applied.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com

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