-2

I am trying to give the user the option to catch all the errors from my class or to catch them individually. I can catch individual exceptions but I can't catch it correctly when I try to catch the base error class. I get the standard error from std::exception "Unknown Error".

I have tried to catch the base exception and to catch the derived errors.

Can I have all my errors be a base error type and catch them all as such?

#include <iostream>
#include <exception>

struct FooException 
    : public std::exception
{

};

struct FooRuntimeException
    : public std::runtime_error,
    public FooException
{
    FooRuntimeException(const char* what)
        : runtime_error(what) {}

};

struct FooRangeError
    : public std::range_error, 
    public FooException
{
    FooRangeError(const char* what)
        : range_error(what) {}
};


class Foo
{
public:
    Foo() = default;
    ~Foo() {};

    void throwRunTimeException()
    {
        throw FooRuntimeException("Runtime Error");
    }
    void throwRangeError()
    {
        throw FooRuntimeException("Range Error");
    }
};

int main()
{
    try
    {
        auto foo = Foo();
        foo.throwRunTimeException();
    }
    catch (const FooException &e)
    {
        std::cerr << e.what(); // catches standard error message
    }

    return 0;
    }

Is there a way to do this or is templates a possibility?

Eddie C.
  • 155
  • 8
  • 2
    None of your derived classes initialise their `FooException` subobject. Why the multiple inheritance? – molbdnilo Jan 18 '19 at 18:34
  • There really is no possible reason for using multiple inheritance when it comes to exceptions. –  Jan 18 '19 at 18:35
  • 1
    Are you enjoying shooting yourself in the foot? – SergeyA Jan 18 '19 at 18:37
  • 1
    You are forcing a [diamond problem](https://stackoverflow.com/questions/2064880/diamond-problem). Your exceptions inherit from `std::exception` twice. – François Andrieux Jan 18 '19 at 18:49
  • I mean they only default-initialise them, of course. – molbdnilo Jan 18 '19 at 18:52
  • Either 1) do not derive `FooRuntimeException` from `std::runtime_error` and `FooRangeError` from `std::range_error`, just derive them from `FooException` only, or 2) do not derive `FooException` from `std::exception`, since `std::runtime_error` and `std::range_error` already derive from it. – Remy Lebeau Jan 18 '19 at 19:00
  • @NeilButterworth -- many years go, Jerry Schwarz (author of the iostreams library) gave me a $10 prize for pointing out that when you're writing a library that uses two other libraries, each with its own exception hierarchy, it could make sense to merge exceptions from those libraries into a new, single hierarchy with each exception in the new hierarchy inheriting from one exception from each of the two base libraries. He said this was the first **legitimate** use of multiple inheritance that he had heard of. That's not this case, of course. – Pete Becker Jan 18 '19 at 20:13
  • @Pete As iostreams itself uses multiple inheritance, are you saying Jerry Schwarz didn't think its use was legitimate? I myself would never design an MI hierarchy, particularly for things like exceptions that I believe should be kept as simple as possible, for obvious reasons. But I have used libraries that seemed to use MI quite elegantly, for example (long while ago) the Reuters Triarch trading data system. –  Jan 18 '19 at 20:27
  • @NeilButterworth — yes, that was the implication: that the use of multiple inheritance in iostreams was gratuitous. And I forgot to mention: that was before the standard had its own exception hierarchy. Multiple inheritance in exception hierarchies today would lead to problems when someone properly writes a catch clause to catch `const std::exception&`. – Pete Becker Jan 18 '19 at 20:32

1 Answers1

4

You're getting confused because there are two base classes of type std::exception in each of your exception classes. Try catching const std::exception& and watch the compiler complain.

The actual problem that you're getting here is that the code explicitly initializes one of those bases, but default-initializes the other; the catch clause gets the FooException object, which has the default-initialized std::exception, so that's the message you get.

As some of the comments have hinted, this is a complicated class hierarchy, and there's not much you can do to fix the problem. To avoid having multiple copies of std::exception you'd need to inherit virtually from std::exception in the two places where it's used. The inheritance in FooException is easily changed, but you can't change the fact that std::runtime_exception is not derived virtually from std::exception.

So there's a design problem, and you have to decide exactly what you want to provide to the user. This hierarchy does more than what you describe in your question, since it provides both FooException and parts of the standard exception hierarchy. Pick one: either use the standard exception hierarchy, or use your own exception hierarchy, with FooException deriving from std::exception and providing a constructor that initializes the std::exception sub-object.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • Also, there's a hack that could make this work, but I don't want to put it into the answer because that would imply that it's a good idea. Change `FooException` so that it doesn't derive from `std::exception`, and make its destructor (or any other member function) virtual. Then you can catch `const FooException& ex` and use `dynamic_cast(ex)` to get to the `std::exception` sub-object. – Pete Becker Jan 18 '19 at 20:07
  • Do you have a website or book you would recommend? I would like to understand the advantage of my own exception hierarchy more. – Eddie C. Jan 19 '19 at 01:09