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*
Usually you should derive your own exception classes from std::exception and its derivatives to represent errors relevant to your application domain, for example if you deal with files you should have FileNotFoundException that includes the filepath and other relevant information, this way you can create catch blocks to handle certain error types and leave out others, you should also refrain from throwing or catching non-class exceptions.
Have a look at similar exception hierarchies in .NET and Java to see how to model general errors(files errors, IO errors, network errors, etc)
I thought it might be interesting to post some real code for a change. This is the exception class my own utility library uses:
//---------------------------------------------------------------------------
// a_except.h
//
// alib exception handling stuff
//
// Copyright (C) 2008 Neil Butterworth
//---------------------------------------------------------------------------
#ifndef INC_A_EXCEPT_H
#define INC_A_EXCEPT_H
#include "a_base.h"
#include <exception>
#include <sstream>
namespace ALib {
//------------------------------------------------------------------------
// The only exception thrown directly by alib
//------------------------------------------------------------------------
class Exception : public std::exception {
public:
Exception( const std::string & msg = "" );
Exception( const std::string & msg, int line,
const std::string & file );
~Exception() throw();
const char *what() const throw();
const std::string & Msg() const;
int Line() const;
const std::string & File() const;
private:
std::string mMsg, mFile;
int mLine;
};
//------------------------------------------------------------------------
// Macro to throw an alib exception with message formatting.
// Remember macro is not in ALib namespace!
//------------------------------------------------------------------------
#define ATHROW( msg ) \
{ \
std::ostringstream os; \
os << msg; \
throw ALib::Exception( os.str(), __LINE__, __FILE__ ); \
} \
} // namespace
#endif
And this is the .cpp file:
//---------------------------------------------------------------------------
// a_except.h
//
// alib exception handling stuff
//
// Copyright (C) 2008 Neil Butterworth
//---------------------------------------------------------------------------
#include "a_except.h"
using std::string;
namespace ALib {
//---------------------------------------------------------------------------
// exception with optional message, filename & line number
//------------------------------------------------------------------------
Exception :: Exception( const string & msg )
: mMsg( msg ), mFile( "" ), mLine(0) {
}
Exception :: Exception( const string & msg, int line, const string & file )
: mMsg( msg ), mFile( file ), mLine( line ) {
}
//---------------------------------------------------------------------------
// Do nothing
//---------------------------------------------------------------------------
Exception :: ~Exception() throw() {
}
//------------------------------------------------------------------------
// message as C string via standard what() function
//------------------------------------------------------------------------
const char * Exception :: what() const throw() {
return mMsg.c_str();
}
//------------------------------------------------------------------------
// as above, but as C++ string
//------------------------------------------------------------------------
const string & Exception :: Msg() const {
return mMsg;
}
//---------------------------------------------------------------------------
// File name & line number
//---------------------------------------------------------------------------
int Exception :: Line() const {
return mLine;
}
const string & Exception :: File() const {
return mFile;
}
} // namespace
// end
You would derive your own class from std::exception, so that there is some way of uniformly handling exceptions.
If this seems like overkill, you can throw std::logic_error or one of the other standard exception types intended for applications to use.
You could also use these as base classes for your own more specific exceptions: this saves a little work because they take care of implementing the what
method for you.
Note that deep exception hierarchies can be unusable, because you're basically making a guess in advance about how to categorize the errors, and your clients may disagree.
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 <string>
#include <memory>
#include <stdexcept>
/************************************************************************/
/* 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<std::string> 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_, Logic::DescriptiveException>
Exception;
class InvalidArgument_ {};
typedef
Compound <InvalidArgument_, Exception>
InvalidArgument;
class NotInitialized_ {};
typedef
Compound <NotInitialized_, Exception>
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 <typename Type, typename Base>
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
};
}
Here is a code snippet that shows how to extend and use the std::exception class: (BTW, this code has a bug, which I will explain later).
#include <iostream>
#include <string>
#include <exception>
class my_exception : public std::exception
{
public:
explicit my_exception(const std::string& msg)
: msg_(msg)
{}
virtual ~my_exception() throw() {}
virtual const char* what() const throw()
{
return msg_.c_str();
}
private:
std::string msg_;
};
void my_func() throw (my_exception&)
{
throw my_exception("aaarrrgggg...");
}
int
main()
{
try
{
my_func();
}
catch (my_exception& ex)
{
std::cout << ex.what() << '\n';
}
return 0;
}
Note that the constructor is explicit and the destructor and what() are declared (using throw()) to indicate that they themselves will not throw exceptions. This is where the bug is. Is it guaranteed that the call to msg_.c_str() will not throw exceptions of its own? What about the string constructor we are using to initialize msg_? It can also raise exceptions. How can we design an exception class that is safe from exceptions raised by member objects? The answer is - inherit from std::runtime_error or a similar std::exception subclass. So the proper way to implement my_exception would be:
class my_exception : public std::runtime_error
{
public:
my_exception(const std::string& msg)
: std::runtime_error(msg)
{ }
};
We need not override what(), as it is already implemented in std::runtime_error. Proper handling of the message buffer is done by std::runtime_error, so we can be sure that my_exception itself will not throw some unknown error at runtime.