Actions

icon Post
text/html Subscribe
text/html Unsubscribe

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [cxx-abi-dev] trivial __dynamic_cast fails?


  • To: David Abrahams <dave@xxxxxxxxxxxxxxxxxxxx>
  • Subject: Re: [cxx-abi-dev] trivial __dynamic_cast fails?
  • From: David Baraff <deb@xxxxxxxxx>
  • Date: Fri, 27 Feb 2004 21:03:44 -0800

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