Introduction
This artcile talks about Design By Contract Technique with an example to show how it can be used in an Application.
Background
Most application problems arise because of inappropriate handling of data obtained from external systems that could be a application user, remote server, data base etc.
Defensive Programming prevents any failures or asserts that are going to be triggred, by foreseeing them or figuring out what might go wrong at each stage of the application and thus minimizes the bugs from external system. There are many techinques in this approach and Design by Contract is one among them.
Design By Contract:
This technique enforces the below contracts between blocks or functions of your code:
Pre-conditions:
These are the condtions that must always be True before a block or function of code is enetered. If this condition fails then you can think, that there is a problem with "caller function" code. Pre-conditions have to be checked for internal methods (methods internal to application/module) only.
They should not be checked for public or interface methods, where we need to have some error handling techniuqes to handle wrong inputs from external users.
Examples for Pre-condition checks could be:
Ensure that function parameters are valid before using them in the function.
Post-conditions:
These are the conditions that must hold True once a code block is left, after successfully completing it's assigned task. If this check fails then you can think that there is a problem with "called code".
Unlike to those Pre-conditions, these Post-conditions can be done on both public/interface methods and internal methods. This is to ensure the "called method" has done it's task successuly and returning the valid results too if any.
Example for Post-conditions checks could be:
Testing the validity of a pointer before returning to caller. or
Even the validity of returned results can be done by the "Called method" as part of Post-condition check.
Invariants:
These are the conditions that must hold True, every time the program's execution reaches some points like: between loop passes, between function calls etc. Failure of this test, shows there is a problem with the logic of the program.
Examples for this could be:
1. Prove that an object's state is consistent and valid before operating on itis member
functions.
This test we can have it at the beginning of every member function of the object.
2. Assert for member pointers being not NULL before dereferencing them.
Apart from having implementation of all these conditions, it is always good to explain in brief on these conditions in the code. So, that user/future developer need not inspect the implementations to understand them.
Assertions: These above conditions can be tested using assertions. So that they are available only during development and debugging time and not in customer load. This is to make sure, above contracts and our assumptions on the program are all True and it is been tested as well to prove the same, before shipping the load to customer.
Here is an Exaple code in C++ to implement the above technique:
The code
In this code, there are total 3 sets of files.
1. myMacros.h - defines the below customized macros
MY_ASSERT
IS_VALID_PTR
SET_OBJECT_OF
IS_OBJECT_OF
2. The respective static methods are implemented in below files
myRobust.cc and myRobust.h
3. Example application program that uses the Design By Contract Technique is:
designByContract.cc
For Invariants:
Here, checking the consistency of object is done - by setting a random number to the object when it gets instaniated and nullifying that value on it's deletion.
SET_OBJECT_OF macro does this.
And then test the value that was set, everytime on entring it's member functions, to make sure "this" pointer is valid.
IS_OBJECT_OF macro does this
For Pre-condition and Post-condition:
Generally to check the validity of pointers before using, that can be tested as part of Pre-condition and Post-condition tests, is done using the macro IS_VALID_PTR.
Customized MY_ASSERT macro is used generally to validate any expression.
Exaplanation on designbyContract.cc:
Here, the implementation of changeName and setName methods shows the usage of this technique.
Invariant condition is checked in all the member functions.
Pre-condition check can be seen in operator = method.
Code: Cpp
// FILE - myMacros.h
//------------------------
#define DEBUG
#include<iostream.h>
#include "myRobust.h"
#ifndef DEBUG
#define IS_VALID_PTR(ptr)
#else
#define IS_VALID_PTR(ptr) \
MyRobust::isValidPointer((unsigned int)ptr)
#endif
#ifndef DEBUG
#define MY_ASSERT(x)
#else
#define MY_ASSERT(x) \
MyRobust::logMsg(x, #x, __FILE__, __LINE__)
#endif
#ifndef DEBUG
#define SET_OBJECT_OF(instValue, thisPtr)
#else
#define SET_OBJECT_OF(instValue, thisPtr) \
MyRobust::setObjectOf(instValue, (unsigned int) thisPtr, (thisPtr)->myInstValue())
#endif
#ifndef DEBUG
#define IS_OBJECT_OF(instValue, thisPtr)
#else
#define IS_OBJECT_OF(instValue, thisPtr) \
MyRobust::isObjectOf(instValue, (unsigned int)thisPtr, thisPtr->getInstanceValue())
#endif
// FILE - myRobust.h
//--------------------
#ifndef __MyRobust_H_INCLUDED__
#define __MyRobust_H_INCLUDED__
#include <iostream.h>
class MyRobust
{
MyRobust();
MyRobust(const MyRobust & obj);
public:
static bool logMsg(bool x, const char *msg, char* file, unsigned int line);
static bool isValidPointer(unsigned int ptr);
static bool setObjectOf(unsigned int instValue, unsigned int ptr, unsigned int& myInstValue);
static bool isObjectOf(unsigned int instValue, unsigned int ptr, unsigned int myInstValue);
};
// FILE - myRobust.cc - For implementation of static methods
//-----------------------------------------------------------------
#include "myRobust.h"
#include "myMacros.h"
#define nullValue 0x1111
bool MyRobust::logMsg(bool x, const char *msg, char* file, unsigned int line)
{
if( false == x)
{
cout<<"On line " <<line<<":";
cout<<" in file " <<file <<":";
cout<<" Error !! Assert "<<msg << " failed\n\n";
abort();
return(true);//---
}
else
{
return(true);//---
}
}
bool MyRobust::setObjectOf(unsigned int instValue, unsigned int ptr, unsigned int& myInstValue)
{
MY_ASSERT((0 != ptr) && (NULL != ptr));
if(0 == instValue)
{
cout<<"myInstValue ID is not correct...\n";
return (false);
}
else
{
//set myInstValue
myInstValue = instValue;
return (true);
}
}
bool MyRobust::isObjectOf(unsigned int instValue, unsigned int ptr, unsigned int myInstValue)
{
MY_ASSERT(0 != ptr && NULL != ptr);
if(instValue != myInstValue)
{
cout<<"Current InstValue: "<<myInstValue<<" and Refered InstValue: "<<instValue<<" does not match\n";
return (false);
}
else
{
return (true);
}
}
bool MyRobust::isValidPointer(unsigned int ptr)
{
if ((ptr == 0) && (NULL == ptr))
{
return(false);//---
}
else
{
return(true);//---
}
}
// FILE - designByContract.cc - Example for using in an Application
//-------------------------------------------------------------------------
#include<iostream.h>
#include "myMacros.h"
#include "myRobust.h"
#define nullValue 0x1111
class MyClass
{
unsigned int myInstValue_m;
char * name;
public:
//accessors
unsigned int getInstanceValue();
unsigned int &myInstValue();
//constructors
MyClass();
MyClass(const MyClass & obj);
//destructor
~MyClass();
//assignment operator
MyClass & operator =(const MyClass &obj);
void dispName();
void deleteName();
void changeName(char *name);
void setName(char *name);
};
unsigned int MyClass::getInstanceValue(void)
{
return (myInstValue_m); //---
}
unsigned int & MyClass::myInstValue(void)
{
return (myInstValue_m); //---
}
MyClass::MyClass()
{
//set the random number
SET_OBJECT_OF(0x10, this);
name = new char[8];
strcpy(name, "Mridula");
}
MyClass::MyClass(const MyClass & obj)
{
//set the random number
SET_OBJECT_OF(0x10, this);
name = new char[8];
name = obj.name;
}
MyClass &
MyClass::operator =(const MyClass &obj)
{
//check the validity of object
MY_ASSERT(IS_OBJECT_OF(0x10, this));
cout<<"\nEntering MyClass::operator =\n";
//Pre-condition check - obj != this
MY_ASSERT(this != &obj);
name = new char[8];
name = obj.name;
return(*this); //---
}
MyClass::~MyClass()
{
if(name)
{
delete (name);
}
//nullify the value
SET_OBJECT_OF(nullValue, this);
}
void MyClass::dispName()
{
//check the validity of object
MY_ASSERT(IS_OBJECT_OF(0x10, this));
cout<<"\nEntering MyClass::dispName\n";
//check the validity of pointer name
MY_ASSERT(IS_VALID_PTR(name));
cout<<"Name is: "<<name<<"\n";
}
void MyClass::changeName(char *name)
{
//check the validity of object
MY_ASSERT(IS_OBJECT_OF(0x10, this));
cout<<"\nEntering MyClass::changeName\n";
if(NULL != name)
{
//calling an internal method to set the name
setName(name);
//This check can be done here as well, if it is not done in setName method.
//Post-condition check on the result
//MY_ASSERT(IS_VALID_PTR(name));
}
else
{
cout<<"Please input a proper name...\n";
}
}
void MyClass::setName(char * newName)
{
//check the validity of object
MY_ASSERT(IS_OBJECT_OF(0x10, this));
cout<<"\nEntering MyClass::setName\n\n";
//Pre-condition check on argument
MY_ASSERT(IS_VALID_PTR(newName));
deleteName();
name = new char(sizeof(newName)+1);
//change the name
strcpy(name, newName);
//Post-condition check on the result
MY_ASSERT(IS_VALID_PTR(name));
}
void MyClass::deleteName(void)
{
//check the validity of object
MY_ASSERT(IS_OBJECT_OF(0x10, this));
cout<<"\nEntering MyClass::deleteName\n\n";
//check the validity of pointer name
MY_ASSERT(IS_VALID_PTR(name));
delete (name);
name=NULL;
}
int main()
{
unsigned int input;
cout<<"\nDesign By Contract Design Technique...\n\n";
cout <<"Please enter a valid number: \n";
cout<<"1 - Invariant Check - To check the validity of an Instance \n";
cout<<"2 - Pre and Post condition usage example \n";
cout<<"3 - Pre-condition Check - To check the self assignment\n";
cout<<"4 - Post-condition Check - To check the validity of a Pointer\n";
cout<<"5 - Error Handling for wrong input in public function\n";
cin>>input;
cout<<"\n";
cout << "You entered the number " << input << ".\n\n";
switch(input)
{
case 1:
{
MyClass *obj1;
obj1 = new MyClass();
obj1->dispName();
delete (obj1);
cout<<"\nTrying to access the func from Object that is deleted ...\n";
//calling the func on invalid obj
obj1->dispName();
break; //---
}
case 2:
{
MyClass *obj2;
obj2 = new MyClass();
cout<<"Current name is:\n";
cout<<"----------------------\n";
obj2->dispName();
cout<<"\n";
char *name = new char(sizeof("Manaswita"));
strcpy(name, "Manaswita");
obj2->changeName(name);
cout<<"Changed the name to:\n";
cout<<"----------------------\n";
obj2->dispName();
cout<<"\n";
delete(name);
delete(obj2);
break; //---
}
case 3:
{
MyClass obj2;
cout<<"\nTrying to Assign to the same object ...\n";
obj2 = obj2;
break;//----
}
case 4:
{
MyClass *obj2;
obj2 = new MyClass();
obj2->dispName();
obj2->deleteName();
cout<<"\nTrying to display the name which is NULL ...\n";
obj2->dispName();
delete(obj2);
break; //---
}
default:
{
MyClass *obj2;
obj2 = new MyClass();
cout<<"\nTrying to change the name to NULL ...\n";
char *name;
name = NULL;
obj2->changeName(name);
delete(obj2);
break; //---
}
}
return (0); //---
}
1. output:
--------------
Design By Contract Design Technique...
Please enter a valid number:
1 - Invariant Check - To check the validity of an Instance
2 - Pre and Post condition usage example
3 - Pre-condition Check - To check the self assignment
4 - Post-condition Check - To check the validity of a Pointer
5 - Error Handling for wrong input in public function
1
You entered the number 1.
Entering MyClass::dispName
Name is: Mridula
Trying to access the func from Object that is deleted ...
Current InstValue: 4369 and Refered InstValue: 16 does not match
On line 91: in file designByContract.cc: Error !! Assert IS_OBJECT_OF(0x10, this) failed
Abort (core dumped)
2. output:
----------------
Design By Contract Design Technique...
Please enter a valid number:
1 - Invariant Check - To check the validity of an Instance
2 - Pre and Post condition usage example
3 - Pre-condition Check - To check the self assignment
4 - Post-condition Check - To check the validity of a Pointer
5 - Error Handling for wrong input in public function
2
You entered the number 2.
Current name is:
----------------------
Entering MyClass::dispName
Name is: Mridula
Entering MyClass::changeName
Entering MyClass::setName
Entering MyClass::deleteName
Changed the name to:
----------------------
Entering MyClass::dispName
Name is: Manaswita
3. output:
---------------
Design By Contract Design Technique...
Please enter a valid number:
1 - Invariant Check - To check the validity of an Instance
2 - Pre and Post condition usage example
3 - Pre-condition Check - To check the self assignment
4 - Post-condition Check - To check the validity of a Pointer
5 - Error Handling for wrong input in public function
3
You entered the number 3.
Trying to Assign to the same object ...
Entering MyClass::operator =
On line 69: in file designByContract.cc: Error !! Assert this != &obj failed
Abort (core dumped)
4. output:
-------------
Design By Contract Design Technique...
Please enter a valid number:
1 - Invariant Check - To check the validity of an Instance
2 - Pre and Post condition usage example
3 - Pre-condition Check - To check the self assignment
4 - Post-condition Check - To check the validity of a Pointer
5 - Error Handling for wrong input in public function
4
You entered the number 4.
Entering MyClass::dispName
Name is: Mridula
Entering MyClass::deleteName
Trying to display the name which is NULL ...
Entering MyClass::dispName
On line 95: in file designByContract.cc: Error !! Assert IS_VALID_PTR(name) failed
Abort (core dumped)
5. output:
--------------
Design By Contract Design Technique...
Please enter a valid number:
1 - Invariant Check - To check the validity of an Instance
2 - Pre and Post condition usage example
3 - Pre-condition Check - To check the self assignment
4 - Post-condition Check - To check the validity of a Pointer
5 - Error Handling for wrong input in public function
5
You entered the number 5.
Trying to change the name to NULL ...
Entering MyClass::changeName
Please input a proper name...
References
ASSERTs in C++



