2016-07-15 19:37 GMT+02:00 Andrzej Krzemienski
2016-07-15 19:22 GMT+02:00 Lorenzo Caminiti
: On Fri, Jul 15, 2016 at 12:35 AM, Andrzej Krzemienski
wrote: 2016-07-15 6:04 GMT+02:00 Lorenzo Caminiti
: 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
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?
It bothers me that I have to copy the entire vector upon each push_back. Because OLDOF stuff must do a copy, right? I do not think I could afford that; even in debug builds.
My point being: the idea is theoretically sound, but seems impractical. This is simply my impression. My personal view on DbC is that only the preconditions are really important, as they help discover misunderstanding and miscommunications between library/component authors and users. Postconditions are mostly useful (in my view) in that they can be matched with other functions' preconditions. In this view, the "expect" functionality is even more remote to preconditions (and hence, of less practical use). Regards, &rzej;