Attemps at Contracting

Started by
4 comments, last by raptorstrike 17 years, 9 months ago
For those unfamiliar with contracting: A contract is a set of conditions which a function or set of statements must agree to uphold. Preconditions are specified which must be met in order for the subsequent statements to be processed. Post conditions must be met at the closing of the contract. If either part of this contract is violated it is an error, it shouldnt happen and means that the function was passed invalid data, thus making contracts a debugging tool. Here is my admitedly weird and brewt force approach to contracting in C++ //CONTRACT.H

#ifndef _CONTRACT
#define _CONTRACT

class Test_Condition_Violation
{
    public:
        Test_Condition_Violation(int nLINE, const char*nFILE) : LINE(nLINE), FILE(nFILE)
        {
        }
        int LINE;
        const char* FILE;
};
class Contract
{
    public:
    Contract(const char* nfile, int nline) : file(nfile), line(nline)
    {
    }  
    void Test_Conditions(bool a, bool b = true, bool c = true, bool d = true, bool e = true, bool f = true, bool g = true, bool h = true, bool i = true,   
               bool j = true, bool k = true, bool l = true, bool m = true, bool n = true, bool o = true, bool p = true, bool q = true, bool r = true, 
               bool s = true, bool t = true, bool u = true, bool v = true, bool w = true, bool x = true, bool y = true, bool z = true, 
               bool a1 = true, bool b1 = true, bool c1 = true, bool d1 = true)
    {
            if(a && b && c && d && e && f && g && h && i && j && k && l && m 
                 && n && o && p && q && r && s && t && u && v && w && x && y && z && a1 && b1 && c1 && d1)
            {
            }
            else
            {
                Test_Condition_Violation Violation(line, file);
                throw(Violation);
            }
    }
    const char* file;
    int line;
};

#define Contract Contract Binding(__FILE__, __LINE__); Binding.Test_Conditions
#define Close_Contract Binding.line = __LINE__; Binding.file = __FILE__; Binding.Test_Conditions
#endif






everything next to Contract in parenthesis are the preconditions everything next to Close_Contract is the post conditions a violation of either will throw exception Test_Condition_Violation //EXAMPLE

#include "contractor.h"
#include <iostream>
using namespace std;
int main()
{
    int X = 0;
    int Y = 0;
    try
    {
        Contract(X == 0);
        X += 9;
        Close_Contract(X == 10);
    }
    catch(Test_Condition_Violation &e)
    {
      cout << "Contract violation at line " << e.LINE << endl << e.FILE<< endl;
    }
    system("pause");
    return 0;
}








the maximum number of contract arguments are 30, I would like any feed back on this, even if its "just accept that c++ isnt a DBC language and move on" thanks for your time [smile] [Edited by - raptorstrike on July 4, 2006 3:49:04 PM]
____________________________"This just in, 9 out of 10 americans agree that 1 out of 10 americans will disagree with the other 9"- Colin Mochrie
Advertisement
Looks like you've fallen into the NIH (Not Invented here) trap [smile].
Have a look at the boost test tools for example. CppUnit offers similar facilities to support design by contract in C++.

Your implementation looks weird (if not clumsy[smile]), indeed. I'd suggest exploring the most effective use of existing solutions instead. Trying to come up with your own approach is great for learning purposes, but IMHO using existing and proven solutions and finding out as well as documenting how those can help with DBC is more fruitful.

Just my .02€,
Pat.
1) I like the idea, and the minimal implementation. I may use it. Its more appealing to copy-paste a small code-snippet than to download and learn a full blown testing library.

2) whats the use of the variable "post"? I understand its true for post conditions, but you dont put it in the exception and it doesnt affect the logic.

3) some macro issues:

if someone writes:

if (check) Contract(a==5);
or
for (i=1;i++<10;) Contract(a==0);

the code will mess up since "Contract" is being replaced by multiple statements. One way to fix that would be to surround the macro implementation with curly braces but that will break the nifty passing of arguments to TestCondition.
Maybe if you use "," instead of ";" it will work? Im not sure.

4) You can use the precompiler to easily turn on and off the contract checking-

put on top #define CONTRACT_ENABLED true
surround the whole contract code with #if CONTRACT_ENABLED
and for the macros write this:

#if CONTRACT_ENABLED
#define Contract .... // same as before
#define Close_Contract ... //same as befpre
#else
#define Contract ; /* compiles to nothing */
#define Close_Contract ;
#end

this way one can turn Contract checking off in one line.



Iftah.
Thanks for the response.
The post variable was origonally used to check if a post condition was ever set for the contract, this variable would be checked when the contract went out of scope and would throw an unfufilled contract exception but I wasnt sure how to handle a case where the post wasnt defined, you could put a try/catch around the function within the calling function but that seemed like too many try/catch blocks. It can be safely removed (post). I am thinking about implementing an overloaded Test_Conditions() with one extra parameter, a function to be called to handle an error that has arisen rather then throw an exception, or maybe an overload that takes a custom exception to be thrown. I will look into the multiple line issue for more flexible use of the contract. thanks again for the feedback.

To put the contract in an if statement is a missuse anyway, if the condition is true nothing will be done, if its not true an appropriate exception or function will be called. So really you can look at it like this

{
if(Everything in the contract is true)
everything went as planed, go on with the program
else
handle error
}
[/cdde]

I am happy with the functionality so far, if I need something more complete I will look at one of the above librarys suggested, if you wish to persure this further PM me. I have added custom exception throwing and custom function calls on failure. However the problem with single line loops still exists.

[Edited by - raptorstrike on July 4, 2006 4:13:34 PM]
____________________________"This just in, 9 out of 10 americans agree that 1 out of 10 americans will disagree with the other 9"- Colin Mochrie
As long as you're doing it yourself:

- Don't use a leading underscore and capital letter for your include guard.

- Inherit your exception from std::exception, or perhaps std::runtime_error. Overload what() accordingly. (Note that you don't get to use nice C++ tools to concatenate the file name and line number, because you don't want the possibility of throwing in the exception constructor due to bad-alloc, so use the preprocessor to do the join.) You can then use temporary objects to simplify the macro a bit. After all, there isn't really any state being held in the object...

- I wouldn't use that system for accepting multiple parameters. Instead, overload an operator() and implement chaining:

#ifndef CONTRACT_H#define CONTRACT_H#include <stdexcept>class Test_Condition_Violation : public std::runtime_error {};class Contract {  Test_Condition_Violation to_throw;  public:  Contract(const char* msg) : to_throw(msg) {}  Contract& operator()(bool condition) {    if (!condition) {      throw to_throw;    }    return *this;  }};// I'm not sure of the macro stringification/concatenation stuff... :/#define Precondition Contract(__FILE__##"(line ##__LINE__##)")#define Postcondition Contract(__FILE__##"(line ##__LINE__##)")#endif// and example:#include "contract.h"#include <iostream>int main() {  int X = 0;  int Y = 0;  try {    Precondition(X == 0)(Y == 0);    X += 9;    Postcondition(Y == 0)(X == 10);  } catch(Test_Condition_Violation &e) {    std::cout << "Contract violation at " << e.what() << std::endl;  }}


But honestly, I would just use assertions :P
Yes that seems like a more elequent solution, I was thinking about overloading the () operator but couldnt find anything on it. I don't overload operators frequently.
____________________________"This just in, 9 out of 10 americans agree that 1 out of 10 americans will disagree with the other 9"- Colin Mochrie

This topic is closed to new replies.

Advertisement