RTTI draft proposal

Jason Merrill jason at cygnus.com
Thu Aug 19 22:31:49 UTC 1999


>>>>> Daveed Vandevoorde <daveed at edg.com> writes:

 > the typeid, throw or catch.  Basic type information (such as for "int",
 > "bool", etc.) could be kept in the run-time support library but the benefits
 > of that may be limited.

g++ currently does this.

 > Assuming that after linking and loading only one type_info structure is
 > active for any particular type symbol

We've found that we can't always rely on that, when people build shared
libraries with -B symbolic.  So we fall back on strcmp of the mangled name.

 > std::extended_type_info derives from std::type_info but adds no fields
 > to the latter; it is introduced solely to follow the suggestion of the
 > C++ standard.

That seems wrong; the idea of extended_type_info was supposed to extend the
type_info API so that the user could access more information about
classes.  Stuff like member lists and whatnot.  If we aren't doing anything
like that, we shouldn't introduce extended_type_info.

 > 6. std::__qualifier_type_info is similar to std::__pointer_type_info but
 > describes top level qualifiers as in "int const" and "char *const".

Where would this be used?  I included it in the G++ implementation, but
have since come to the conclusion that it was a mistake.

 > 7. std::__class_type_info introduces a variable length structure.
 > The variable part that follows consists of a sequence of base class
 > descriptions having the following structure:
 >     struct std::__base_class_info {
 >        std::type_info *type; /* Null if unused. */
 >        std::ptrdiff_t offset;
 >        std::__base_class_index next; /* Hash table link. */
 >        int is_direct: 1;
 >        int is_floating: 1; /* I.e., virtual or base of virtual subobject. */
 >        int is_virtual: 1; /* Implies is_floating. */
 >        int is_shared: 1; /* Implies is_floating and the virtual subobject
 >                             appears on multiple derivation paths. */
 >        int is_accessible: 1;
 >        int is_ambiguous: 1;
 >     };

So you're proposing that we store the info about all bases in the type_info
for a class, not just the info for direct bases?  That would certainly make
upcasts (i.e. EH matching and cross dynamic_casts) faster, at the price of
larger RTTI info.

 >  The fixed length introduction adds the following fields to std::type_info:
 >   . a word with flags describing details about the class such as whether
 >     it is a class/struct/union and whether it is polymorphic.

Why?

 >   . a hashvalue that can be used for quick lookups in a variable length
 >     structure describing base classes.

How is this calculated/used?

 >   . the number of base class descriptions that follow it (a power of two).

Why a power of two?

 > The dynamic_cast algorithm
 > --------------------------
 >   . If, in the most derived object pointed (referred) to by v, v points
 >     (refers) to a public base class sub-object of a T object [note: this can
 >     be checked at compile time], and if only one object of type T is derived
 >     from the sub-object pointed (referred) to by v, the result is a pointer
 >     (an lvalue referring) to that T object.

More precisely, we can check at compile time whether T has a unique base
subobject of type typeof(*v).  We don't know anything about the true
referent of v.

 >   . Otherwise, if v points (refers) to a public base class sub-object of the
 >     most derived object, and the type of the most derived object has an
 >     unambiguous public base class of type T, the result is a pointer (an
 >     lvalue referring) to the T sub-object of the most derived object. 
 >   . Otherwise, the run-time check fails.

 > The first check corresponds to a "base-to-derived cast" and the second to a
 > "cross cast".  These tests are implemented by std::__dynamic_cast.

 >    void* std::__dynamic_cast(void *sub, std::__class_type_info *src,
 >                                         std::__class_type_info *dst,
 >                                         std::ptrdiff_t src2dst_offset) {
 >      // Pick up vtable pointer from given object:
 >      void *vptr = *(void**)sub;
 >      if (src2dst_offset>=0 && NO_VBASE(sub, vptr)) {
 >        // If the type of "v" was not an accessible nonvirtual base type of
 >        // "T", src2dst_offset should have been set to -1 if it was an
 >        // accessible but floating base of "T" and to -2 if it was not at all
 >        // an accessible base of "T".
 >        // In addition, the vtable should contain an entry to indicate that
 >        // the complete object has no virtual bases (e.g., a count of the
 >        // vbase locator entries).
 >        return (char*)sub+src2dst_offset;

What about the case where v doesn't point to a base subobject of T at all,
even though the compile time check says it could?  Consider

  struct V;
  struct T: public V;
  struct A: public V;
  struct B: public A, public T;

  B b;
  V* v = (V*)(A*)&b;
  dynamic_cast<T*>(v);

The static check says this could be a downcast, but it's really a cross-cast.

Who passes in src2dst_offset?
In a typical downcast, the offset from src to dst will be negative.

 >      } else {
 >        // Slower case
 >        void *result = 0;
 >        if (src2dst_offset==-1) {
 >          // Possibly a "floating" base-to-derived cast:
 >          result = floating_base2derived(sub, src, dst);
 >        }
 >        if (result==0) {
 >          // The base-to-derived case did not succeed, so we should attempt
 >          // the cross-cast (which is really a derived-to-base cast from the
 >          // complete object):
 >          result = derived2base(complete, dst);
 >        }
 >      }
 >      return result;
 >    }

Where are the defns of floating_base2derived and derived2base?

 > The exception handler matching algorithm
 > ----------------------------------------

 >    bool __eh_match(std::type_info *thrown_type,
 >                    std::type_info *handled_type) {
 >      if (thrown_type == handled_type) {
 >        return true;
 >      } else if (IS_REFTYPE(handled_type)) {
 >        std::type_info *caught_type = REMOVE_REFTYPE(handled_type);
 >        if (IS_CVQUAL(caught_type)) {
 >          caught_type = REMOVE_CVQUAL(caught_type);
 >        }
 >        return thrown_type==caught_type;

We do derived-to-base conversions for non-pointers, too.

3 A  handler  is a match for a throw-expression with an object of type E
  if

  --The handler is of type cv T or cv T& and E and T are the  same  type
    (ignoring the top-level cv-qualifiers), or

  --the  handler is of type cv T or cv T& and T is an unambiguous public
    base class of E, or

  --the handler is of type cv1 T* cv2 and E is a pointer type  that  can
    be converted to the type of the handler by either or both of

    --a  standard  pointer conversion (_conv.ptr_) not involving conver-
      sions to pointers to private or protected or ambiguous classes

    --a qualification conversion

FWIW, g++ turns all dynamic_casts into upcasts, using the original pointer
for disambiguation.  I'm not yet convinced that there's a good way to
accelerate downcasts.

Jason




More information about the cxx-abi-dev mailing list