A simple scheme to implement Design by Contract in C++

-

Recently I got in­ter­ested in C++ again. The new lambda func­tions in C++ 11 open up a world of op­por­tu­ni­ties for C++ pro­gram­mers. I’ll talk more about how you can write func­tional code in C++ 11 in up­com­ing posts. For now let’s look at de­sign by con­tract.

Design by con­tract is a de­vel­op­ment style pro­moted by  Bertrand Meyer and it is im­ple­mented in his own Eiffel pro­gram­ming lan­guage. At core, it ad­vo­cates us­ing pre­con­di­tions, post­con­di­tions and in­vari­ants.

An in­vari­ant is an as­ser­tion that al­ways holds true for a class af­ter the class has been fully con­structed and if the code is not ex­e­cut­ing in­side a method. As a user of the class, you al­ways ob­serve the in­vari­ant to be true. As an im­ple­menter of the class, you can be as­sured that the in­vari­ant is true be­fore a method is en­tered and you need to make the in­vari­ant true again by the time your method ex­its.

A pre­con­di­tions is an as­ser­tion that needs to hold true at the start of a func­tion, for the post­con­di­tion to be true at the end of it. Taken to­gether, in­vari­ant, pre­con­di­tion and post­con­di­tion de­fine the con­tract be­tween the im­ple­menter and the user of a class.

Code for this post is here and here. Thanks to Andy Sawyer, Steve Bower and Ganesh Sittampalam for re­view­ing my code and sug­gest­ing im­prove­ments.

Preconditions are sim­ple and every­one uses them. They are those lit­tle if state­ments that you put at the start of your func­tions to make sure that the caller has given you the right pa­ra­me­ters.

double divide(double x, double y) {
    if(y == 0) throw new exception(“y cannot be 0”);
}

These lit­tle if’ state­ments don’t re­ally make the pre­con­di­tion stand out. They can be con­fused with other, un­re­lated, if’ state­ments that do com­pletely dif­fer­ent se­man­tic things. A more read­able al­ter­na­tive is:

double divide(double x, double y) {
    requires(y != 0);
    
}

Not an im­pres­sive dif­fer­ence, for sure, but kind of nice. The evil macro looks like this:

#ifndef ___PRECOND
#define requires(F) {if((!(F))) throw preexception(__FILE__, __LINE__,"Pre-condition failure: " #F);};
#else
#define requires(F)
#endif

Note that the ex­cep­tion main­tains in­for­ma­tion not just about the file and line num­ber of the fail­ure, but also a tex­tual rep­re­sen­ta­tion of the failed con­di­tion. Such things you can do with macro mag­ick.

Postconditions are trick­ier. In the case of a side-ef­fect free (pure) func­tion, a post­con­di­tion as­serts some­thing of in­ter­est about the re­turn value. In the case of a class, it as­serts some­thing of in­ter­est about the state of the class be­fore and af­ter the ex­e­cu­tion of the method.

Let’s start with a pure func­tion. I like to have all my as­ser­tion at the start of the func­tion to al­low rea­son­ing about it with­out look­ing at im­ple­men­ta­tion de­tails. But that poses the prob­lem that the re­sult is avail­able just at the end of the func­tion.  My so­lu­tion is to en­force this id­iom:

double divide(double x, double y) {
    double result;
    requires(y != 0);
    ensures(result < x); // Silly, just to falsify it in tests
return result; }

So you need to de­clare your re­sult up­front. That is the biggest lim­i­ta­tion of the over­all so­lu­tion in my opin­ion.  If that is ac­cept­able to you, the trick now is how to ex­e­cute the post­con­di­tion test be­fore the method ex­its. We can do that by stor­ing a lambda and ex­e­cut­ing it in the de­struc­tor:

