By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
454,132 Members | 1,267 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 454,132 IT Pros & Developers. It's quick & easy.

Implementation of catching by type

P: n/a
Consider the following code:

try
{
// throws exceptions of several types
Foo();
}
catch (out_of_range& ex)
{ }
catch (exception& ex)
{ }

Given that this requires identification of object type at runtime, how
is this implemented -- does it use RTTI? If so, then why is RTTI
attacked for its overheads given that any exception-based error
handling strategy would involve it. (I know RTTI can also point to poor
design, ignore that.)
If not, then how is this achieved?

TIA,
--rob

Jan 3 '07 #1
Share this Question
Share on Google+
5 Replies


P: n/a
[rob desbois] wrote:
Consider the following code:

try
{
// throws exceptions of several types
Foo();
}
catch (out_of_range& ex)
{ }
catch (exception& ex)
{ }

Given that this requires identification of object type at runtime, how
is this implemented -- does it use RTTI?
Exactly.
If so, then why is RTTI
attacked for its overheads given that any exception-based error
handling strategy would involve it. (I know RTTI can also point to poor
design, ignore that.)
I don't think RTTI is evil per se, it has overhead (tiny, usually in the
form of some bytes for each polymorphic class instantiated) but so far I've
seen almost NO correct explicit RTTI usage other one exception: when I had
to implement a serialization framework to work with polymorphic types (ex.
when deserializing on a base pointer the framework should be able to
realise what type exactly has been stored, create it and so on) in which
case is very clear why I had to use RTTI (because you just need the type
information of a base pointer/reference). I guess in this exception too can
be workarround with having the user specify the type behind the base
pointer/reference when serializing but seems awkward to me that way.
If not, then how is this achieved?
AFAIK as I said it uses RTTI :)

--
Dizzy
http://dizzy.roedu.net

Jan 3 '07 #2

P: n/a

"Dizzy" <di***@roedu.netwrote in message
news:45***********************@news.sunsite.dk...
[rob desbois] wrote:
>Consider the following code:

try
{
// throws exceptions of several types
Foo();
}
catch (out_of_range& ex)
{ }
catch (exception& ex)
{ }

Given that this requires identification of object type at runtime, how
is this implemented -- does it use RTTI?

Exactly.
Yes, except not exactly. Try this:

- - -

#include <iostream.h>

class Base
{
public:
virtual ~Base()
{
// Base and its derivants are now RTTI-enabled - if needed...
}
};

class Super : public Base
{
};

int main()
{
Base *p;
try
{
p = new Super();
throw p;
}
catch( Super * )
{
cout << "Super" << endl;
}
catch( Base * )
{
cout << "Base" << endl;
}
delete p;
return 0;
}

- - -

So, if RTTI (run-time type information) were used to identify the
appropriate exception handler, this progam should print "Super".
However, the handler is determined in the compile-time, based
on the static type of the expression used in the throw-statement;
hence this program prints "Base".

To the original poster: All catch-blocks will get an entry point
in the object file. The entry points are categorized based on
the type of an object they catch. When a throw-statement is
compiled, the compiler emits code that scans the catch handler
of the category determined by the type of the expression in the
throw-statement: If you throw a Base *p [like in my example]
the catch handlers for a "Base *" are scanned. It does require
linker magic, but RTTI is definitely not necessary[*].

- Risto -
[*] Individual compiler implementors, however, are free to use
RTTI type_info structures in the exception implementation if they
deem it useful (and some indeed do).
Jan 3 '07 #3

P: n/a
Risto Lankinen wrote:
So, if RTTI (run-time type information) were used to identify the
appropriate exception handler, this progam should print "Super".
However, the handler is determined in the compile-time, based
on the static type of the expression used in the throw-statement;
hence this program prints "Base".
It is true that the type of the object (which is Base*) is used.
The VALUE of the object plays no roll in the selection of the
matching catch block. You can throw null pointers.
Dynamic typing is a feature of the pointed
to value.

It is NOT THE CASE that this can be determined at COMPILE time.
Imagine the following:

void Thrower() {
int i;
cin >i;
switch(i) {
case 1:
throw (Base*) 0;
case 2:
throw (Super*) 0;
default:
throw (int) 0;
}
}

int main() {
try {
Thrower();
} catch (Super*) { ...
catch (Base*) { ...
Jan 3 '07 #4

P: n/a
Risto Lankinen wrote:
[...] Try this:

- - -

#include <iostream.h>

class Base
{
public:
virtual ~Base()
{
// Base and its derivants are now RTTI-enabled - if needed...
}
};

class Super : public Base
{
};

int main()
{
Base *p;
try
{
p = new Super();
throw p;
}
catch( Super * )
{
cout << "Super" << endl;
}
catch( Base * )
{
cout << "Base" << endl;
}
delete p;
return 0;
}
Stylistic note: FAQ 17.7 rightly says that unless there is a good
reason not to, one should catch by reference rather than pointer as you
do here. I'd combine that with FAQ 18.1 to say that one should catch by
const reference unless there's a good reason not to.

Cheers! --M

Jan 3 '07 #5

P: n/a

"Ron Natalie" <ro*@spamcop.netwrote in message
news:45**********************@news.newshosting.com ...
>
It is NOT THE CASE that this can be determined at COMPILE time.
And yet, it is the case. Let's reorder a bit and, hmm, compile...
int main() {
__main:
try {
Thrower();
} catch (Super*) { ...
..DATA SEGMENT __pSuper_handlers
DW __main
DW __pSuper_handler$main$filename = __pSuper_handler
DW __main_unwrap
..CODE
__pSuper_handler:
; code for the catch handler
catch (Base*) { ...
..DATA SEGMENT __pBase_handlers
DW __main
DW __pSuper_handler$main$filename = __pSuper_handler
DW __main_unwrap
..CODE
__pSuper_handler:
; code for the catch handler

. . .

..CODE
__main_unwrap:
; code to unwrap this function call level if no handler found

void Thrower() {
int i;
cin >i;
switch(i) {
case 1: throw (Base*) 0;
1. locate [immediate/next] caller's stack frame
2. within data segment named __pBase_handlers...
3. is current return address between __XXX and __XXX_unwrap?
* No, call __XXX_unwrap and go back to 1
* Yes, jump to __pSuper_handler$XXX$filename
case 2:
throw (Super*) 0;
Likewise here, except that two tables (pBase and pSuper) need
to be scanned, because Super is a derived class [however, even
this can be determined thru static analysis]

- - -

Linker magic I mentioned in my original article is twofold:

1. Unwrappers are generated everywhere where throwers are
located. There may well be more than one thrower for any given
type, but the linker should not complain about them having the
same name in the name table (and instead combine these into one
function only).

2. Data segments containing pointers to catch handlers may come
from multiple compilation units; linker must be able to consolidate
these tables so that the scanner routine finds all handlers.

Feel free to ask if my [hastily written] description is insufficient.

Cheers!

- Risto -
Jan 3 '07 #6

This discussion thread is closed

Replies have been disabled for this discussion.