August 1, 2007
////////////////////////////////////////////////////////////////////////////////////
// MANDATORY ERROR CODES REVISITED.
// http://www.ddj.com/dept/cpp/191601612
//
// This is implementation is copied from an article I had read about forcing the use of
// error codes in C++ programs. I'm not a fan of error codes in general,
// because I think people should use exceptions instead, but the exception
// model in C++ is so hard to get right, that sometimes I have no choice but to
// use error codes. However, even error codes lose their so called convenience when
// not taken care of. Hence the use of this framework.
//
////////////////////////////////////////////////////////////////////////////////////
#pragma once
#include <string>
#include <stdexcept>
namespace return_code_usage
{
///////////////////////////////////////////////////////////////////////////////
// A simple class to indicate that a return value can be ignored. Used by a
// programmer who is sure about s/he is doing.
///////////////////////////////////////////////////////////////////////////////
struct ignore_return_code{};
///////////////////////////////////////////////////////////////////////////////
// Simple exception class that is thrown when a return value is not handled
// correctly.
///////////////////////////////////////////////////////////////////////////////
class unhandled_return_code_exception
: public std::exception
{
public:
unhandled_return_code_exception()
: err_text_("")
{
}
unhandled_return_code_exception(const std::string &err_text)
: err_text_(err_text)
{
}
const char *what( ) const{
return err_text_.c_str();
}
~unhandled_return_code_exception(){/* */}
private:
std::string err_text_;
};
///////////////////////////////////////////////////////////////////////////////
// This is the class that wraps the return code. If its not recieved by a
// return_code class then the exception will not be disarmed, and an exception
// will be thrown in the destructor.
//
// By having a sepereate class with a exception-throwing exception, it is ensured
// that should a function returning a throwable_return_code object, throws an exception,
// then an exception from throwable_return_code's destructor is not thrown, when
// the stack is unwinding.
///////////////////////////////////////////////////////////////////////////////
template <class CODE>
class throwable_return_code
{
template<class CODE> friend class return_code;
public:
// constructor recieve the code and arm the exception
throwable_return_code(CODE r_code)
: code_(r_code)
, throw_(true)
{
}
//explicitly ignore error codes and avoid exception
operator ignore_return_code(){
throw_ = false;
return ignore_return_code();
}
~throwable_return_code(){
//will throw unless something is done to prevent it.
if (throw_){
throw unhandled_return_code_exception();
}
}
private:
CODE code_;
bool throw_;
};
///////////////////////////////////////////////////////////////////////////////
// Simple class that recieved the throwable_return_code, and disarms the
// throwable_return_code exception-throwing destructor. This class is only used
// by a developer who knows what s/he is doing when disarming the exception.
///////////////////////////////////////////////////////////////////////////////
template<typename CODE_T>
class return_code
{
typedef CODE_T code_type;
public:
// Explicit ctor to make sure that hte user of this class
// knows what s/he is doing
explicit return_code(throwable_return_code<code_type> &code)
: code_(code.code_)
{
code.throw_ = false;
}
~return_code(){ }
operator code_type (){
return code_;
}
private:
code_type code_;
};
}
Here is test implementation.
#include <iostream>
#include "return_code_usage.hpp"
using namespace std;
using namespace return_code_usage;
void print_function_name(std::string function_name)
{
std::cout
<< std::string(function_name.size(), '-')<<std::endl
<< function_name <<std::endl
<< std::string(function_name.size(), '-')<<std::endl;
}
#define PRINT_FUNCTION_NAME print_function_name(__FUNCTION__)
throwable_return_code<int> fallible_function(){
PRINT_FUNCTION_NAME;
return 1;
}
struct point_class{
int x,y;
point_class(int x_, int y_)
: x(x_), y(y_){}
};
// For custom types, need to have overloaded operators for comparison
// not needed for basic types.
bool operator == (const point_class &a, const point_class &b){
return (a.x == b.x) && (a.y==b.y);
}
throwable_return_code<point_class> another_fallible_function(){
PRINT_FUNCTION_NAME;
point_class p(1,2);
return p;
}
int main(int argc, char *argv[]){
// Okay
return_code<int> result1(fallible_function());
//This doesn'nt work with an explicit constructor
return_code<int> result2 = (return_code<int>)fallible_function();
// OK
if((return_code<int>(fallible_function()) == 0)){
}
// OK
if (0==(return_code<int>)fallible_function()){
}
//OK because explicitly ignoring error
(ignore_return_code)fallible_function();
//OK
return_code<point_class> result3 = (return_code<point_class>)another_fallible_function();
//OK - with thedefinition of overloaded equality operator
if (return_code<point_class>(another_fallible_function())==point_class(1,2)){
}
// OK with overloaded equality operator.
if (point_class(1,2) == (return_code<point_class>)another_fallible_function()){
}
// will throw an exception
fallible_function();
return 0;
}
Leave a Comment » |
C++, Computer science, Programming |
Permalink
Posted by bybitsandbytes