typedef std::function<bool ()> ___dbcLambda;
class ___post {
public:
    ___post(const char *file, long line, const char *expr, const ___dbcLambda& postF)
        : _f(postF),
          _file(file),
          _line(line),
          _expr(expr)
    {}
    ~___post()
    {
        if( !std::uncaught_exception() && !_f() )
        {
            throw postexception(_file,_line,_expr);
        }
    }
private:
    const ___dbcLambda _f;
    const char * const _file;
    const long _line;
    const char * const _expr;
};

You might think that you should­n’t throw ex­cep­tions in a de­struc­tor. That is some­thing I never un­der­stood about the RAII pat­tern in C++. If I choose to use ex­cep­tions as my er­ror no­ti­fi­ca­tion method, how am I sup­posed to get no­ti­fied if there is a prob­lem re­leas­ing a re­source in RAII, other than by throw­ing an ex­cep­tion in the de­struc­tor?

Maybe be­cause of this, the stan­dard has an un­caugh­t_ex­cep­tion() func­tion that al­lows you to check if an ex­cep­tion has been thrown, so that you don’t throw an­other one dur­ing stack un­wind­ing. If you re­ally don’t like throw­ing in the de­struc­tor, feel free to as­sert.

You might be wor­ried about per­for­mance, but you re­ally should­n’t as you can dis­able all these macros in Release.

The macro then cre­ates a ___post class on the stack.

