
On Sat, Oct 17, 2009 at 8:04 PM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
Hello all,
Is there any interest in a library that supports Design By Contract (DBC) for C++?
Yes, a Boost version of DbC would be fantastic. I have been using a half-baked macro solution for years, and would be delighted to assist.
All DBC features of the Eiffel programming language are supported by this library, among others: 2) Subcontracting for derived classes.
Have you managed to correctly test the derived classes class invariants in the destructor and constructor? Is it possible to alter the behaviour when a contract is violated? My experience shows that it is better to allow more flexibility than simply terminating the program. I often use exceptions and an exception handling strategy that allows various objects to be disposed and other objects invariants to be maintained followed by continuation. This is vital in some cases where total program termination would be dangerous e.g. flight control systems. This, of course, introduces problems with how one handles violations from destructors in particular. Does your constructor precondition checking occur before initialisation through the member initialisation list? Does your invariant checking mandate the use of a virtual function? 3) Use of "old" in postconditions.
EXAMPLE
template<typename T> class Stack4 DBC_INHERIT_OBJECT(Stack4<T>) { public: Stack4(const int& n): capacity_(0), count_(0), representation_() DBC_CONSTRUCTOR( (private) (template)(Stack4)( (const int&) (n) ), { // Constructor preconditions. DBC_ASSERT(n >= 0, "non negative capacity"); }, { // Constructor postconditions. DBC_ASSERT(self.now.capacity() == n.now, "capacity set"); DBC_ASSERT(self.now.empty(), "is empty"); }, { // Constructor body. capacity_ = n; representation_.resize(capacity_); })
While I appreciate the intense difficulty in putting these features into C++ the DBC_CONSTRUCTOR and DBC_INHERIT_OBJECT look so foreign that I wonder if the advantages outweigh the disadvantages of other design alternatives such as requiring the discipline of the developer to call base functions. If you are managing to maintain Liskov substitution principle automatically though, then I might be convinced that this is worth the strange syntax. <snip> </snip> DBC_INVARIANT(Stack4, {
DBC_ASSERT(self.count() >= 0, "count non negative"); DBC_ASSERT_STREAM(self.count() <= self.capacity(), "count no greater than capacity", err << "count " << self.count() << " bounded by capacity " << self.capacity()); DBC_ASSERT( self.capacity() == int(self.representation_.capacity()), "capacity consistent with vector capacity"); DBC_ASSERT(self.empty() == (self.count() == 0), "empty when no elements"); if (self.count() > 0) DBC_ASSERT( self.representation_.at(self.count() - 1) == self.item(), "if positive count, item at top"); })
The DBC_INVARIANT checking with the ability to add instance information is excellent and often neglected. This hugely reduces time to defect resolution. I assume that the same facility exists in the pre and post-condition assertions.
};
<snip> </snip> As you can tell, I am extremely keen for a solution that can be standardised and peer reviewed. I hope you have the time to answer my questions. Best wishes, Neil Groves