Exceptions

E

Eric

I have been researching how exceptions and dynamic_cast work to
determine if I will use either feature for a game-console application.
I have a few pre-concieved notions and questions that I was hoping
some people here could validate/answer.

PRECONCEIVED NOTION #1: Common implementations of dynamic_cast<>
(VC++, GCC) rely on the presence of type_info objects. The use of
strcmp() instead of type_info pointer-equivalence in common
implementations is used ONLY to support non-statically linked
applications (.dll's & such). Other than this requirement,
dynamic_cast<> conceptually would never need the type_info::name
strings. typeid(), on the other hand, must have type_info::name
strings to fulfill it's function regardless of whether the app is
entirely statically linked or not.

QUESTION #1: Given that I am writing a statically-linked game-console
app and I am only interested in dynamic_cast and not typeid(), it
would benefit me to not have type_info::name strings added to my app
and have dynamic_cast implemented using type_info pointer equivalence.
Can gcc do this??


PRECONCEIVED NOTION #2: Exception handling (the more attractive
feature to me) actually requires dynamic_cast<> to implement the
catch-by-type mechanism, which in turn requires strcmp() to support
non-statically linked apps, which in turn requires a full
type_info::name string for every type in my code regardless of whether
or not it is ever thrown.

QUESTION #2: Given that this amounts to unnecessary size-bloat for my
statically linked game-console app, does anyone know if gcc can
implement catch-by-type using type_info pointer equivalence and
suppress the type_info::name strings?


PRECONCEIVED NOTION #3: The best implementations of exception handling
out there use the zero-runtime-cost model (I understand that VC++ does
not, but gcc does). I understand that this model has no performance
penalties when not throwing because the stack is unwound using an
exteral table of ip-sorted elements rather than inserting code into
every function. In addition to the typename strings that are
theoretically irrelevant to statically linked apps, zero-runtime-cost
models also have to add this instruction-pointer table to the app, and
the table must cover every single function in the code regardless of
whether or not it throws.

QUESTION #3: Can anyone characterize the structure of the elements in
such a table so that I can get my head around the size implications of
it? Is there one entry per instantiation of objects with actual
destructors?


Thanks for any insight you can give on any of these issues.

-Eric
 
R

Ron Natalie

..
Most of your questions about internals of particular compilers or how they may
be forced to deviate from standard behavior is not appropriate for this group.
You'd be better off asking in microsoft.public.vc.* for VC++ or the gnu.gcc
groups for gcc.
PRECONCEIVED NOTION #1: Common implementations of dynamic_cast<>
(VC++, GCC) rely on the presence of type_info objects

I'll venture this is wrong in general. There's no requirement that type_info::name
is UNIQUE (and it frequently isn't).
Other than this requirement,
dynamic_cast<> conceptually would never need the type_info::name
strings.

Don't agree. Why would dynamic libraries need or care about type_info::name
for this? All that is necessary is some resolved per-class value. Certainly linkers
can handle the single definition of some object (like the vtable) accross libraries.
QUESTION #1: Given that I am writing a statically-linked game-console
app and I am only interested in dynamic_cast and not typeid(), it
would benefit me to not have type_info::name strings added to my app
and have dynamic_cast implemented using type_info pointer equivalence.
Can gcc do this?

Many compilers (including visual C++ and I'm assuming GCC)...have options
to ignore certain parts of the langauge.
PRECONCEIVED NOTION #2: Exception handling (the more attractive
feature to me) actually requires dynamic_cast<> to implement the
catch-by-type mechanism, which in turn requires strcmp() to support
non-statically linked apps, which in turn requires a full
type_info::name string for every type in my code regardless of whether
or not it is ever thrown.

Again, I don't know why you think strcmp has anything to do with this.
Further, dynamic typing doesn't apply to exceptions. The static type
of the object thrown must be mapped. While it might be possible or
useful to share parts of this facility with the RTTI information, it's not
required, and I can tell you Visual C++ doesn't do that.
QUESTION #2: Given that this amounts to unnecessary size-bloat for my
statically linked game-console app, does anyone know if gcc can
implement catch-by-type using type_info pointer equivalence and
suppress the type_info::name strings?

Your question is nonsense in light of the invalid assumption of your notion.
 
R

red floyd

Eric said:
PRECONCEIVED NOTION #2: Exception handling (the more attractive
feature to me) actually requires dynamic_cast<> to implement the
catch-by-type mechanism, which in turn requires strcmp() to support
non-statically linked apps, which in turn requires a full
type_info::name string for every type in my code regardless of whether
or not it is ever thrown.

