After going through some links on exception handling (1, 2, and 3), I know that C++ programs can throw pretty much anything as exceptions (int, char*
If you can use boost then you should do so. Refer to this link on how to use boost exceptions. You can also design your own exception class hierarchy as stated by other answers, but you need to take care of subtle aspects like 'nothrow' requirements from the 'what' method. A basic design on how this can be done on the lines of boost::exception is explained below:-
#include
#include
#include
/************************************************************************/
/* The exception hierarchy is devised into 2 basic layers.
System exceptions and Logic exceptions. But Logic exceptions are
convertible to the ultimate base 'System' in the system layer.
*************************************************************************/
// the system exception layer
namespace ExH
{
namespace System {
// This is the only way to make predefined exceptions like
// std::bad_alloc, etc to appear in the right place of the hierarchy.
typedef std::exception Exception;
// we extend the base exception class for polymorphic throw
class BaseException : public Exception {
public:
BaseException() throw() {}
explicit BaseException(char const* /*desc*/) throw()
: Exception()
{}
BaseException(BaseException const& that)
: Exception(that)
{}
virtual void raise() const { throw *this; } // used to throw polymorphically
virtual ~BaseException() throw () {}
};
// module level classes compose and catch the descriptive
// versions of layer-exceptions
class DescriptiveException : public BaseException {
public:
explicit DescriptiveException (char const* description) throw()
: description_(description)
{ }
explicit DescriptiveException (std::string const& description) throw()
: description_(description.c_str())
{ }
virtual ~DescriptiveException () throw () {}
DescriptiveException (DescriptiveException const& src) throw()
: BaseException(src)
{
this->description_ = src.description_;
}
DescriptiveException& operator= (DescriptiveException const& src) throw()
{
if (this != &src)
{
this->description_ = src.description_;
}
return *this;
}
/*virtual*/ char const* what () const throw() { return description_; }
/*virtual*/ void raise() const // used to throw polymorphically
{ throw *this; }
protected:
DescriptiveException () throw ();
private:
char const* description_;
};
}
}
// the logic exception layer
namespace ExH
{
namespace Logic
{
// Logic::Exception inherits from System::Exception for the
// following reason. Semantically for some part of the
// system particular instance of Logic::Exception may seem as
// opaque System::Exception and the only way to handle it would
// be to propagate it further. In other words Logic::Exception
// can be seamlessly "converted" to System::Exception if there is
// no part of the system interested in handling it.
//
class BaseException : public System::BaseException
{
public:
BaseException() throw() {}
explicit BaseException(char const* desc) throw()
: System::BaseException(desc)
{}
BaseException(BaseException const& that)
: System::BaseException(that)
{}
virtual void raise() const { throw *this; } // used to throw polymorphically
virtual ~BaseException() throw () {}
};
// module level classes compose and catch the descriptive
// versions of layer-exceptions
class DescriptiveException : public BaseException {
public:
explicit
DescriptiveException (char const* description) throw()
: description_(new std::string(description))
{ }
explicit
DescriptiveException (std::string const& description) throw()
: description_(new std::string(description))
{ }
DescriptiveException(DescriptiveException const& src) throw()
: BaseException(src)
{
// copy the string
std::string* str = new std::string(src.description_.get()->c_str());
description_.reset(str);
}
virtual ~DescriptiveException () throw () {}
/*virtual*/ char const* what () const throw() { return description_->c_str(); }
/*virtual*/ void raise() const { throw *this; }
private:
DescriptiveException& operator= (DescriptiveException const& src) throw(); // copy disabled
std::auto_ptr description_; // do not use std::string, as it can throw
};
}
}
/************************************************************************/
/* Users of the exception hierarchy compose specific exceptions as and
when needed. But they can always be caught at the System::Exception base
class level. Some of the standard conversion examples are demonstrated :-
class MyClass {
public:
class Exception_ {};
typedef
Compound
Exception;
class InvalidArgument_ {};
typedef
Compound
InvalidArgument;
class NotInitialized_ {};
typedef
Compound
NotInitialized;
public:
void myFunction1() const throw(NotInitialized);
void myFunctionN() const throw(NotInitialized);
};
void MyClass::myFunction1() const {
throw NotInitialized("Not Inited!");
}
void MyClass::myFunctionN() const {
try {
// call myFunction1()
}
catch(NotInitialized const& e){
// use e
}
}
This has to be per-class basis. The exposed module will have an exception
specification which will catch all the sub-class exceptions. The calling
module will in turn rely on this exception-specification. This will allow
us to have generalized exception-catching at the application-level and
more specialized exception-catching at the specific module level. */
/************************************************************************/
// a simple template to compose the exceptions as per conversion requirements
namespace ExH
{
template
class Compound : public Base
{
public:
explicit Compound (char const* description) throw ()
: Base(description)
{}
explicit Compound (std::string const& description) throw ()
: Base(description)
{}
Compound (Compound const& src) throw ()
: Base(src)
{}
virtual ~Compound () throw () {}
protected:
Compound () throw () {}
private:
Compound& operator= (Compound const& src) throw (); // disable copy
};
}