type_info, vtable

R

Rob Williscroft

Ron Natalie wrote in
Well in some cases it doesn't, but in most of the interesting cases
it does. Your observation is correct. All the places where RTTI
behaves polymorhically (dynamic_cast or typeinfo) requires a
polymorphic class (at least one virtual function). This is not a
coincidence, the idea was to not require the overhead of RTTI unless
the user was doing something polymorhically anyhow.

Right, this seperates the idea of std::typeinfo as a static part
of RTTI and dynamic_cast<> and typeid( polymorhic_type ) as the
dynamic (runtime) part of RTTI.


Also what are the cases in which dynamic_cast<> doesn't need
a polymorhic type ?

The incremental overhead to already polymorhic classes is a small
constant added to each types vtable. The overhead to non-polymorphic
class is the cost of including some typing feature (notably the vtable
pointer) which may be large if the objects are already small.

I've seen borlands non-standard __rtti keyword that does this on
a class by class basis, and also a compiler option (borland again)
that add's rtti to just about every thing. I've allway's assumed this
was non-standard i.e. not C++ (the compiler version 4.52 if memory
serves, so quite old now).

Rob.
 
R

Ron Natalie

Rob Williscroft said:
Also what are the cases in which dynamic_cast<> doesn't need
a polymorhic type ?

If
the type casted to is the same (or more cv-qualified) as the operand, or
the operand is a null pointer, or
the casted to type is a pointer/reference to an unambiguous accessible
base class of the operand
the operand does not need to be a pointer or lvalvue of polymorphic type.

5.2.7 of the standard lists the above conversions and then at line 6 says
"Otherwise, v shall be a poitner to or an lvalue of a polymorphic type."
I've seen borlands non-standard __rtti keyword that does this on
a class by class basis, and also a compiler option (borland again)
that add's rtti to just about every thing. I've allway's assumed this
was non-standard i.e. not C++ (the compiler version 4.52 if memory
serves, so quite old now).

It is a nonstandard option. The standard ties RTTI behavior to virtual
functions being present.
 
R

Ron Natalie

tom_usenet said:
Given that the "thing" is an exception in this case, this isn't a
limitation!

Huh? Any type can be thrown.

No it isn't! It is only required for the types that are actually used
in a dynamic cast, just as for exception handling, it is only required
for those types used in a catch.

RTTI is not required at all for exceptions to work. The exception object
is not polymorphic. It's copied on basis of it's static type and that static
type is matched against the catch blocks. The only thing that may happen
is one of the implicit conversions.
 
R

Risto Lankinen

tom_usenet said:
I still think catch blocks are a type of RTTI (along with
dynamic_cast, typeid, etc.), since they allow you to get information
about the type of an exception at runtime.

Like many have already said, this is an illusion. In a very similar
way to exceptions, the following program seemingly implements
"a type of RTTI":

void Catch( int ) { cout << "int caught!"; }
void Catch( char * ) { cout << "char * caught!"; }

template <typename T> void Throw( const T &r ) { Catch(r); }

int main()
{
rand()%2 ? Throw( 1 ) : Throw( "" );
}

However, all of the magic takes place at compile time (it really
takes place more typically at link time if we're splitting hairs, but
the point remains: exceptions are run-time-static by nature).

- Risto -
 
T

tom_usenet

Like many have already said, this is an illusion. In a very similar
way to exceptions, the following program seemingly implements
"a type of RTTI":

void Catch( int ) { cout << "int caught!"; }
void Catch( char * ) { cout << "char * caught!"; }

template <typename T> void Throw( const T &r ) { Catch(r); }

int main()
{
rand()%2 ? Throw( 1 ) : Throw( "" );
}

However, all of the magic takes place at compile time (it really
takes place more typically at link time if we're splitting hairs, but
the point remains: exceptions are run-time-static by nature).

Ahh, so your hair is run-time-static vs run-time-dynamic, although
your example is only about the compile-time concepts of templates and
overloading.

RTTI has neither the word dynamic nor the work static in it - it is
the runtime bit that is important, and also the "information" bit.
Exception handling requires runtime compatibility checking of thrown
types with caught types, and how you implement this doesn't change
what it is. dynamic_cast requires runtime compatibility checking of
object types with cast-to types. You can implement catch blocks in any
number of ways that don't involve direct type information. You can
implement dynamic_cast in any number of ways that don't involve type
information. So why isn't exception handling a type of RTTI?

