15

I'm currently working on integrating a third-party package that uses lots of RTTI stuff on a non-RTTI platform (Android). Basically, I did my own RTTI implementation but I'm stuck on a problem.

The issue is that a lot of classes are having the diamond inheritance problem since all the classes derive from the same base class (object).. and so, if I want to downcast from the base class to the derived class, I have to use a dynamic_cast - but RTTI is not available! How do I convert an object from parent to child when there are virtual inheritance without dynamic_cast?

It looks like that:

class A 
{
public:
 virtual char* func() { return "A"; };
};
class B : public virtual A
{
public:
 //virtual char* func() { return "B"; };
};
class C : public virtual A 
{
public:
 //virtual char* func() { return "C"; };
};

class D : public B, public C 
{
public:
 //virtual char* func() { return "D"; };
};

D d;
A* pa = static_cast<A*>(&d);
D* pd = static_cast<D*>(pa); // can't do that! dynamic_cast does work though...

Those are my errors:

error C2635: cannot convert a 'A*' to a 'D*'; conversion from a virtual base class is implied

error C2440: 'initializing' : cannot convert from 'test_convert::A *' to 'test_convert::D *' Cast from base to derived requires dynamic_cast or static_cast

Any ideas?

log0
  • 10,489
  • 4
  • 28
  • 62
Adam
  • 151
  • 1
  • 1
  • 3

6 Answers6

13

You can only do this cast with dynamic_cast; no other cast will do this.

If you can't design your interfaces so that you don't need to perform this type of cast then the only thing you can do is make the casting functionality part of your class hierarchy.

E.g. (horribly hacky)

class D;

class A
{
public:
    virtual D* GetDPtr() { return 0; }
};

class B : public virtual A
{
};

class C : public virtual A 
{
};

class D : public B, public C 
{
public:
    virtual D* GetDPtr() { return this; }
};
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 3
    I don't know whether to minus or add for this answer. – Edward Strange Jul 27 '10 at 21:09
  • 2
    @Noah: In my defence, I did say "horribly hacky"! – CB Bailey Jul 27 '10 at 21:11
  • 2
    @Noah: the hacky version looks better than a cast matrix I have seen on one project long time ago. on rtti-less system people found nothing better than to give every class an unique id and use the id as an index into a two dimension matrix. matrix[cast_what][cast_to] was an id of the class, the result of dynamic_cast. – Dummy00001 Jul 27 '10 at 21:47
5

Android does support RTTI. You need latest NDK (at least r5, latest is r6) and you need to compile against the GNU stdlibc++ instead of the default.

Even before, there was the CrystaX's rebuild which did support exceptions and rtti (we had to use that until official NDK r5c because r5a and r5b had the support, but crashed on older (pre-2.3) systems).

PS: Somebody should really forbid vendors say they support C++ when they don't support exceptions and rtti, because most of standard library, and that's part of the C++ standard, does not work without either. Plus not supporting them is silly, especially for the exceptions, because code with exceptions is more efficient than one without (provided they are properly used to signal exceptional cases).

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
3

In most cases the visitor-pattern can be used to avoid downcasts. It can be used to avoid dynamic_cast, too.

Some caveats:

1) It must be possible to change the offending classes.
2) You may need to know EVERY derived class.
3) The objects must be known to derive from at least the baseclass, you cannot try to cast completely unrelated types. (This seems to be fulfilled: "I want to downcast from the base class to the derived class")

In the following example i used templates. These can be easily get rid off, but would require quite some writing effort.

class A;
class B;
class C;
class D;

// completely abstract Visitor-baseclass.
// each visit-method must return whether it handled the object
class Visitor
{ 
public:
    virtual bool visit(A&) = 0;
    virtual bool visit(B&) = 0;
    virtual bool visit(C&) = 0;
    virtual bool visit(D&) = 0;
};

