views:

1243

answers:

5

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*, string, exception class). I know that std::exception is the base class for standard exceptions thrown by the program. However, I'm trying to design a try...catch block as such:

try
{
    MyFunc();
}
catch (certain exceptions)
{
    // deal with the exception accordingly
}
catch (the rest of the exceptions)
{
    // deal with these accordingly
}

while MyFunc() contains the following

void MyFunc()
{
    ...
    if (certain condition is true) throw exception;
    ...
}

The trouble is that in that part of MyFunc function, I'm not sure what type of exception I should throw. To keep the code clean by implementing my own exceptions class, I have no idea what would be a good way to implement such exceptions class. It would be great to see some suggestions or advices on how to go about this. Thanks in advance.

+8  A: 

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.

Daniel Earwicker
+1  A: 

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
    };

  }
Abhay
Would using boost be a good idea even though my entire project isn't using boost libraries yet?
stanigator
Probably not. The above hierarchy can be used instead and modified as per your needs. I have had moderate success using such named eexception-levels in large projects. Also, there may be minor portability issues if you are using cross-compilation using boost as it heavily templatized. Some compilers may not conform to the C++ Standard.
Abhay
+3  A: 

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
anon
Your first Exception ctor should be `explicit` and `ATHROW(x)` would be better defined to `do { std::stringstream _s; _s<<(x); throw ALib::Exception( _s.str(), __FILE__, __LINE__ ) } while(0)` to avoid operator precedence problems and to allow `ATRHOW(x)` use as a proper C/C++ statement.
I take your point regarding explicit. Calling the stringstream _s would be a bad idea, as the name is reserved at namespace scope. And unlike others, I've never found that way of constructing macros worth the trouble, as I have never experienced a problem without it, probably due to my other programming practices.
anon
If you had derived from std::runtime_error some of this would already be handled for you!
Martin York
+2  A: 

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.

Vijay Mathew
A: 

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)

Diaa Sami