#define ensures(F) \
    int ___UNIQUE_LINE = __LINE__;  \
    auto ___UNIQUE_POST = ___post( __FILE__, __LINE__, "Post-condition failure:" #F, [&](){return (F);});

The UNIQUE stuff is messy busi­ness. Part of it is by de­sign and it is used to make sure that each __post vari­able has a unique name to have mul­ti­ple ensures’ in a func­tion. The other part is a workaround for this msvc bug. Let me know if you want more de­tails. I sus­pect there is a bet­ter way to do it.

Here is the full en­chi­lada …

#define ___MERGE(a, b) a##b
#define ___POST(a) ___MERGE(___postcond,a)
#define ___UNIQUE_POST ___POST(__LINE__)
#define ___LINE(a) ___MERGE(___line, a)
#define ___UNIQUE_LINE ___LINE(__LINE__)

The case in which a post­con­di­tion is used in­side a method of a class is even trick­ier be­cause the post­con­di­tion must be able to com­pare the state of the class at the en­trance of the method to the state of the class at its exit. Assuming a Counter ob­ject with an Add method and as­sum­ing ___pre’ cap­tures the state of the counter at the start of the method, you’d like to write some­thing like:

    void Add(int x) {
        ensuresClass(this->c_ == ___pre.c_ + x);
}

Now, this is tricky. The only way to cap­ture the old’ state in ___pre’ is by mak­ing a copy of it and store it there. This is what the code be­low does:

#define ensuresClass(F) \
    auto ___pre(*this); \
    auto ___UNIQUE_POST = ___post( __FILE__, __LINE__, "Post-condition failure: " #F, [&](){return (F);});

More trou­bling is the pos­si­bil­ity that the class does­n’t have a copy con­struc­tor. In that case you ex­plic­itly need to as­so­ci­ate a value with ___pre2’ by pass­ing it as the first pa­ra­me­ter to the ap­pro­pri­ate macro as in the code be­low:

    void Add(int x) {
        ensuresClass2(this->c_, c_ == ___pre2 + x);
    }

Which is im­ple­mented as fol­lows:

#define ensuresClass2(ASS,F) \
    auto ___pre2(ASS); \
    auto ___UNIQUE_POST = ___post( __FILE__, __LINE__, "Post-condition failure: " #ASS " is ___pre2 in " #F, [&](){return (F);});

And I know about the gi­ant ass …

Now for in­vari­ants. The user should im­ple­ment an is­Valid() method on his class as be­low:

    bool isValid() { return c_ >= 0;}

Then he should add an invariant()’ call at the start of each method, at the end of each con­struc­tor and at the start of each de­struc­tor:

    void Add(int x) {
        invariant();
        requires(x < 10);
        ensures(this->c_ == ___pre.c_ + x);
}

This calls the isValid’ func­tion at the start of the method and at the end of it us­ing the same de­struc­tor trick:

#define invariant() \
    if(!(this->isValid())) throw preexception(__FILE__, __LINE__,"Invariant failure"); \
    auto ___UNIQUE_INV = ___post( __FILE__, __LINE__, "Invariant failure", [&](){return this->isValid();});

All the above ma­chin­ery is not at all equiv­a­lent to hav­ing such con­structs in the lan­guage, but it is sim­ple enough and with a de­cent enough syn­tax to be in­ter­est­ing.

Now a caveat: I have no idea if any of this works. It does work in my ex­am­ples and its be­hav­iour seems rea­son­ably safe to me, but I haven’t tried it out on any big code­base and haven’t stressed it enough for me to be con­fi­dent rec­om­mend­ing its us­age. So, use it at your own risk, let me know how it goes.

Tags

11 Comments

Comments

Generally, I like the idea.
But you might want to con­sider what Herb Sutter has to say about this: http://​www.gotw.ca/​gotw/​047…
Ben

Thanks for the link. It is hard to dis­agree with Herb, but I’ll try :-)
He does­n’t like us­ing un­caugh­t_ex­cep­tion in a de­struc­tor on prac­ti­cal and moral grounds.
1. On prac­ti­cal grounds, do­ing so is not com­pos­able in cer­tain sce­nar­ios. Meaning that us­ing RIIA ob­jects that rely on it in de­struc­tors needs to be done care­fully. That is true, but that does­n’t dis­qual­ify the so­lu­tion. Many things are use­ful, but need to be used care­fully. In the case ex­posed in this blog, that is not a prob­lem. The be­hav­ior would be cor­rect.
2. On moral grounds, you of­ten need to re­port er­rors dif­fer­ently if you are al­ready in an er­ror con­di­tion. I.E. you don’t want to send e-mail to your user twice, you don’t want to re­port the sec­ond (bogus) er­ror in a parser, you don’t want to al­lo­cate mem­ory if you got an out-of-mem ex­cep­tion etc …
More gen­er­ally, I fail to see a bet­ter way to man­age er­rors in re­leas­ing re­sources in RIIA. All the ones I’ve seen (i.e. log­ging to file) are gross hacks that don’t com­pose at all.
But if you still are un-con­vinced by my ar­gu­ment, feel free to change the throw­ing code with an as­sert and things work pretty much the same.

Yes, I would def­i­nitely use an as­ser­tion, by­pass­ing this whole prob­lem.
But I won­der why you use ex­cep­tions at all? From my un­der­stand­ing (which might be wrong, as I have never read any books about dbc) a vi­o­lated con­tract in­di­cates a pro­gram­mer’s er­ror (a bug), not an ex­cep­tional sit­u­a­tion. Thus, if your post-con­di­tion is vi­o­lated your data is very likely to be in an in­con­sis­tent state. How would you even re­act to such a dbc-ex­cep­tion be­ing caught?
Ben.

If you leave them on in Release mode (which seems to be more fre­quently done for pre­con­di­tion), then you want to treat them as any other er­ror (i.e. clean up re­sources, log­ging and re­port­ing to user).
In Debug, it prob­a­bly does­n’t makes a dif­fer­ence. I be­lieve that’s true of ex­cep­tions in gen­eral.

You should dis­tin­guish care­fully be­tween run­time er­rors that lie out­side of your pro­gram (occurring most of­ten when ac­quir­ing or ac­cess­ing a re­source, e.g. a file can not be opened) and bugs (incorrect code, in this case con­tract vi­o­la­tions).
The first case can and should be treated via ex­cep­tions. These can be han­dled at run-time. The lat­ter can not be safely dealt with via ex­cep­tion han­dling, as your pro­gram is al­ready in an in­con­sis­tent state. If un­lucky, you might to­tally crash your pro­gram when throw­ing an ex­cep­tion as your de­struc­tors’ pre­con­di­tions might be in­valid. If even un­luck­ier, you might end up with a pro­gram whose data only gives the im­pres­sion of be­ing con­sis­tent.
My take on vi­o­lated con­tracts in re­lease mode would be to im­ple­ment a func­tion­al­ity that is or­thog­o­nal to ex­cep­tion han­dling by pro­vid­ing an ap­pro­pri­ate call­back func­tion. This call­back func­tion would then in­form the user, log the er­ror, try to write out any data that could help the user re­store is work later on (which might al­ready fail badly), pos­si­bly clean up and fi­nally… call std::ter­mi­nate().
Ben.

It is not pos­si­ble to draw the line so sharply. What is a re­source er­ror for one user of your li­brary, might be a cod­ing er­ror for an­other. That’s why most pro­gram­ming en­vi­ro­nent (i.e. .NET, Java, PHP, …) have an in­valid ar­gu­ment ex­cep­tion that gets thrown when a pre­con­di­tion is vi­o­lated.
You dis­tin­guish be­tween the two cases by catch­ing the dif­fer­ent kinds of ex­cep­tion and do­ing some­thing dif­fer­ent about them. The catch­ing is done out­side of your li­brary, which is the only place where you have enough in­for­ma­tion to re­ally know what to do. Who knows, for the space shut­tle, call­ing ter­mi­nate() for a bug might not be the right ac­tion. You don’t know what to do, the user of your li­brary does.

I see your point.
Although I would try to treat the two er­ror sources dif­fer­ently in pro­duc­tion code, I agree that in li­brary code it might not al­ways be pos­si­ble to make a clear dis­tinc­tion.
Thanks for the feed­back.
Ben.

…. though hav­ing thought about it for a sec­ond I would tend to go for the call­back ap­proach in­stead of the ex­cep­tion path in li­brary code: After all, you can still im­ple­ment a call­back han­dler which throws an ex­cep­tion. But it’s not pos­si­ble the other way around.

Frankly, I don’t see the ad­van­tages of a call­back ap­proach com­pared to an ex­cep­tion ap­proach. In both cases you let the user de­cide which code runs when an er­ror oc­curs. Both ap­proaches let’s you dif­fer­en­ti­ate be­tween dif­fer­ent cat­e­gories of er­rors. The call­back ap­proach has the draw­backs of be­ing sig­nif­i­cantly more cum­ber­some to use.
Most modern’ li­braries out there (.net, java, etc…) go for the ex­cep­tion ap­proach. Most of the old style ones go for a returning an er­ror code’ ap­proach. There are ad­van­tages and dis­ad­van­tages in both ap­proaches (topic for an­other post), but I don’t see the need for a third one.
Good (?) thing about C++ is that it al­lows us vast lat­i­tude on how we do things …

In both cases you let the user de­cide which code runs when an er­ror oc­curs. Both ap­proaches let’s you dif­fer­en­ti­ate be­tween dif­fer­ent cat­e­gories of er­rors.“
That is ex­actly not the case. You have no idea what de­struc­tors will be called.
…I don’t see the need for a third one.“
It’s ac­tu­ally not a new ap­proach. Think of is as en­abling as­ser­tions for your re­lease build (maybe the term call­back was mis­lead­ing). Also, when op­er­a­tor new() fails a sim­i­lar ap­proach is taken in call­ing an er­ror han­dler, which can be changed us­ing std::set_new_han­dler().
From my point of view ex­cep­tions sig­nal er­ror con­di­tions that might be re­cov­er­able. Now while we can dis­cuss about pre-con­di­tions this is clearly not the case when your class in­vari­ant is bro­ken or your post-con­di­tion is in­valid. And then you’re try­ing to fix bugs at run­time.
Ben.

Hi Ben,
Le’ts just agree to dis­agree here. I think we are run­ning in cir­cles.
In the end, it is triv­ial to plug what­ever er­ror re­port­ing mech­a­nism you feel is best in this frame­work.
Cheers,
.luca