QUESTION #2: Given that this amounts to unnecessary size-bloat for my
statically linked game-console app, does anyone know if gcc can
implement catch-by-type using type_info pointer equivalence and
suppress the type_info::name strings?

I believe notion is incorrect. AFAIK, exception handling uses the same
rules as conversion and argument passing. That is, derived can be used
as a base, but not vice versa. My understanding is that dynamic_cast<>
is used for safe downcasting (base class to derived class).

Any gurus out there are welcome to flame my obvious ignorance :)
 
R

Rolf Magnus

Eric said:
QUESTION #1: Given that I am writing a statically-linked game-console
app and I am only interested in dynamic_cast and not typeid(), it
would benefit me to not have type_info::name strings added to my app
and have dynamic_cast implemented using type_info pointer equivalence.
Can gcc do this??

I have no idea, and I don't know why you want that. But you might want
to ask in a newsgroup like gnu.g++.help, which is better for g++
specific quesitons like yours.
PRECONCEIVED NOTION #2: Exception handling (the more attractive
feature to me) actually requires dynamic_cast<> to implement the
catch-by-type mechanism, which in turn requires strcmp() to support
non-statically linked apps, which in turn requires a full
type_info::name string for every type in my code regardless of whether
or not it is ever thrown.

Why do you think that exception handling has anything to do with
dynamic_cast or type_info at all? Actually, you can switch RTTI off
completely in g++ and still use exceptions.
QUESTION #3: Can anyone characterize the structure of the elements in
such a table so that I can get my head around the size implications of
it? Is there one entry per instantiation of objects with actual
destructors?

I don't know the answer to that question, but again would advice you to
ask in gnu.g++.help.
 
L

lilburne

Eric said:
I have been researching how exceptions and dynamic_cast work to
determine if I will use either feature for a game-console application.
I have a few pre-concieved notions and questions that I was hoping
some people here could validate/answer.


QUESTION #2: Given that this amounts to unnecessary size-bloat for my
statically linked game-console app, does anyone know if gcc can
implement catch-by-type using type_info pointer equivalence and
suppress the type_info::name strings?

What evidence do you have that dynamic_cast results in code
bloat?

Personally I'd be a little suspicious of code that was
written to specifically throw exceptions. dynamic_cast only
throws if you are casting to a reference.
PRECONCEIVED NOTION #3: The best implementations of exception handling
out there use the zero-runtime-cost model (I understand that VC++ does
not, but gcc does). I understand that this model has no performance
penalties when not throwing because the stack is unwound using an
exteral table of ip-sorted elements rather than inserting code into
every function.


Perhaps not. The last time I check GCC 3.3.2 was 5 times
slower with exceptions than asserts:
http://groups.google.com/groups?&[email protected]

Performance seems to vary from compiler release to compiler
release.
 
R

Rolf Magnus

lilburne said:
Perhaps not. The last time I check GCC 3.3.2 was 5 times
slower with exceptions than asserts:
http://groups.google.com/groups?&as_umsgid=bpgq4c$1pao1h$1 40ID-203936.news.uni-berlin.de

Performance seems to vary from compiler release to compiler
release.

Either the gcc version for your platform has a bug related to exceptions
or your installation is somehow broken. Look at the following:

$ g++ --version
g++ (GCC) 3.3.2
Copyright (C) 2003 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
$ cat point.C
#include <iostream>
#include <ctime>
#include <cmath>
#include <limits>
#include <cassert>

using namespace std;

class Point{
double m_x;
double m_y;
double m_z;
public:
Point() {
m_z = numeric_limits<double>::max();
}
Point(double x, double y, double z) {set(x,y,z);}
void set(double x, double y, double z) {m_x = x, m_y =
y; m_z = z;}
const double& x() const { return m_x;}
const double& y() const { return m_y;}
const double& z() const { return m_y;}
bool coincident(const Point& a, double eps) const;
bool is_valid() const {
return m_z != numeric_limits<double>::max();
}
};


