C
christopher diggins
Bertrand Meyers' DBC (Design By Contract) has an important characteristic,
which is oftentimes ignored, of separating the contract (preconditions /
postconditions / invariants) from the implementation. So it would seem that
ideally we would write the class separate from the contract, and then merge
them. For a small example we could have the following class MFuBar (M for
iMplementation):
class MFuBar {
public:
Fu() { std::cout << "fu" << endl; };
Bar() { std::cout << "bar" << endl; };
};
Let's say that the contract that must be enforced is (in English): any call
to Bar must be precedeed by at least one call to Fu. We could express that
in code as follows:
template<typename T>
class FuBarContract : public T {
public:
FuBarContract() { bCanCallBar = false; };
void Fu() { bCanCallBar = true; T::Fu(); };
void Bar() { assert(bCanCallBar); T::Bar(); bCanCallBar = false; };
private:
bool bCanCallBar;
};
What we could then do is use a conditionally defined typedef, to define a
new type:
#ifdef DEBUG
typedef FuBarContract<MFuBar> FuBar;
#else
typedef MFuBar FuBar;
#endif
Some of the advantages of this technique are that our original type does not
care about the details of the contract and retains a very low complexity.
Another advantage that I see of this technique is that the Contract can
actually be applied to any type that has two Fu and Bar signatures.
What I would like to know is how well this technique accomplishes the spirit
and goals of DBC, along with its viability for production quality code.
Thanks in advance!
which is oftentimes ignored, of separating the contract (preconditions /
postconditions / invariants) from the implementation. So it would seem that
ideally we would write the class separate from the contract, and then merge
them. For a small example we could have the following class MFuBar (M for
iMplementation):
class MFuBar {
public:
Fu() { std::cout << "fu" << endl; };
Bar() { std::cout << "bar" << endl; };
};
Let's say that the contract that must be enforced is (in English): any call
to Bar must be precedeed by at least one call to Fu. We could express that
in code as follows:
template<typename T>
class FuBarContract : public T {
public:
FuBarContract() { bCanCallBar = false; };
void Fu() { bCanCallBar = true; T::Fu(); };
void Bar() { assert(bCanCallBar); T::Bar(); bCanCallBar = false; };
private:
bool bCanCallBar;
};
What we could then do is use a conditionally defined typedef, to define a
new type:
#ifdef DEBUG
typedef FuBarContract<MFuBar> FuBar;
#else
typedef MFuBar FuBar;
#endif
Some of the advantages of this technique are that our original type does not
care about the details of the contract and retains a very low complexity.
Another advantage that I see of this technique is that the Contract can
actually be applied to any type that has two Fu and Bar signatures.
What I would like to know is how well this technique accomplishes the spirit
and goals of DBC, along with its viability for production quality code.
Thanks in advance!