The word "information" is Alf's point of contention, but my point was
that dynamic_cast doesn't require type information either - you just
need to match object creations with matrix rows. His entropy arguments
are irrelevent. He's trying to say that because there is only one
exception, there is no "information" associated with it. However, RTTI
certainly doesn't have a formal definition that uses any mathematical
definition of "information".

I suppose my point is really that RTTI should be reserved for
type_info and typeid, and that a new term should be invented for
dynamic_cast and exception handling, such as "Runtime type
compatibility checking", or RTCC, since neither of them involve
information about the type, only about the compatibility of types.

Tom
 
R

Risto Lankinen

Hi!

RTTI has neither the word dynamic nor the work static in it - it is
the runtime bit that is important, and also the "information" bit.

Would you therefore call the _static_ selection of overloaded
functions...

void Catch( int );
void Catch( void * );

.... a type of RTTI? Why, or why not? If not, what would you
call it instead?
I suppose my point is really that RTTI should be reserved for
type_info and typeid, and that a new term should be invented for
dynamic_cast and exception handling, such as "Runtime type
compatibility checking", or RTCC, since neither of them involve
information about the type, only about the compatibility of types.

Like it has been explained, the implementation details of
dynamic_cast and exception handling do not necessarily
intersect. Attempting to define a descriptive term for what
does not exist is a futile exercise.

On the other hand, if the new term is to describe the union
(rather than intersection) of the two, then things like virtual
methods where the compiler selects a method at run-time
based on the actual type of a polymorphic reference or
pointer, and even overloaded methods where the compiler
selects a method _seemingly_ at run-time based on the
static type of its argument also fall in the domain of this
new term.

Cheers!

- Risto -
 
R

Ron Natalie

tom_usenet said:
The static type is known at the throw site, just as the static type of
an object is known at its creation site.

The point is that the static type isn't known at the catch site,
except through matching of catch blocks, which happens in a similar
way to resolution to dynamic_casts (via implicit conversions from
actual type to cast-to type).

No it does NOT work that way. There's no dynamic typing involved.
It uses the equivelent of calling typeinfo on a non-pointer/non-reference.
It's entirely static.
 
A

Alf P. Steinbach

I still think catch blocks are a type of RTTI (along with
dynamic_cast, typeid, etc.), since they allow you to get information
about the type of an exception at runtime.

This is different from saying that
1. Exception handling requires RTTI
2. Exception handling requires type_info
3. Exception handling requires dynamic_cast.

IMHO, all of the above are false.

I'd just like to clear up a little point. Long after we agreed that
RTTI involves storing some type descriptor in each object, I continued
to meet your arguments with (essentially) the simple assertion that
exception handling does not require RTTI. It didn't occur to me to say
right out that


Exception handling does not require storing a type descriptor in
each object.


since, after all, we'd agreed that that was was RTTI was, and you could
not be _that_ blind, could you?

Now, perhaps, can you see that at least the claim that exception handling
is a _form_ of RTTI is inconsistent with the above definition?

Perhaps even, when you think really Really REALLY hard about it (hold your
pants!), _if_ exception handling is a form of RTTI, then (a) all exception
handling involves RTTI, and so (b) exception handling is not possible to have
without also having RTTI, and so -- from (b) -- (c) exception handling
_requires_ RTTI, in contradiction with your stm that (2) is false?
 
T

tom_usenet

No it does NOT work that way.

What way?
There's no dynamic typing involved.

The static type of an exception isn't known at the site at which it is
caught. It depends how you define "static" and "dynamic". If you
define "static type" as a type known by the compiler at compile time
at a particular line of code, then exception types are dynamic except
at the throw site. Normal object types are dynamic except at their
creation site.
It uses the equivelent of calling typeinfo on a non-pointer/non-reference.
It's entirely static.

Again, what do you mean by static? The static type of an exception is
only known at the throw statement.

The bit that dynamic_cast and catch have in common is the need to
determine whether type A (unknown at the point of the check at compile
time, in one case held in the object, in the other case held in the
stack frame?) is derived from type B (known at the point of the check
at compile time).

Tom
 
R

Ron Natalie

tom_usenet said:
What way?

By looking at the same stuff dynamic_cast (i.e., vtable).

Again, what do you mean by static? The static type of an exception is
only known at the throw statement.