#ifdef USE_EXCEPTIONS
bool Point::coincident(const Point& a, double eps) const
{
if (!is_valid()) {
throw int(1);
}
return abs(x()-a.x()) < eps &&
abs(y()-a.y()) < eps &&
abs(z()-a.z()) < eps;
}

void test(const Point& a, const Point& b)
{
try {
a.coincident(b, 1e-5);
}
catch (int) {
cout << "caught exception" << endl;
}
}
#else
bool Point::coincident(const Point& a, double eps) const
{
assert(is_valid());
return abs(x()-a.x()) < eps &&
abs(y()-a.y()) < eps &&
abs(z()-a.z()) < eps;
}

void test(const Point& a, const Point& b)
{
a.coincident(b, 1e-5);
}
#endif


int main()
{
time_t start = time(0);
Point a(10.0,10.0,20.0);
Point b(10.0,10.0,20.0);
for (int i = 0; i < 10000000; ++i) {
test(a,b);
}

cout << time(0) - start << endl;
return 0;
}
$ g++ point.C -O2 -DUSE_EXCEPTIONS
$ time ./a.out
0

real 0m0.371s
user 0m0.367s
sys 0m0.001s
$ g++ point.C -O2
$ time ./a.out
1

real 0m0.370s
user 0m0.366s
sys 0m0.002s


As you can see, your program isn't 5 times slower with exceptions than
without on my gcc 3.3.2 installation. The difference is actually
smaller than the measurement inaccuracy.
 
L

lilburne

Rolf said:
lilburne wrote:


Either the gcc version for your platform has a bug related to exceptions
or your installation is somehow broken. Look at the following:

$ g++ --version
g++ (GCC) 3.3.2
Copyright (C) 2003 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
$ g++ point.C -O2 -DUSE_EXCEPTIONS
$ time ./a.out
0

real 0m0.371s
user 0m0.367s
sys 0m0.001s
$ g++ point.C -O2
$ time ./a.out
1

real 0m0.370s
user 0m0.366s
sys 0m0.002s


As you can see, your program isn't 5 times slower with exceptions than
without on my gcc 3.3.2 installation. The difference is actually
smaller than the measurement inaccuracy.

I was mistaken about the version of GCC it is 3.3.1 not
3.3.2. Perhaps 3.3.1 was broke.

22:37:48 ~ > g++ --version
g++ (GCC) 3.3.1 (cygming special)
Copyright (C) 2003 Free Software Foundation, Inc.
This is free software; see the source for copying
conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.


22:34:51 ~ > g++ point.C -O2 -DUSE_EXCEPTIONS
22:35:42 ~ > time ./a.exe
lapsed time: 32

real 0m34.058s
user 0m0.000s
sys 0m0.000s
22:36:32 ~ > g++ point.C -O2
22:37:32 ~ > time ./a.exe
lapsed time: 6

real 0m7.168s
user 0m0.000s
sys 0m0.000s
 
R

Risto Lankinen

Eric said:
PRECONCEIVED NOTION #2: Exception handling (the more attractive
feature to me) actually requires dynamic_cast<> to implement the
catch-by-type mechanism, which in turn requires strcmp() to support
non-statically linked apps, which in turn requires a full
type_info::name string for every type in my code regardless of whether
or not it is ever thrown.

QUESTION #2: Given that this amounts to unnecessary size-bloat for my
statically linked game-console app, does anyone know if gcc can
implement catch-by-type using type_info pointer equivalence and
suppress the type_info::name strings?

Exception handling can be implemented using static type checking, and
therefore it doesn't necessarily require type_info structs. If the size of
the type_info is a concern, check out other compilers for your platform
for whether they implement exceptions without using the RTTI.

- Risto -
 
R

Rolf Magnus

lilburne said:
I was mistaken about the version of GCC it is 3.3.1 not
3.3.2. Perhaps 3.3.1 was broke.

22:37:48 ~ > g++ --version
g++ (GCC) 3.3.1 (cygming special)
Copyright (C) 2003 Free Software Foundation, Inc.
This is free software; see the source for copying
conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.


22:34:51 ~ > g++ point.C -O2 -DUSE_EXCEPTIONS
22:35:42 ~ > time ./a.exe
lapsed time: 32

real 0m34.058s
user 0m0.000s
sys 0m0.000s
22:36:32 ~ > g++ point.C -O2
22:37:32 ~ > time ./a.exe
lapsed time: 6

