Casting of derived class to one of the bases through base pointer

193 views Asked by At

EDIT: Ok as I see now this changes the case a lot so the more precise scenario is as such:

The hierarchy I currently have is somewhat similar to this:

class IBase() { virtual void Foo() = 0; };

class Base() : public IBase { virtual void Foo() { } };

class IDerived() { virtual void Bar() = 0; };

template<typename TT, typename TB, typename... TI>
class Derived : public Base, public IDerived { virtual void Bar() {}};

template<typename TT, typename TB, typename... TI>
IBase* CreateDerived() { return new Derived(); }

IBase* derived = CreateDerived<some types...>();

I am getting error with Visual Studio while trying to cast an object and call a function:

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call.  

All calls of IBase interface through derived work fine but when I try to cast derived to IDerived I get the error calling any function:

IDerived* d = (IDerived*)derived;
d->Bar(); <- boom error ;) 

I guess that such cast is illegal but how can I cast the pointer so that I can get access to IDerived interface methods (preferably without dynamic_cast I would prefer a good and portable hack if one exists ;))? Is it possible somehow to calculate offset to the pointer so that the proper vtable is used and everything works as it should ?

I am programming for quite a long time but I always dodged all those fancy-shmancy over engineered systems with tons of interfaces and templates and now I am doomed to make one myself.

EDIT: Now as you can see this gets tricky and difficult. I don't know the exact type of the Derived I am getting as it is templated, also the function CreateDerived is templated and returns interface.

Also, one of the requirements is not to use dynamic_cast (RTTI is disabled in the project)

1

There are 1 answers

4
Simple On BEST ANSWER

You are performing a cross-cast; IBase and IDerived are unrelated. You need to cast to Derived first and then to IDerived. If you used a static_cast rather than a C cast the compiler would have caught this for you at compile-time.

I assume you know that the IBase is really a Derived because you used a C cast, but if you don't then you can also use dynamic_cast to perform cross-casts safely.

EDIT: If you can't use RTTI and you don't know the dynamic type of the object then virtual inheritance and dynamic_cast go out of the window. When you call CreateDerived it looks like you do know the dynamic type of the object (because of its template arguments) so you could have a std::map<IBase*, IDerived*> and then after the CreateDerived<TT, TB, TI...>() call you can static_cast the IBase* to Derived<TT, TB, TI...>* and then insert the pointer as both the key and value into the map.

Just enable RTTI; this is getting complicated. >.<

EDIT 2: Alternatively, you seem to know that the object pointed at by the IBase* does also derive from IDerived*. If you can modify the class hierarchy you can have an abstract base class that derives from both IBase and IDerived and then have Derived derive from this new base class. Then you can static_cast from an IBase* to the new class in the hierarchy and then to IDerived*.

It would look something like this:

class IBase { virtual void Foo() = 0; };

class Base : public IBase { virtual void Foo() { } };

class IDerived { virtual void Bar() = 0; };

class BaseAndDerived : public Base, public IDerived { };

template<typename TT, typename TB, typename... TI>
class Derived : public BaseAndDerived { virtual void Bar() {}};