views:

834

answers:

2

Hi, I've been trying to make some custom exception classes for a C++ library I'm working on. These custom exceptions capture extra info, such as file,line number,etc, needed for debugging, if for some reason while testing an exception is not caught in the right place. However most people seem to recommend inheriting from the std::exception class in the STL, which I agree with, but I was wondering maybe it would be better to use multiple inheritance to inherit from each of the derived std::exception classes (eg. std::runtime_error) and a custom exception class, as in the code below?

Another thing, how does one go about copy constructors and assignment operators in exception classes? Should they be disabled?

class Exception{
    public:
        explicit Exception(const char *origin, const char *file, 
                           const int line, const char *reason="", 
                           const int errno=0) throw();

     virtual ~Exception() throw();

     virtual const char* PrintException(void) throw();

     virtual int GetErrno(void);

    protected:
     std::string m_origin;
     std::string m_file;
     int m_line;
     std::string m_reason;
     int m_errno;
}; 

class RuntimeError: public virtual std::runtime_error, public Exception{
    public:
              explicit RuntimeError(const char *origin, const char *file, 
                                    const int line, const char *reason="", 
                                    const int errno=0) throw();
     virtual ~RuntimeError() throw();
};
+5  A: 

You should try boost::exception

The purpose of Boost Exception is to ease the design of exception class hierarchies and to help write exception handling and error reporting code.

It supports transporting of arbitrary data to the catch site, which is otherwise tricky due to the no-throw requirements (15.5.1) for exception types. Data can be added to any exception object, either directly in the throw-expression (15.1), or at a later time as the exception object propagates up the call stack.

The ability to add data to exception objects after they have been passed to throw is important, because often some of the information needed to handle an exception is unavailable in the context where the failure is detected.

Boost Exception also supports N2179-style copying of exception objects, implemented non-intrusively and automatically by the boost::throw_exception function.

fmuecke
I've never used boost before. I'll look into it. Thanks for the post.
Tom
Pretty awesome library. Now I wish I had read it before!
Matthieu M.
+5  A: 

I was wondering maybe it would be better to use multiple inheritance to inherit from each of the derived std::exception classes

Note that this is a problem, due to the fact that the exceptions in the standard library derive non-virtually from each other. If you introduce multiple inheritance, you get the dreaded diamond exception hierarchy without virtual inheritance and won't be able to catch derived exceptions by std::exception&, since your derived exception class carries two subobjects of std::exception, making std::exception an "ambiguous base class".

Concrete example:

class my_exception : virtual public std::exception {
  // ...
};

class my_runtime_error : virtual public my_exception
                       , virtual public std::runtime_error {
  // ...
};

Now my_runtime_error derives (indirectly) from std::exception twice, once through std::run_time_error and once through my_exception. Since the former doesn't derive from std::exception virtually, this

try {
  throw my_runtime_error(/*...*/);
} catch( const std::exception& x) {
  // ...
}

won't work.

Edit:

I think I've seen the first example of an exceptions class hierarchy involving MI in one of Stroustrup's books, so I concluded that, in general, it is a good idea. That the std lib's exceptions do not derive virtually from each other I consider as a failure.

When I last designed an exception hierarchy, I used MI very extensively, but did not derive from the std lib's exception classes. In that hierarchy, there were abstract exception classes which you defined so your users could catch them, and corresponding implementation classes, derived from these abstract classes and from an implementation base class, which you would actually throw. To make this easier, I defined some templates which would do all the hard work:

// something.h
class some_class {
private:
  DEFINE_TAG(my_error1); // these basically define empty structs that are needed to 
  DEFINE_TAG(my_error2); // distinguish otherwise identical instances of the exception 
  DEFINE_TAG(my_error3); // templates from each other (see below)
public:
  typedef exc_interface<my_error1>  exc_my_error1;
  typedef exc_interface<my_error2>  exc_my_error2;
  typedef exc_interface<my_error3,my_error2> // derives from the latter
                                    exc_my_error3;

  some_class(int i);
  // ...
};

//something.cpp
namespace {
  typedef exc_impl<exc_my_error1> exc_impl_my_error1;
  typedef exc_impl<exc_my_error2> exc_impl_my_error2;
  typedef exc_impl<exc_my_error3> exc_impl_my_error3;
  typedef exc_impl<exc_my_error1,exc_my_error2> // implements both
                                  exc_impl_my_error12;
}
some_class::some_class(int i)
{
  if(i < 0) 
    throw exc_impl_my_error3( EXC_INFO  // passes '__FILE__', '__LINE__' etc.
                            , /* ... */ // more info on error
                            ); 
}

Looking back at this now, I think I could have made that exc_impl class template derive from std::exception (or any other class in the std lib exception hierarchy, passed as optional template parameter), since it never derives from any other exc_impl instance. But back then this wasn't needed, so it never occurred to me.

sbi
+1: quite explanatory
fmuecke
Thanks for the post sbi. Would making exception classes with multiple inheritance be a good practice? Or is it better just to inherit from std::exception class?
Tom
@Tom: I added my thoughts as an edit.
sbi
But he does not have that problem. You have gone off on an irrelavant tangent to the problem.
Martin York
@Martin: I'm not sure what you're getting at. I even quoted the question I answered to on top of my answer. And the edited part was added due to a question from Tom in his comment.
sbi