real 0m7.168s
user 0m0.000s
sys 0m0.000s

I still wonder why it takes so extremely long on your machine. I mean,
even the "fast" non-exception version took over 7 seconds on your
machine and only 0.37 seconds on mine. Even when I use -O0 to turn off
all optimizations, the program doesn't take longer than about 1.5
seconds here (again, no significant difference between exception and
non-exception version).
 
E

Eric

Risto Lankinen said:
Exception handling can be implemented using static type checking, and
therefore it doesn't necessarily require type_info structs. If the size of
the type_info is a concern, check out other compilers for your platform
for whether they implement exceptions without using the RTTI.

- Risto -

Forgive my ignorance about common compiler-implementation strategies
regarding exceptions - I've just started trying to get my head around
it. That said, I have to ask you how exception handling can possibly
be statically type checked by *any* implementation. To me, that says
that "throw Error" is bound to some other "catch(Error&)" statically
at compile time. That is, of course, impossible.

In any implementation, doesn't the thrown type have to be represented
by some value at runtime. In fact, "catch by type" is an illusion...
it's it really "catch by compiler-generated value that represents
type". That's really akin to rtti (even if you don't call it rtti).
I had assumed that type_info was a likely candidate for this "value",
but I concede that the standard says nothing about it *having* to be.
Still, it's a value nontheless. Further, it must be a value that
works accross dynamically linked boundaries - otherwise a dll couldn't
throw a type that was caught by the app that loaded it. That rules
out vtbl ptrs or any other "simple equivalence" checks - it would have
to be something more universal, like a name (hence my assumption that
type_info::name was a likely candidate).

Does anyone understand me or am I totally off the deep end?

Thanks,

-Eric
 
R

Risto Lankinen

Hi!

Eric said:
"Risto Lankinen" <[email protected]> wrote in message

Forgive my ignorance about common compiler-implementation strategies
regarding exceptions - I've just started trying to get my head around
it. That said, I have to ask you how exception handling can possibly
be statically type checked by *any* implementation. To me, that says
that "throw Error" is bound to some other "catch(Error&)" statically
at compile time. That is, of course, impossible.

In a nutshell, RTTI is one way (and clearly the most obvious
way based on the number of people who assume that RTTI is
strictly necessary for exception handling), but not the only one.

Another way is to generate an unwinder for every exception
type that can be caught. It will know of all available catchers
of the same type. The unwinder will check the stack, and if
it finds out that a function in the stack has a handler for that
exception type, it will call the handler, or otherwise it will call
the local parameter cleanup routine for that function, and go
on with the next function in the return stack.

(What I wrote about "functions" handling exceptions above
also applies to nameless statement blocks which, too, must
have their own cleanup at the point where the corresponding
scope closes [if they define local variables], and be identified
by the return address in the stack. This fact nicely illustrates
that catchers are really function objects on their own right and
not only in association of some function containing them in the
source code.)

Whenever you code a function that catches some new type,
a new unwinder is created. Whenever you code a function
that catches some type that is also being caught somewhere
else, the unwinder is augmented to include your new handler
in its list of handlers of that type. Augmentation thru multiple
compilation units catching the same exception can be dealt
with some segmentation magic (basically the pointers to all
unwinders will go to some common data segment, and when
the initial unwinder is called, it will know how to chain with
the corresponding handlers in other compilation units).

The thrower knows the exact type of the exception, hence
the compiler could simply generate a JMP instruction to the
unwinder of that particular exception type, which is easy to
do at compile-time. Certain weak-reference mechanisms
need to be implemented by the linker (or a "stub" unwinder
must be created at the point of throw) to cater for situations
where a thrown type may have no catcher at all.

In real world, there may be only one unwinder that is driven
by a 2D matrix, indexed by thrown types and caught types.
Instead of generating/calling unwinders, the catchers merely
just add a row in the matrix indicating the types they catch,
and the throwers pass matrix column index as an argument
to the unwinder.

Which brings us to the point, that RTTI type_info is one way
to use as a column index in such a matrix, or even implement
the whole matrix (by applying sparce matrix implementation
techniques using type_info as the node type) in a reasonably
space efficient - but not run-time efficient - manner. That is
actually one explanation why throwing exceptions in general
have a high run-time cost.

- Risto -
 

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

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,071
Latest member
MetabolicSolutionsKeto

Latest Threads

Top