class A
{
public:
    virtual const char* func() { return "A"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class B : public virtual A
{
public:
    virtual const char* func() { return "B"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class C : public virtual A
{
public:
    virtual const char* func() { return "C"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class D : public B, public C
{
public:
    virtual const char* func() { return "D"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};

// implementation-superclass for visitors: 
// each visit-method is implemented and calls the visit-method with the parent-type(s)
class InheritanceVisitor : public Visitor
{ 
    virtual bool visit(A& a) { return false; }
    virtual bool visit(B& b) { return visit(static_cast<A&>(b)); }
    virtual bool visit(C& c) { return visit(static_cast<A&>(c)); }
    virtual bool visit(D& d) { return visit(static_cast<B&>(d)) || visit(static_cast<C&>(d)); }
};

template<typename T> // T must derive from A
class DerivedCastVisitor : public InheritanceVisitor
{
public:
    DerivedCastVisitor(T*& casted) : m_casted(casted) {}
    virtual bool visit(T& t) 
    { m_casted = &t; return true; }
private:
    T*& m_casted;
};

// If obj is derived from type T, then obj is casted to T* and returned. 
// Else NULL is returned.
template<typename T> 
T* derived_cast(A* obj)
{
  T* t = NULL;
  if (obj) 
  {
    DerivedCastVisitor<T> visitor(t);
    obj->accept(visitor);
  }
  return t;
}

int main(int argc, char** argv)
{
  std::auto_ptr<A> a(new A);
  std::auto_ptr<A> b(new B);
  std::auto_ptr<A> c(new C);
  std::auto_ptr<A> d(new D);

  assert(derived_cast<A>(a.get()) != NULL); // a has exact type A
  assert(derived_cast<B>(b.get()) != NULL); // b has exact type B
  assert(derived_cast<A>(b.get()) != NULL); // b is derived of A
  assert(derived_cast<C>(b.get()) == NULL); // b is not derived of C
  assert(derived_cast<D>(d.get()) != NULL); // d has exact type D
  assert(derived_cast<B>(d.get()) != NULL); // d is derived of B 
  assert(derived_cast<C>(d.get()) != NULL); // d is derived of C, too
  assert(derived_cast<D>(c.get()) == NULL); // c is not derived of D

  return 0;
}
Markus Kull
  • 1,471
  • 13
  • 16
1

the code:

template <typename E, typename T>
E& force_exact(const T& ref)
 {
   static const E* exact_obj;
   static const T& exact_obj_ref = *exact_obj;
   static const ptrdiff_t exact_offset = ...

doesn't work very well for me as static const E* exact_obj is zero, so static const T& exact_obj_ref = *exact_obj derefs zero, too, and thus static const ptrdiff_t exact_offset becomes also zero.

It seems to me that the derived class needs to be instantiated (which may be a problem for abstract classes...). So my code is:

template <typename D, typename B>
D & Cast2Derived(B & b)
{ static D d;
  static D * pD = & d;
  static B * pB = pD;
  static ptrdiff_t off = (char *) pB - (char *) pD;

  return * (D *) ((char *) & b - off);
} 

Tested under MSVC 2008, WinXP 32b.

Any comments / better solution(s) are welcome.

LuP

LuP
  • 69
  • 3
  • This is problematic if D's constructor incurs side-effects. Perhaps the better solution is to reinterpret_cast an appropriately sized and aligned char buffer to the D pointer. Also, to keep with C++ casting name convensions, how about naming it `virtual_cast(B*)`? I haven't yet given it deep thought, but are there some inheritance hierarchies that this will fail for? Perhaps a class that has inherits from the same virtual class multiple times (even if indirectly) or perhaps with a combination of non-virtual inheritance to boot. Anyhow +1 – Thomas Eding Dec 26 '12 at 04:59
  • Hi Thomas, could you please describe better your idea about reinterpret casting char buffer to D*? – Juster Dec 04 '15 at 16:13
0

The problem with virtual inheritance is that the base class address is not necessarily the same as the derived address. Thus, even reinterpret_cast or void* casts will not help.

One way to solve this without using dynamic_cast is to compute the offset between both pointer type (the exact type and ref type) in order to modify the object address accordingly during the cast.

 template <typename E, typename T>
 E& force_exact(const T& ref)
 {
   static const E* exact_obj;
   static const T& exact_obj_ref = *exact_obj;
   static const ptrdiff_t exact_offset =
     (const char*)(void*)(&exact_obj_ref)
     - (const char*)(void*)(exact_obj);
   return *(E*)((char*)(&ref) - exact_offset);
 }
log0
  • 10,489
  • 4
  • 28
  • 62
  • Side note: The declaration of the offset is made static because it has to be computed only once for each couple T,E in theory. However in practice, using `static` has a a runtime cost (rewrited as `if (!var_initilized) var_ = ...`). One way to get out of this is to instantiate a template class that statically store the offset value for each couple T,E but that's another story... – log0 May 12 '11 at 22:28
-2

As long as you have another way to make sure what you're doing is type safe at runtime, just use reinterpret_cast.

It's basically the same thing as a C style cast so only use it if you have to, but it will allow the code above to compile.

Matt Edlefsen
  • 37
  • 1
  • 2
  • 1
    Well, I've tried but I got a warning message (4946:reinterpret_cast used between related classes: 'class1' and 'class2'). Note that the warning is by default disabled. I believe the reinterpret_cast just reinterprets the pointer (as it says) but it doesn't cast it and it causes issues when there are virtual inheritances or multiple parents since casting to one parent or another may modify the pointer's value. As such, the reinterpret_cast will not work in my case where the third-party I use makes extensive uses of those concepts (multiple parents, virutal inheritance, etc). – Adam Jul 28 '10 at 14:21