|
Boost : |
Subject: Re: [boost] [contract] Strong exception safety guarantees
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2016-07-15 13:22:09
On Fri, Jul 15, 2016 at 12:35 AM, Andrzej Krzemienski
<akrzemi1_at_[hidden]> wrote:
> 2016-07-15 6:04 GMT+02:00 Lorenzo Caminiti <lorcaminiti_at_[hidden]>:
>
>> I've been considering adding a .except(...) construct to
>> Boost.Contract that will allow to specify conditions to check at
>> function exit but when the function body throws (in contrast to
>> postconditions that are checked at function exit but only when the
>> function body does not throw).
>>
>> I think .except(...) could be used to assert strong exception safety
>> guarantees... (note that class invariants are already checked at exit
>> of public functions and also when the public function bodies throw,
>> but invariants are suited to assert only basic, and not strong,
>> exception safety guarantees).
>
> How would you express the strong guarantee on std::vector::push_back?
vector::push_back exception safety:
``If no reallocations happen, there are no changes in the container in
case of exception (strong guarantee).
If a reallocation happens, the strong guarantee is also given if the
type of the elements is either copyable or no-throw moveable.
Otherwise, the container is guaranteed to end in a valid state (basic
guarantee).
If allocator_traits::construct is not supported with val as argument,
it causes undefined behavior.''
http://www.cplusplus.com/reference/vector/vector/push_back/
Hmm... so assuming the allocator API can be augmented with an
allocations() const query that returns the number of allocations done
so far, and that operator== can be used to check if the vector has not
changed, maybe something like this:
template<typename T>
void vector<T>::push_back(T const& value) {
boost::contract::old_ptr<vector<T> > old_me = BOOST_CONTRACT_OLDOF(*this);
boost::contract::old_ptr<unsigned> old_allocations =
BOOST_CONTRACT_OLDOF(get_allocator().allocations());
boost::contract::guard c = boost::contract::public_function(this)
... // Preconditions and postconditions.
.except([&] {
if(
get_allocator().allocations() == *old_allocations ||
is_copyable<T>::value ||
is_nothrow_movable<T>::value
) BOOST_CONTRACT_ASSERT(*this == *old_me);
// Otherwise, just basic guarantees as asserted by the
class invariants.
})
;
... // Body implementation.
}
I'm not 100% sure... what do you think?
Thanks,
--Lorenzo
P.S. A part from this example, for sure there will be specifications
that cannot be programmed with contracts (e.g., inability to express
programmatically "iterator range is valid, or invalid" and something
like properties would help here...). But this is an old limitation of
Contract Programming, it was already discussed by Bertrand Meyer in
Object Oriented Software Construction for Eiffel in the 1990s.
Nevertheless, contracts are still useful for those specifications that
can be expressed programmatically, even if not all specifications can.
So I'd expect the same to be true if using contracts to program (both
strong with .except(...) and basic with invariants) exceptions safety
guarantees.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk