 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
davidbaraff@gmail.com Guest
|
Posted: Sat Dec 10, 2005 2:15 am Post subject: reinterpret_cast<> + virtual call vs. dynamic_cast + non-vir |
|
|
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
|
Posted: Sat Dec 10, 2005 12:59 pm Post subject: (sorry, typo in original:) Re: reinterpret_cast<> + virtual |
|
|
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
|
Posted: Sat Dec 10, 2005 9:23 pm Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non |
|
|
[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
|
Posted: Sun Dec 11, 2005 12:55 pm Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non |
|
|
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
|
Posted: Mon Dec 12, 2005 12:11 pm Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non |
|
|
[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
|
Posted: Mon Dec 12, 2005 7:51 pm Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non |
|
|
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
|
Posted: Tue Dec 13, 2005 11:06 pm Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non |
|
|
[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
|
Posted: Wed Dec 14, 2005 3:20 am Post subject: Re: reinterpret_cast<> + virtual call vs. dynamic_cast + non |
|
|
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 |
|
 |
|
|
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
|
|