[cxx-abi-dev] trivial __dynamic_cast fails?
David Baraff
deb at pixar.com
Sat Feb 28 05:03:44 UTC 2004
At Pixar, we've been making use of a type-system that piggy-backs off
of RTTI.
The only thing we require users to do is the following for each type
they want to participate in the type system:
----source-file: derived.cpp-----
#include "instantiateType.h"
#include "derived.h"
TF_TYPE_INSTANTIATE(Derived, TF_1_PARENT(Base));
----------------------------------
At startup-time, the above macro injects into a registry that Derived
has a parent, Base.
(Presumably base has done the same, possibly saying it has no parents
and really is a base.)
If you have multiple parents, you can do e.g.
TF_TYPE_INSTANTIATE(MyMultiClass, TF_3_PARENT(Base1, Base2, Base3));
Along with inserting the parent/child info, the macro above also
synthesizes cast functions to/from parent and child and adds them to
the registry -- this is trivially done in the macro, since static type
information is available. After this, at runtime, we simply use the
typeid() operator on an object and use the RTTI type_info structure to
do lookups in our type-registry, allowing for all the queries and
conversions you'd imagine in functions that have no access to static
type information.
The only ugliness, in fact, is requiring users to add that macro in
their source code.
It would be nice to avoid that.
------------------------------------------------------
The __cxxabiv1 spec could let us avoid the macro ugliness. (I say
could because I'm quite leery about depending on constructs outside the
language. While Pixar is limited to linux and OsX right now, [i.e.
both g++ compilers], you never know how things could change.)
Anyway, we make use of the type facility to let our variant class
[called TfAny, in fact!] hold onto a pointer of any type, and then let
a user ask if it is holding that particular pointer type, or if it
could be cast to that pointer type. I don't see anyway that a query
involving parent/child relationship can be done completely at run-time,
by a class like boost::any without either the scheme I describe I
above, or reliance on a facility like __cxxabiv1. Since you create a
virtual function which knows only one type, you can't ask it if a cast
can succeed to an arbitrary static type, you *have* to be able to ask
if it a cast to a type represented by a type_info can succeed. And
*that* can't be done within the language as it stands.
You wrote
>> Here's a biggy. Consider the boost::any class. Currently, if it
>> holds a Derived* and you ask it for a Base*, I think it says "no can
>> do." Imagine if it could actually say "yes, I can give you back a
>> Base*", because it could dynamically look up the parent chain, and do
>> the address shift.
>>
>> That would make boost::any a heck of a lot more useful than it is
>> today.
>
> boost::any could already be modified to do this, since it "knows" the
> "Derived" type. I've enclosed an implementation. Seems to reveal EH
> bugs in quite a few compilers, tho ;-)
I don't understand what you mean by the above. As I see it in the
implementation, the any_cast<> operation must hit *exactly* the type
actually being held or it fails.
The holder object can't answer the question "can your object be cast to
some other type" without making use of the abi-specific functionality
we are discussing. Do you think it can?
Anyway, to answer your other question about shifting addresses, it's
trivial:
(and Mark Mitchell patiently informed me that what I wanted was indeed
sitting in the structure, if I'd just take the time to read carefully).
In the code below, ArchGetDemangled() takes a const type_info& and
returns the demangled string name:
#include <cxxabi.h>
void
PrintParents(const std::type_info* ti)
{
if (const abi::__si_class_type_info* siTi =
dynamic_cast<const abi::__si_class_type_info*>(ti)) {
// there is no offset between parent and base class
printf("%s", ArchGetDemangled(*siTi->__base_type).c_str());
}
else if (const abi::__vmi_class_type_info* vmiTi =
dynamic_cast<const abi::__vmi_class_type_info*>(ti)) {
for (size_t i = 0; i < vmiTi->__base_count; i++) {
printf("%s [offset %d]\n",
ArchGetDemangled(*vmiTi->__base_info[i].__base_type).c_str(),
(vmiTi->__base_info[i].__offset_flags) >>
abi::__base_class_type_info::__offset_shift);
}
}
}
template <typename T>
void PrintParents()
{
printf("Parents of %s: ", ArchGetDemangled(typeid(T)).c_str());
PrintParents(&typeid(T));
printf("\n");
}
----------------------------------------
I'll tell you, if ever a feature was needed to be added to the core C++
language, it would be just standardizing the __cxxabiv1 spec across all
platforms. (Yes, I know, wishes and fishes...). [Well, maybe I'd take
if_templated(<compile-time-expression>)
... // only needs to be legal if guard above is true
else
...
first.]
>
> // See http://www.boost.org/libs/any for Documentation.
>
> #ifndef BOOST_ANY_INCLUDED
> #define BOOST_ANY_INCLUDED
>
> // what: variant type boost::any
> // who: contributed by Kevlin Henney,
> // with features contributed and bugs found by
> // Ed Brey, Mark Rodgers, Peter Dimov, and James Curran
> // when: July 2001
> // where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95
>
> #include <algorithm>
> #include <typeinfo>
>
> #include "boost/config.hpp"
> #include <boost/throw_exception.hpp>
>
> namespace boost
> {
> class any
> {
> public: // structors
>
> any()
> : content(0)
> {
> }
>
> template<typename ValueType>
> any(const ValueType & value)
> : content(new holder<ValueType>(value))
> {
> }
>
> any(const any & other)
> : content(other.content ? other.content->clone() : 0)
> {
> }
>
> ~any()
> {
> delete content;
> }
>
> public: // modifiers
>
> any & swap(any & rhs)
> {
> std::swap(content, rhs.content);
> return *this;
> }
>
> template<typename ValueType>
> any & operator=(const ValueType & rhs)
> {
> any(rhs).swap(*this);
> return *this;
> }
>
> any & operator=(const any & rhs)
> {
> any(rhs).swap(*this);
> return *this;
> }
>
> public: // queries
>
> bool empty() const
> {
> return !content;
> }
>
> const std::type_info & type() const
> {
> return content ? content->type() : typeid(void);
> }
>
> #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
> private: // types
> #else
> public: // types (public so any_cast can be non-friend)
> #endif
>
> class placeholder
> {
> public: // structors
>
> virtual ~placeholder()
> {
> }
>
> public: // queries
>
> virtual const std::type_info & type() const = 0;
>
> virtual placeholder * clone() const = 0;
>
> virtual void throwme() = 0;
> };
>
> template<typename ValueType>
> class holder : public placeholder
> {
> public: // structors
>
> holder(const ValueType & value)
> : held(value)
> {
> }
>
> public: // queries
>
> virtual const std::type_info & type() const
> {
> return typeid(ValueType);
> }
>
> virtual placeholder * clone() const
> {
> return new holder(held);
> }
>
> virtual void throwme()
> {
> ValueType* me = &held;
> throw me;
> }
>
> public: // representation
>
> ValueType held;
>
> };
>
> #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
>
> private: // representation
>
> template<typename ValueType>
> friend ValueType * any_cast(any *);
>
> #else
>
> public: // representation (public so any_cast can be non-friend)
>
> #endif
>
> placeholder * content;
>
> };
>
> class bad_any_cast : public std::bad_cast
> {
> public:
> virtual const char * what() const throw()
> {
> return "boost::bad_any_cast: "
> "failed conversion using boost::any_cast";
> }
> };
>
> template<typename ValueType>
> ValueType * any_cast(any * operand)
> {
> if (0 && operand && operand->type() == typeid(ValueType))
> {
> return &static_cast<any::holder<ValueType>
> *>(operand->content)->held;
> }
> else try
> {
> operand->content->throwme();
> }
> catch(ValueType* x)
> {
> return const_cast<ValueType*>(x);
> }
> catch(...) { }
> return 0;
> }
>
> template<typename ValueType>
> const ValueType * any_cast(const any * operand)
> {
> return any_cast<ValueType>(const_cast<any *>(operand));
> }
>
> template<typename ValueType>
> ValueType any_cast(const any & operand)
> {
> const ValueType * result = any_cast<ValueType>(&operand);
> if(!result)
> boost::throw_exception(bad_any_cast());
> return *result;
> }
>
> }
>
> // Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved.
> //
> // Permission to use, copy, modify, and distribute this software for
> any
> // purpose is hereby granted without fee, provided that this copyright
> and
> // permissions notice appear in all copies and derivatives.
> //
> // This software is provided "as is" without express or implied
> warranty.
>
> #endif
>
>
> --
> Dave Abrahams
> Boost Consulting
> www.boost-consulting.com
More information about the cxx-abi-dev
mailing list