C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

reinterpret_cast<> + virtual call vs. dynamic_cast + non-vir

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
davidbaraff@gmail.com
Guest





PostPosted: Sat Dec 10, 2005 2:15 am    Post subject: reinterpret_cast<> + virtual call vs. dynamic_cast + non-vir Reply with quote



Consider a class (like boost's any) which wants to store heterogenous
datatypes, and then retrieve them (safely). One pattern might be as
follows (with many details omitted, for brevity):

class UntypedData {
public:
template <typename T>
const T& Get() {
if (typeid(T) != _GetTypeid())
ERROR;
else // call virtual _GetData()
return *(reinterpret_cast<T*>(_GetData()));
}
private:
virtual void* _GetData() = 0;
virtual const type_info& _GetTypeid();
};

template <typename T>
class Hidden_Data : public UntypedData {
public:
Hidden_Data(const T& t) : _value(t) { }

virtual void* _GetData() { return &_value; }
virtual const type_info& _GetTypeid() { return typeid(T); }

// non-virtual call, only in derived class:
const T& _GetTypedData() { return _value; }
};

Usage:
UntypedData* myStuff = new Hidden_Data<int>(42);
UntypedData* myStuff2 = new Hidden_Data<double>(3.141);
int val = myStuff->Get<int>();
double pi = myStuff2->Get<double>();

An alternative approach is to eschew the reinterpret_cast<> in favor
of a dynamic_cast<> in UntypedData::Get(), as follows:

// in UntypedData:
template <typename T>
const T& Get() {
if (typeid(T) != _GetTypeid())
ERROR;
else // call non-virtual _GetTypedData
return (dynamic_cast<T*>(this))->_GetTypedData();
}

Does anyone see any reasons why one approach is markedly better than
the other?
(This question is *not* limited to performance issues; any reasons at
all why one is superior is what I'm after.) In terms of performance,
they both call a virtual function (_GetData() vs dynamic_cast),
plus a call to typeid().


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
deb@pixar.com
Guest





PostPosted: Sat Dec 10, 2005 12:59 pm    Post subject: (sorry, typo in original:) Re: reinterpret_cast<> + virtual Reply with quote



Ugh, I can't even get my own posts straight today.
I really wanted to compare the implementations of

// in UntypedData:
template <typename T>
const T& Get() {
if (typeid(T) != _GetTypeid())
ERROR;
else // call non-virtual _GetTypedData
return (static_cast<T*>(this))->_GetTypedData();
}

vs.

// in UntypedData:
template <typename T>
const T& Get() {
if (typeid(T) != _GetTypeid())
ERROR;
else // call virtual _GetData()
return *(reinterpret_cast<T*>(_GetData()));
}

I know the second version has one more virtual call than the first,
please ignore that performance consideration for now.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David Abrahams
Guest





PostPosted: Sat Dec 10, 2005 9:23 pm    Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non Reply with quote



[email]davidbaraff (AT) gmail (DOT) com[/email] writes:

Quote:
Does anyone see any reasons why one approach is markedly better than
the other?
(This question is *not* limited to performance issues; any reasons at
all why one is superior is what I'm after.) In terms of performance,
they both call a virtual function (_GetData() vs dynamic_cast),
plus a call to typeid().

Well, the T* -> void* conversion is the same as a static_cast. The
standard gives no guarantees of behavior for a round-trip conversion
that uses static_cast in one direction and reinterpret_cast in the
other.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
davidbaraff@gmail.com
Guest





PostPosted: Sun Dec 11, 2005 12:55 pm    Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non Reply with quote


David Abrahams wrote:
Quote:

Well, the T* -> void* conversion is the same as a static_cast. The
standard gives no guarantees of behavior for a round-trip conversion
that uses static_cast in one direction and reinterpret_cast in the
other.

Good point. I didn't think a static_cast<> from void* back to T* was
allowed, but I see now it is.
Ok, suppose then that the version with the virtual call is instead:

// in UntypedData:
template <typename T>
const T& Get() {
if (typeid(T) != _GetTypeid())
ERROR;
else // call virtual _GetData()
return *(static_cast<T*>(_GetData()));
}

Anything now that makes one way clearly a better idea than the other?
(Although I think an answer is emerging: the version using
_GetTypedData() involves only one cast, and one less virtual call. As
you point out, the version with the virtual call has two casts, though
one of them is implicit in the virtual function itself.)


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Ulrich Eckhardt
Guest





PostPosted: Mon Dec 12, 2005 12:11 pm    Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non Reply with quote

[email]davidbaraff (AT) gmail (DOT) com[/email] wrote:
Quote:
class UntypedData {
template <typename T
const T& Get() {
if (typeid(T) != _GetTypeid())
ERROR;
else // call virtual _GetData()
return *(reinterpret_cast }

const T& _GetTypedData() { return _value; }

Symbols beginning with an underscore followed by an uppercase letter are
reserved, you aren't allowed to use them.

Quote:
An alternative approach is to eschew the reinterpret_cast<> in favor
of a dynamic_cast<> in UntypedData::Get(), as follows:

// in UntypedData:
template <typename T
const T& Get() {
if (typeid(T) != _GetTypeid())
ERROR;
else // call non-virtual _GetTypedData
return (dynamic_cast_GetTypedData();
}

Here, I now see the problem that the dynamic_cast is useless. You already
know the type of 'this' so there is no need to use dynamic_cast. I'd even
assume it performs worse, because a dynamic cast is sometimes a pretty
complex thing because it also works for baseclasses on a derived object.

Other than that, the dynamic_cast is simply wrong, too: in one case, you
cast 'this' (i.e. from container baseclass to container derived class) and
in the other you do the operation on the data. You should have used a cast
to 'HiddenData<T>*' and not to 'T*'. In that case, you can then as well
use this code:
if( HiddenData<T>* der = dynamic_cast<HiddenData(this))
return der->_GetTypedData();
else
ERROR;

Further, dynamic_cast requires a polymorphic type, so you can't dynamic
cast on a type without virtual functions. This is also solved when using
the above change, because class UntypedData is always polymorphic.

Quote:
Does anyone see any reasons why one approach is markedly better than
the other?
(This question is *not* limited to performance issues; any reasons at
all why one is superior is what I'm after.) In terms of performance,
they both call a virtual function (_GetData() vs dynamic_cast),
plus a call to typeid().

If you are after the performance, you should create a few benchmarks that
define what you mean with it. Other than that, I'd try to avoid virtual
calls as far as possible, i.e. I'd use an approach like this:

void* do_get( type_info const&) = 0;

The derived class then compares the type info and returns the data which
you can simply static_cast<> to the requested type.
if( void* raw = do_get( typeid(T)))
return *static_cast<T*>(raw);
else
ERROR;

If you want to go further, you might want to eliminate the requirement for
polymorphism at all: since you only have one single virtual function left,
you could as well store a pointer to it in the baseclass instead of a
pointer to the vtable (yes, not guaranteed by the standard but the typical
implementation) which then points to the function, and some more stuff you
possibly don't need (the rest of the RTTI). I remember an article in one
of the recent CUJ which did something similar.

template<typename T>
static void*
try_convert( UntypedData* base, type_info const& ti)
{
if(typeid(T) == ti)
return static_cast<HiddenData(base)->data;
else
return 0;
}

The proper instance of this non-member function is then passed as pointer
to the baseclass initialiser list.

There's one thing I like about neither your approach nor Boost's, and that
is that a Get<baseclass> will fail if the object contains a derived
object, but that isn't that easy to resolve and probably costs
performance.

Uli


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
davidbaraff@gmail.com
Guest





PostPosted: Mon Dec 12, 2005 7:51 pm    Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non Reply with quote

Ulrich wrote:
There's one thing I like about neither your approach nor Boost's, and
that
is that a Get<baseclass> will fail if the object contains a derived
object, but that isn't that easy to resolve and probably costs
performance.

As an aside, the code I posted is just a pared-down snippet of the real
code. In fact, we do handle the above (converting to the baseclass, or
in fact, to any desired type that the stored pointer can be dynamically
cast to just fine). An "any" which requires you to exactly guess the
static type of the pointer at the time it was stored in the "any", when
the pointer is part of an inheritance hiearchy is fairly useless in
practice.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David Abrahams
Guest





PostPosted: Tue Dec 13, 2005 11:06 pm    Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non Reply with quote

[email]davidbaraff (AT) gmail (DOT) com[/email] writes:

Quote:
Ulrich wrote:
There's one thing I like about neither your approach nor Boost's, and
that
is that a Get<baseclass> will fail if the object contains a derived
object, but that isn't that easy to resolve and probably costs
performance.

A dynamic cast from one typeid to another would be fantastic, and
should be implementable.

dynamic_cast( some_pointer, source_typeid, target_typeid )

Someone needs to write a proposal, though. One very complicated
module in Boost.Python is dedicated to emulating that functionality.
Unfortunately, it requires explicit registration of base/derived
relationships.

Quote:
As an aside, the code I posted is just a pared-down snippet of the real
code. In fact, we do handle the above (converting to the baseclass, or
in fact, to any desired type that the stored pointer can be dynamically
cast to just fine).

What did you do to implement that capability? Do you require
base-derived relationships to be registered?

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
davidbaraff@gmail.com
Guest





PostPosted: Wed Dec 14, 2005 3:20 am    Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non Reply with quote

David Abrahams writes:
A dynamic cast from one typeid to another would be fantastic, and
should be implementable.
dynamic_cast( some_pointer, source_typeid, target_typeid )

Someone needs to write a proposal, though. One very complicated
module in Boost.Python is dedicated to emulating that functionality.
Unfortunately, it requires explicit registration of base/derived
relationships.

--------------------------------

Hi David A; in fact we had this conversation (with other participants)
about a year or so ago. To be completely portable, as you [as a Boost
author] require, yes, you have to explicitly register base/derived
relationships (and which is exactly what I was doing, I'm sure we have
highly isomorphic implementations).

For those of us with the "freedom" to live only within the gcc world
(or slightly wider, within runtimes that use the C++ ABI provided by
the code-sourcery folks), their abi::__dynamic_cast function (with a
little work on the side) provides exactly this.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.