By static, I mean that there is no reason that the object needs to be tagged
with some type information so that the object type can be detected when you
only have a pointer or reference to a possible base class..
The bit that dynamic_cast and catch have in common is the need to
determine whether type A (unknown at the point of the check at compile
time, in one case held in the object, in the other case held in the
stack frame?) is derived from type B (known at the point of the check
at compile time).

Nope. Dynamic cast needs additional help because it has no clue what
the object that the pointer/reference actually is (only that it knows what a
permissable base class is). It needs to inspect the object to determine
what the t ype really is.

Throwing execptions is different. At compile time essentially the throw code
only has to remember the static type (you can't throw polymorphically) of the
experssion thrown. This is then checked at runtime against the various
catch blocks as it's unwinding the stack. However the value actually thrown
is immaterial, the exception handling code doesn't EVER need to look at it
until it has already found the catch block and needs it to initalize the local
variable there.
 
E

Erik

Exception handling does obviously not require RTTI.

Proof:

class A {};
class B : public A {};

void f() {
throw B();
}

A *g() {
return B;
}

int good() {
try {
f();
}
catch (A &a) {
cout << "Here";
}
}

int bad() {
A *a = g();
B *b = dynamic_cast<B *>(a);
}

good() is valid according to the example in The C++ Programming Language,
3rd ed, section 14.2.
<Quote>
class Matherr {};
class Overflow : public Matherr {};
class Underflow : public Matherr {};
class Zerodivide : public Matherr {};
// ..

This allows us to handle anny Matherr without caring precisely which kind it
is. For example:

void f()
{
try {
// ...
}
catch (Overflow) {
// handle overflow or anything derived from Overflow
}
catch (Matherr) {
// handle any Matherr that is not Overflow
}
}

<End quote>

bad() is invalid according to the same book, section 15.4.1: "A dynamic_cast
requires a pointer
or a reference to a polymorphic type to do a downcast or a crosscast". A ->
B is a downcast,
B is not polymorphic.

However, I have no idea how EH is implemented. Please tell me if you find
out, I'm interested.

/ Erik
 
R

Ron Natalie

Erik said:
However, I have no idea how EH is implemented. Please tell me if you find
out, I'm interested.
Well in the case of the Sun compiler (which I havehandy). It passes the value
(possibly a pointer to it) and a constant which appears to be the type_info
value for the static type thrown and then calls a function CrunIex_throw.
Presumably this hunts for the catch blocks with a matching type_info on
their argument.
 
T

tom_usenet

By looking at the same stuff dynamic_cast (i.e., vtable).



By static, I mean that there is no reason that the object needs to be tagged
with some type information so that the object type can be detected when you
only have a pointer or reference to a possible base class..

That isn't the definition of static type:

1.3.11 static type
the type of an expression (3.9), which type results from analysis of
the program without considering execution semantics. The static type
of an expression depends only on the form of the program in which the
expression appears, and does not change while the program is
executing.

In addition, an exception does have to be tagged with some "type
information" to allow matching catch blocks to be chosen. The
exception object itself won't contain this information (it may not
have a vtable), but it is there nevertheless, held globally. Some
implementations choose the obvious implementation of holding the
(statically determined) type_info object for the thrown exception.
Others might just hold the lesser information required to check
compatibility with catch blocks.
Nope. Dynamic cast needs additional help because it has no clue what
the object that the pointer/reference actually is (only that it knows what a
permissable base class is). It needs to inspect the object to determine
what the t ype really is.

Yes, that pretty obviously (I thought) what I meant by "in one case
held in the object".
Throwing execptions is different. At compile time essentially the throw code
only has to remember the static type (you can't throw polymorphically) of the
experssion thrown. This is then checked at runtime against the various
catch blocks as it's unwinding the stack. However the value actually thrown
is immaterial, the exception handling code doesn't EVER need to look at it
until it has already found the catch block and needs it to initalize the local
variable there.

Yes, I don't (and haven't) disagreed with any of that. The dynamic
type and static type of an exception are the same at the throw site
(since whatever the dynamic type of the object thrown, a copy is made
of the static type). At the catch site they can be different of
course.

I think this thread might come to a close quicker if I clarified.

1. dynamic_cast checks to see whether the dynamic type of the passed
object *is a subclass of* the cast-to type.
2. catch blocks check to see whether the thrown exception type *is a
subclass of* the caught type.
3. Both these checks must occur at runtime. The dynamic type of an
object is not generally known at compile time. The type of an
exception caught by a particular catch is not known at compile time.
4. Exceptions don't need to have any information stored inside them
about their dynamic type, since the compiler can store it in a "global
datum" at the throw point. Some compilers store the statically
determined type_info object for the thrown type, but their are many
alternatives, such as storing an index into a throw/catch
compatibility matrix.
5. Objects (with virtual functions) do need to have information stored
along with them in order to implement dynamic_cast. This is because
their are many objects in existence at once, so you can't store a
single "global datum" to tell you the type as you can with exceptions
(of which there is only one at a time). Most compilers store a pointer
to the type_info for an object in the type's vtable, but alternative
implementations, such as storing an index into an object creation/cast
target compatibility matrix directly in the object, are possible.
6. Some descriptive definitions of RTTI exclude dynamic_cast. Others
include exception handling.

The points of others that I have disagreed with:

1. Exception handling doesn't require a runtime check for
compatibility of types.
2. The compatibility check required for exception handling is
substantially different to the check required by dynamic_cast. I have
stated the differences (need to store information associated with each
object for dynamic_cast), but this seemed to be disagreed with.
3. Various definitions of RTTI. There is no formal definition that
I've been able to find. Possibilities include prescriptive ones
(dynamic_cast, typeid, the contents of <typeinfo> (and catch block
matching according to some)), descriptive ones (constructs requiring
checking or identification of types at runtime, constructs requiring
information to be stored in each object relating to its type). I have
pointed out the inadequacy of some vague descriptive definitions,
since they either include catch block matching or exclude dynamic_cast
from RTTI, which I assume wasn't the intent (since the poster in
question was insisting that exception handling has nothing to do with
RTTI, yet dynamic_cast is RTTI). Other definitions talk in terms of
implementation (constructs requiring information to be stored in each
object relating to its type), and therefore seem to me to be poor
definitions.
4. Comments that "is a type of" is the same as "requires".

Tom
 
R

Ron Natalie

tom_usenet said:
In addition, an exception does have to be tagged with some "type
information" to allow matching catch blocks to be chosen. The
exception object itself won't contain this information (it may not
have a vtable),

Even if it had a vtable, it wouldn't be useful. It's the static type of
the object thrown that matters not it's dynamic type.
but it is there nevertheless, held globally.

I don't know what you mean by "held globally" but I suspect you're wrong.
The type info is typically a static value that's passed along with the value
of the expression in some generic form to the runtime exception handler.
Yes, I don't (and haven't) disagreed with any of that. The dynamic
type and static type of an exception are the same at the throw site
I think this thread might come to a close quicker if I clarified.

1. dynamic_cast checks to see whether the dynamic type of the passed
object *is a subclass of* the cast-to type.

By examining the value.
2. catch blocks check to see whether the thrown exception type *is a
subclass of* the caught type.

Because it must be told the type thrown. Looking at the value of the
expression will not provide the correct answer (nor will it work for non
class types anyhow).
3. Both these checks must occur at runtime. The dynamic type of an
object is not generally known at compile time.

The compiler cares not one whit about the runtime dynamic type of
the object. Consider it to be one gigantic static switch statement. Only
the static types matter.
The type of an
exception caught by a particular catch is not known at compile time.

It will always be the type of the catch or something implicitly convertable
to it. The dynamic type is NOT used to match arguments to catch blocks.
4. Exceptions don't need to have any information stored inside them
about their dynamic type, since the compiler can store it in a "global
datum" at the throw point.

They NEED the global datum as you call it because the dynamic type
has no meaning to catch. Catch doesn't match on dynamic type. It
catches based on the static type thrown (this compiled into the code
at compile time). It is never necessary for the compiler to examine the
argument passed at runtime to find the catch block.
5. Objects (with virtual functions) do need to have information stored
along with them in order to implement dynamic_cast. This is because
their are many objects in existence at once, so you can't store a
single "global datum" to tell you the type as you can with exceptions

It matters because they actually can be different types at run time.
Thrown objects are always known at compile time.
1. Exception handling doesn't require a runtime check for
compatibility of types.

It doesn't require a runtime examination of the thrown value.
It runtime checks the static type thrown against possible blocks.
2. The compatibility check required for exception handling is
substantially different to the check required by dynamic_cast.

It's nothing like it. I think you are finally seeing the like. Exception
handling and dynamic_cast have nothing at all to do with each other.
 

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,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top