I don't get it

O

Old Wolf

If C is derived from A & B, then a C object IS an A and IS a B. At the
same time.

Yes, but an A is not a B. The two are unrelated
except in that they are both part of a C. If
you are pointing to an A, then the only way you
are going to be able to point to a related B is
if you know you are actually pointing to an A
sub-object of a C. Which is why this doesn't work
if you are using a pointer to void, which contains
no type information.
// quick recap: class C is derived from both class A and B

A *ap;
B *bp;
C c,*cp;

cp=&c;
ap=cp;
bp=(B*)ap;

This is a reinterpret-cast, because A and B are unrelated
types (see above). The pointer 'ap' has lost the information
that it is actually pointing to an A that's a subobject
of a C.
or the last line being

bp=static_cast<B*>(ap);

Should be a compliation error for the above reason.

You can perform the conversion successfully:
bp = dynamic_cast<B *>(ap);

which tells the compiler to find the biggest object
that *ap is a sub-object of, and then try to find a
B object within that object. This is sometimes
called cross-casting.

Note, I'm not entirely sure about this, but I think
this only works if A and B are both polymorphic
classes, that is, they both contain at least one
virtual function (which will usually be the virtual
destructor). You might just get a null pointer if
this requirement is not met.
 
A

Andre Kostur

[more snip as the issue has been resolved]
Indeed.

But i prefer not to use RTTI for the reasons mentioned in the earlier
post.

Next item.... measure, measure, measure. How do you know that your own
RTTI method is ligher-weight than the one that the compiler is using?
I guess i was confused:

Because i ran into the casting problem (due to the use of the void*),
i started thinking that it had to do with the multiple inheritance...

I really didn't know that using a void* abandons all the class
relation info. (and that after all these years of programming, damn
damn damn)

Thanks a lot for your help!

A dummy got a bit less dummy :)

Ask questions... that's what we're here for :)
 
J

Jo

Old said:
Yes, but an A is not a B. The two are unrelated
except in that they are both part of a C. If
you are pointing to an A, then the only way you
are going to be able to point to a related B is
if you know you are actually pointing to an A
sub-object of a C. Which is why this doesn't work
if you are using a pointer to void, which contains
no type information.




This is a reinterpret-cast, because A and B are unrelated
types (see above). The pointer 'ap' has lost the information
that it is actually pointing to an A that's a subobject
of a C.




Should be a compliation error for the above reason.

You can perform the conversion successfully:
bp = dynamic_cast<B *>(ap);

which tells the compiler to find the biggest object
that *ap is a sub-object of, and then try to find a
B object within that object. This is sometimes
called cross-casting.

That's powerful!

But at what cost?

Do you know how heavy the RTTI overhead is

-> on memory
-> on cpu

?

If it's similar to using virtual functions, then it's ok.
 
J

Jo

Andre said:
Andre Kostur wrote:

[more snip as the issue has been resolved]


Indeed.

But i prefer not to use RTTI for the reasons mentioned in the earlier
post.

Next item.... measure, measure, measure. How do you know that your own
RTTI method is ligher-weight than the one that the compiler is using?

True.

That's also why i asked about the cost of RTTI.

At the moment i don't have a clue.
 
J

Jo

Jo said:
True.

That's also why i asked about the cost of RTTI.

At the moment i don't have a clue.


Well, i've been experimenting a bit with dynamic_cast and must say it's
a nice feature.

Why is it that i read al these warnings about RTTI, not only about the
overhead, but also about "it's better not to use it".

Didn't find some explicit reasoning yet.
 
J

Jo

Jo said:
Well, i've been experimenting a bit with dynamic_cast and must say
it's a nice feature.

Why is it that i read al these warnings about RTTI, not only about the
overhead, but also about "it's better not to use it".


I'm sorry, must be more precise: the warnings i read is more like
"better not to use it, unless really necessary".
 
A

Andre Kostur

I'm sorry, must be more precise: the warnings i read is more like
"better not to use it, unless really necessary".

Performance-wise there is a certain cost to using it, but I don't think
it would be any heavier than attempting to implement your own. I think
this is really a QoI (Quality of Implementation) issue with whatever
compiler you're using.

However, a necessity to using dynamic_cast<> tends to indicate a
potential flaw in your design. You should really be asking yourself why
you need to know the derived type. Couldn't it be better implemented as
a virtual function in the base class? So if the function you're
attempting to call in C is already virtual in A, then you don't need
explicitly know that you're working with a C object. Just call the
function through the A* and let the compiler follow the virtual dispatch
to call the real function.
 
J

James Kanze

class A {
public:
char text_a[100];
A() { *text_a=0; }
~A() {}};
//-----------------------------------------------------------------------------
class B {
public:
char text_b[100];
B() { *text_b=0; }
~B() {}};
//-----------------------------------------------------------------------------
class C : public A, public B {
public:
char text_c[100];
C() { *text_c=0; }
~C() {}};
//-------------------------------------------------------------------------------
void test() {
B *bp1,*bp2;
C c,*cp1,*cp2,*cp3;
void *p;

strcpy(c.text_a,"hello a");
strcpy(c.text_b,"hello b");
strcpy(c.text_c,"hello c");
cp1=&c;
p=cp1;
bp1=cp1; // ok
bp2=(B*)p; // resulting bp2 is WRONG!

And why should it be otherwise? You do something illegal,
specified as undefined behavior, and you get undefined behavior.

p is a void*. About the only things you can do with a void*
(except copying it) is cast it back to the *original* type, or
to a character type to access the underlying raw memory. Since
the original type was C*, casting it back to anything but a C*
is undefined behavior, and anything can happen.

bp2 = (B*)(C*)p ;

should work.

[...]
So the hot spot is the bp2=(B*)p;
What's wrong with that???

It's forbidden by the language.

[...]
If so, please give me a link to where i can study this right.

Not a link, but in the standard:

§4.10 Pointer conversions (paragraph 2):

An rvalue of type "pointer to cv T ," where T is an
object type, can be converted to an rvalue of type
"pointer to cv void". The result of converting a
"pointer to cv T" to a "pointer to cv void" points to
the start of the storage location where the object of
type T resides, as if the object is a most derived
object (1.8) of type T (that is, not a base class
subobject). The null pointer value is converted to the
null pointer value of the destination type.

And §5.2.9 Static cast (paragraph 11):

An rvalue of type "pointer to cv1 void" can be converted
to an rvalue of type "pointer to cv2 T", where T is an
object type and cv2 is the same sv-qualifiation as, or
greater cv-qualifiation than, cv1. The null pointer
value is converted to the null pointer value of the
destination type. A value of type pointer to object
converted to "pointer to cv void" and back, possibly
with different cv-qualifiation, shall have its original
value.

You'll note, in that last paragraph, that nothing is said
about the value when converting back to a different type;
the results are not defined by the standard, and can be
anything.
 
J

Jo

Andre said:
Performance-wise there is a certain cost to using it, but I don't think
it would be any heavier than attempting to implement your own. I think
this is really a QoI (Quality of Implementation) issue with whatever
compiler you're using.

However, a necessity to using dynamic_cast<> tends to indicate a
potential flaw in your design. You should really be asking yourself why
you need to know the derived type. Couldn't it be better implemented as
a virtual function in the base class? So if the function you're
attempting to call in C is already virtual in A, then you don't need
explicitly know that you're working with a C object. Just call the
function through the A* and let the compiler follow the virtual dispatch
to call the real function.

Thought 1: If every very base class should foresee all virtual functions
that are coming down the hierarchic tree (or do they say "up" the tree?
still confused), then these base classes get much too much cluttered
with non-relevant virtual funcs!

Thought 2: If class C is derived from class A and B, both having virtual
funcs, and i have an A pointer to a C object, and need to call a B func
of the C object, then do you propose to copy all the virtuals of B in A?
I'm confident the answer is no.

So these are two common situations where i need to know what kind of
object i'm dealing with.

In fact, the two thoughts can be summarized to this common reason to use
RTTI: to avoid "virtual clutter".

My conclusion: If you think about it pure conceptually, i don't see
what's wrong with using RTTI.Using virtuals to avoid RTTI is one good
strategy. Using RTTI to avoid virtuals is another good strategy. It are
two equivalent techniques, imo, one better in situation a, the other
better in situation b.
 
J

James Kanze

John Harrison wrote:

[...]
But i was just reading another webpage where they indeed use
base-to-derived dynamic casts...
Did i misunderstand the cplusplus page?

You didn't read the sentence completely. It says: "The second
conversion in this piece of code would produce a compilation
error since base-to-derived conversions are not allowed with
dynamic_cast UNLESS the base class is polymorphic." Note that
final clause, with the unless. In their example, the base class
didn't have any virtual functions, so using dynamic_cast to the
derived would be a compiler error.

Generally speaking, you should avoid using void* as much as
possible, and use dynamic_cast exclusively for moving between
classes in a hierarchy. When you can't avoid void*, e.g.
because of legacy or C interfaces, always cast back to exactly
the type of pointer you originally had, then go from there.
Note that often, this means casting to a specific type before
casting to void*. For example (using pthread_create as an
example, but it could be any C API function that takes a pointer
to function and a void*):

class Thread
{
public:
virtual ~Thread() {}
virtual void* run() = 0 ;
} ;

extern "C"
void*
threadStarter( void* p )
{
Thread* t = static_cast< Thread* >( p ) ;
return t->run() ;
}

void
f()
{
// MyThread derives from Thread...
pthread_t t ;
pthread_create( &t, NULL, &threadStarter, new MyThread ) ;
}

As written, this is undefined behavior. The last code line must
be:

pthread_create(
&t, NULL, &threadStarter, static_cast< Thread* >( new
MyThread ) ) ;

(The problem is, that as it is undefined behavior, it might seem
to work some of the time anyway. On most implementations, for
example, it will seem to work as long as only single inheritance
is involved.)

Note that the solution here does NOT involve dynamic_cast; you
don't want a pointer to the most derived type, but to the type
to which the void* will ultimately be converted. (The
documentation of "threadStarter" should specify this.)
 
J

Jo

James said:
John Harrison wrote:


[...]

dynamic_cast does do base to derived conversion, that's it's main use.
Where are you getting your information?


I got that fromhttp://www.cplusplus.com/doc/tutorial/typecasting.html




But i was just reading another webpage where they indeed use
base-to-derived dynamic casts...




Did i misunderstand the cplusplus page?

You didn't read the sentence completely. It says: "The second
conversion in this piece of code would produce a compilation
error since base-to-derived conversions are not allowed with
dynamic_cast UNLESS the base class is polymorphic." Note that
final clause, with the unless. In their example, the base class
didn't have any virtual functions, so using dynamic_cast to the
derived would be a compiler error.

I understand.
Generally speaking, you should avoid using void* as much as
possible, and use dynamic_cast exclusively for moving between
classes in a hierarchy. When you can't avoid void*, e.g.
because of legacy or C interfaces, always cast back to exactly
the type of pointer you originally had, then go from there.
Note that often, this means casting to a specific type before
casting to void*. For example (using pthread_create as an
example, but it could be any C API function that takes a pointer
to function and a void*):

class Thread
{
public:
virtual ~Thread() {}
virtual void* run() = 0 ;
} ;

extern "C"
void*
threadStarter( void* p )
{
Thread* t = static_cast< Thread* >( p ) ;
return t->run() ;
}

void
f()
{
// MyThread derives from Thread...
pthread_t t ;
pthread_create( &t, NULL, &threadStarter, new MyThread ) ;
}

As written, this is undefined behavior. The last code line must
be:

pthread_create(
&t, NULL, &threadStarter, static_cast< Thread* >( new
MyThread ) ) ;

(The problem is, that as it is undefined behavior, it might seem
to work some of the time anyway. On most implementations, for
example, it will seem to work as long as only single inheritance
is involved.)

Indeed, and that's why i was so confused, because i've been using this
many times. (too many it seems now!)

I guess that, by coincidence, i never ran into the combination of using
a void* and multiple inherited objects, and so that's why i only
encountered the errors now.

I'm happy that this has been sorted out, and i completely understand the
reasons beyond it.

All sounds logical.

I'll avoid using void* as much as possible!

And i should use more static_cast / dynamic_cast instead of the old
c-style casts, because they're more error-conscious.
(a pity of that quircky syntax though)
 
A

Andre Kostur

[snip]
However, a necessity to using dynamic_cast<> tends to indicate a
potential flaw in your design. You should really be asking yourself
why you need to know the derived type. Couldn't it be better
implemented as a virtual function in the base class? So if the
function you're attempting to call in C is already virtual in A, then
you don't need explicitly know that you're working with a C object.
Just call the function through the A* and let the compiler follow the
virtual dispatch to call the real function.

Thought 1: If every very base class should foresee all virtual
functions that are coming down the hierarchic tree (or do they say
"up" the tree? still confused), then these base classes get much too
much cluttered with non-relevant virtual funcs!

Like most trees in computing, they're usually thought of as growing
downwards. So the most base class is usually written at the top, then
the next derived class below it, etc. Thus the terms "upcasting" and
"downcasting" through an inheritance hierarchy. Upcasting is free,
downcasting requires a cast.

Note that I said "tends to indicate a potential flaw". There are cases
where it may make sense. I'm not sure I can explain how you'd know when
to use it.

Perhaps you really wanted composition where you're using inheritance?

Thought 2: If class C is derived from class A and B, both having
virtual funcs, and i have an A pointer to a C object, and need to call
a B func of the C object, then do you propose to copy all the virtuals
of B in A? I'm confident the answer is no.

No, but whatever function that's going to use the B functionality should
take a B* (or B&). Or perhaps the A class should really have a B as a
member, not as a parent. If you really need to access it through an A*,
perhaps the design is flawed.
So these are two common situations where i need to know what kind of
object i'm dealing with.

In fact, the two thoughts can be summarized to this common reason to
use RTTI: to avoid "virtual clutter".

My conclusion: If you think about it pure conceptually, i don't see
what's wrong with using RTTI.Using virtuals to avoid RTTI is one good
strategy. Using RTTI to avoid virtuals is another good strategy. It
are two equivalent techniques, imo, one better in situation a, the
other better in situation b.

You're blurring the idea of Object Oriented programming (not to say that
you must use OO, C++ supports multiparadigm programming...). The idea
behind inheritance and virtual functions is that it should be eliminating
the need to know exactly which derived class you're dealing with.
 

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,995
Messages
2,570,236
Members
46,821
Latest member
AleidaSchi

Latest Threads

Top