rethrowing exceptions: why does this happen?

S

Steven T. Hatton

I haven't thought about rethrowing an exception in a long time, and tried to
do it the wrong way. Now I'm curious about /why/ it's wrong. My
expectation was that the exception instance would simply be passed up the
call stack as the same object. It would therefore behave polymorphically.
Is the behavior shown below compiler-specific? I'm using gcc 4.0.2.

In terms of what goes on at runtime, why does the `throw e' act as if I
threw a raw std::exception?

#include <stdexcept>
#include <iostream>


void generate(){
throw std::invalid_argument("generate() invalid argument exception");
}

void handle(bool broken) {
try {
generate();
} catch (std::exception& e) {
std::cout<< "handle:"<< e.what() <<std::endl;
if(broken)throw e;
throw;
}
}

void rehandle(bool broken=false) {
try {
handle(broken);
} catch (std::exception& e) {
std::cout<< "rehandle:"<< e.what() <<std::endl;
if(broken)throw e;
throw;
}
}


int main() {
try {
rehandle();
} catch(std::exception& e) {
std::cout<< "main:"<< e.what() <<std::endl;
}

try {
rehandle(true);
} catch(std::exception& e) {
std::cout<< "main:"<< e.what() <<std::endl;
}
}

/*-----------output---------
handle:generate() invalid argument exception
rehandle:generate() invalid argument exception
main:generate() invalid argument exception
handle:generate() invalid argument exception
rehandle:St9exception
main:St9exception
*/
 
P

Pete Becker

Steven said:
In terms of what goes on at runtime, why does the `throw e' act as if I
threw a raw std::exception?

A throw-expression throws creates a temporary object whose type is the
static type of its argument. Since e is a reference to std::exception,
the type of the temporary object is std::exception.

Forget for the moment that e is a reference to the exception object.
Pretend that it's just some reference to an object of type
std::exception. In that case, the only legitimate copy you could make of
it would be an object of type std::exception, because you don't know
it's dynamic type.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
G

Gianni Mariani

Steven said:
I haven't thought about rethrowing an exception in a long time, and tried to
do it the wrong way. Now I'm curious about /why/ it's wrong. My
expectation was that the exception instance would simply be passed up the
call stack as the same object. It would therefore behave polymorphically.
Is the behavior shown below compiler-specific? I'm using gcc 4.0.2.

In terms of what goes on at runtime, why does the `throw e' act as if I
threw a raw std::exception?

Becuase "throw e" makes a copy of e and since the type is
"std::exception", you get a slice of the "invalid_argument" exception
and loose any information about the exception. "throw" however throws
the same object that was caught.
 
S

Salt_Peter

Steven said:
I haven't thought about rethrowing an exception in a long time, and tried to
do it the wrong way. Now I'm curious about /why/ it's wrong. My
expectation was that the exception instance would simply be passed up the
call stack as the same object. It would therefore behave polymorphically.
Is the behavior shown below compiler-specific? I'm using gcc 4.0.2.

In terms of what goes on at runtime, why does the `throw e' act as if I
threw a raw std::exception?

#include <stdexcept>
#include <iostream>


void generate(){
throw std::invalid_argument("generate() invalid argument exception");
}

void handle(bool broken) {
try {
generate();
} catch (std::exception& e) {
std::cout<< "handle:"<< e.what() <<std::endl;
if(broken)throw e;
throw;
}
}

void rehandle(bool broken=false) {
try {
handle(broken);
} catch (std::exception& e) {
std::cout<< "rehandle:"<< e.what() <<std::endl;
if(broken)throw e;
throw;
}
}


int main() {
try {
rehandle();
} catch(std::exception& e) {
std::cout<< "main:"<< e.what() <<std::endl;
}

try {
rehandle(true);
} catch(std::exception& e) {
std::cout<< "main:"<< e.what() <<std::endl;
}
}

/*-----------output---------
handle:generate() invalid argument exception
rehandle:generate() invalid argument exception
main:generate() invalid argument exception
handle:generate() invalid argument exception
rehandle:St9exception
main:St9exception
*/
--

You are rethrowing a copied slice of the original object.

#include <iostream>
#include <typeinfo>

struct Base
{
virtual ~Base() { }
};

struct Derived : public Base
{
};

void foo()
{
try {
throw Derived();
}
catch(const Base& r_b)
{
std::cerr << typeid(r_b).name();
std::cerr << std::endl;
// throw;
throw r_b; // copy a slice
}
}

int main()
{
try {
foo();
}
catch(const Base& r_b)
{
std::cerr << typeid(r_b).name();
std::cerr << std::endl;
}
}

/*
Derived
Base
*/
 
S

Steven T. Hatton

Salt_Peter said:
You are rethrowing a copied slice of the original object.

#include <iostream>
#include <typeinfo>

struct Base
{
virtual ~Base() { }
};

struct Derived : public Base
{
};

void foo()
{
try {
throw Derived();
}
catch(const Base& r_b)
{
std::cerr << typeid(r_b).name();
std::cerr << std::endl;
// throw;
throw r_b; // copy a slice
}
}

int main()
{
try {
foo();
}
catch(const Base& r_b)
{
std::cerr << typeid(r_b).name();
std::cerr << std::endl;
}
}

/*
Derived
Base
*/

I believe Pete Becker had it right. Even if I pass the same time as the
original event, it comes out empty. What happens is that the compiler
borrows the constructor from the class used as the catch parameter, and
constructs a new instance.
 
S

Salt_Peter

Steven said:
I believe Pete Becker had it right. Even if I pass the same time as the
original event, it comes out empty. What happens is that the compiler
borrows the constructor from the class used as the catch parameter, and
constructs a new instance.

In the case where you throw a base or attempt reconstruction of the
derived object, yes.
In the case you *rethrow* the derived exception, however, the catch
block in main captures the original derived object via a pointer to its
base.
output:
/*
Derived
Derived
*/
To "pass" or rethrow the original "event":
throw; // rethrow the exception
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,990
Messages
2,570,211
Members
46,796
Latest member
SteveBreed

Latest Threads

Top