12

I'm writing a set of custom exceptions that extend std::exception. In some code, when an exception is caught, I just re-throw it up the chain until the driver main function call catches and prints the result. However, ultimately all that gets printed is "std::exception". This doesn't appear the be the scope issue I dealt with previously.

Why are my exception messages not printing?

My exception code:

// General exception class
struct MyException : public std::exception
{
    std::string _msg;

    MyException(const std::string &exception_name) : _msg(exception_name) {}

    void setMessage(const std::string &message)
    {
        _msg += ": " + message + "\n";
    }

    void setLocation(const char * func, const char * file, const int line)
    {
        _msg += "  In function " + std::string(func) + "(" + file + ":" + std::to_string(line) + ")";
    }

    const char * what() const throw()
    {
        return _msg.c_str();
    }
};

// Specializations of the MyException
struct FileNotFoundException : public MyException
{
    FileNotFoundException() : MyException("FileNotFoundException") {}
};

struct IOException : public MyException
{
    IOException() : MyException("IOException") {}
};
struct DBException : public MyException
{
    DBException() : MyException("DBException") {}
};

All of my exception throws are wrapped in this macro

#define EXCEPTION_THROWER(ET, message)              \
    {                           \
        ET e;                       \
        e.setMessage(message);              \
        e.setLocation(__func__, __FILE__, __LINE__);    \
        throw e;                    \
    }

and called as

EXCEPTION_THROWER(DBException, "Blah blah database exception")

The intermediate try/catch blocks look like this:

try
{
    // Call a function that throws an exception
}
catch(const std::exception &e)
{
    throw e; // Forward any exceptions
}

and the driver code is all in one try block with a catch (const std::exception &e) block.

marcman
  • 3,233
  • 4
  • 36
  • 71
  • 1
    How are you catching your exceptions in `main`? And why are you bothering re-throwing exceptions; that should be the default behavior if you don’t catch them. – Daniel H Jun 27 '17 at 15:00
  • @DanielH: I just edited the OP to mention I catch `std::exception` by const reference. And, stupid clarification, but uncaught exceptions are automatically passed down the stack? I'd thought they exited failure where they occurred – marcman Jun 27 '17 at 15:01
  • 1
    If an exception isn’t caught right away, it continues on to the next function down the stack. If an exception is uncaught *all the way through `main`*, it calls [`std::terminate`](http://en.cppreference.com/w/cpp/error/terminate), which exits with error. – Daniel H Jun 27 '17 at 15:06
  • 1
    @marcman There's a description of stack unwinding [here on cppreference.com](http://en.cppreference.com/w/cpp/language/throw) – Blastfurnace Jun 27 '17 at 15:09
  • 1
    In particular, [this section](http://en.cppreference.com/w/cpp/language/throw#Stack_unwinding) – Daniel H Jun 27 '17 at 15:10
  • 1
    Off topic: you can ditch that macro and the set methods by using better constructors that take whatever arguments you wish to provide for that particular flavour of exception and the information available. – user4581301 Jun 27 '17 at 15:20
  • This question may be asking about a solution to a problem, not the problem itself [https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem]. I have opened a question more generally about how to customize printing of uncaught exceptions [https://stackoverflow.com/questions/52684542/]. – personal_cloud Oct 07 '18 at 18:03

1 Answers1

18

throw e; is carrying out a bucket load of object slicing as it is essentially slicing whatever e was to a std::exception (and _msg will be lost).

Use throw; to rethrow the caught exception by reference.

marcman
  • 3,233
  • 4
  • 36
  • 71
Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 1
    It’s not quite as bad as object slicing (which can lead to a lot of undefined behavior pretty quickly), but it still isn’t good or what you intend. – Daniel H Jun 27 '17 at 15:13
  • 1
    @DanielH What is your definition of object slicing if this (initialising a base type object from a derived type object) doesn't qualify? – Angew is no longer proud of SO Jun 27 '17 at 15:54
  • 1
    @Angew I suppose there is object slicing in the initialization, but then an initialized, separate object is used. I think of it more as using a subobject and keeping the object it’s part of around, so that the state of the whole object is corrupted. I guess I was thinking of a different problem. – Daniel H Jun 27 '17 at 16:00
  • Frankly, unless the exception is ultimately caught by _value_, I cannot see where the slicing takes place; where is a base object initialised? The parameter `e` is a (const) reference, and if it would be used as argument to a call of a function `g(const std::exception& )`, then the reference is passed on without any object being created. As far as I understand, throwing and catching an error uses the same mechanism as function calling, so a long as all catching is by reference, there is no slicing on a throw. – Marc van Leeuwen Jun 27 '17 at 20:42
  • OK I see my error: reading up on the description of `throw` I see that a temporary is created of the (cv-stripped) _static_ type of the operand of `throw`. Although that does not mention what happens with reference types, I think this can only mean that it is ignored, as there are no objects of reference type. So in spite of the same section saying "the operand of `throw` is treated exactly as a function argument in a call", a copy constructor is being called here, even when a reference is thrown and caught by reference. – Marc van Leeuwen Jun 28 '17 at 05:47
  • @MarcvanLeeuwen: Yes, that's broadly it. But do note the important difference between `throw something;` and `throw;`. – Bathsheba Jun 28 '17 at 07:02