Question

How to catch derived (polymorphic) exception object and re-throw it to second level? In my case derived object is preserved till 'Level 1' only.

I understand 'Return value optimization' done by c++ compilers. In my opinion in the below code reference of 'e' should no longer be valid at 'Level 2' but compiler is doing some implicit creation of local base class Exception object and I am loosing my original ExtException thrown from 'Level 0'.

My question is How could I preserve 'ExtException' object till 'Level 2' ?

#include <string>
#include <iostream>

using std::endl;

typedef std::string pstring;


class Exception
{
    public:
        Exception ( ) {
            my_id = ++static_count;
            std:: cout << "+Exception obj created.  ID: " << my_id << "  (Total: " << static_count << ")" << endl;
        }

        Exception ( const Exception &e ) {
            my_id = ++static_count;
            std:: cout << "+Exception obj created.  ID: " << my_id << "  (Total: " << static_count << ")" << endl;
        }

        virtual ~Exception() {
            --static_count;
            std:: cout << "-Exception obj deleted.  ID: " << my_id << "  (Total: " << static_count << ")" << endl;
        }

    private:
        static int static_count;
        int        my_id;
};
int Exception:: static_count = 0;



class ExtException : public Exception {
    public:
        ExtException () : Exception()
        { 
            std:: cout << "++ExtException obj created" << endl;
        }

        ExtException ( const ExtException &e ) : Exception()
        { 
            std:: cout << "++ExtException obj created" << endl;
        }

        ~ExtException () {
            std:: cout << "--ExtException obj deleted" << endl;
        }
};





void foo2 () {
    throw ExtException();   // Level 0 throw
}


void foo1 () 
{
    try {
        foo2 ();
    } catch ( Exception &e ) {
        // In my normal understanding of c++ 'e' should no longer be valid here 
        // as it was created on a stack which is no longer exists (i.e Level 0 is scoped out).
        //
        // _BUT_  (actually it is valid. and it is valid because of RVO compiler "Return value optimization"

        std:: cout << "\n--- foo1 catch" << endl;
        throw e;  // Level 1 throw
    }
}


void foo () 
{
    try {
        foo1 ();
    } catch ( Exception &e ) {
        // !!! CAUTION !!!
        // In my opinion 'e' must not be valid here as the original "ExtException" must be destroyed till 
        // this point (which is actually destroyed check output trace).
        // It is destroyed because RVO works for one level only...
        //
        // wait a minute... here the compiler has not left 'e' for FMR hit, instead it has created a new temporary object of 'Exception' class and
        //  'e' is referring to that.
        //
        // Here comes my real question!!!  'e' is no longer referring to an actual  ExtException object but the temporary one, 
        // how could I modify this code to access ExtException object created at 'Level 0'?

        std:: cout << "\n--- foo catch" << endl;
        throw e;  // Level 2 throw
    }
}


int main (void) {

    try {
        foo  ();
    } catch (...) { }
    return 1;
}

Below is the result for above code:

+Exception obj created.  ID: 1  (Total: 1)
++ExtException obj created

--- foo1 catch
+Exception obj created.  ID: 2  (Total: 2)
--ExtException obj deleted
-Exception obj deleted.  ID: 1  (Total: 1)

--- foo catch
+Exception obj created.  ID: 2  (Total: 2)
-Exception obj deleted.  ID: 2  (Total: 1)
-Exception obj deleted.  ID: 2  (Total: 0)
Was it helpful?

Solution

[C++11: 15.1/3]: A throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”, respectively. The temporary is an lvalue and is used to initialize the variable named in the matching handler (15.3). If the type of the exception object would be an incomplete type or a pointer to an incomplete type other than (possibly cv-qualified) void the program is ill-formed. Except for these restrictions and the restrictions on type matching mentioned in 15.3, the operand of throw is treated exactly as a function argument in a call (5.2.2) or the operand of a return statement.

In short, you're slicing it, despite the exception handler having accepted a reference.

Re-throwing should be accomplished by writing simply throw, on its own. This is exactly its purpose:

[C++11: 15.1/8]: A throw-expression with no operand rethrows the currently handled exception (15.3). The exception is reactivated with the existing temporary; no new temporary exception object is created. [..]

So:

throw e;  // no
throw;    // yes

OTHER TIPS

Prefer "throw" over "throw e" for polymorphic exceptions.

Reference:

http://ptgmedia.pearsoncmg.com/images/0321113586/items/sutter_item73.pdf

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top