views:

123

answers:

4

I am trying to refactor some code while leaving existing functionality in tact. I'm having trouble casting a pointer to an object into a base interface and then getting the derived class out later. The program uses a factory object to create instances of these objects in certain cases.

Here are some examples of the classes I'm working with.

// This is the one I'm working with now that is causing all the trouble.
// Some, but not all methods in NewAbstract and OldAbstract overlap, so I
// used virtual inheritance.
class MyObject : virtual public NewAbstract, virtual public OldAbstract { ... }

// This is what it looked like before
class MyObject : public OldAbstract { ... }

// This is an example of most other classes that use the base interface
class NormalObject : public ISerializable

// The two abstract classes. They inherit from the same object.
class NewAbstract : public ISerializable { ... }
class OldAbstract : public ISerializable { ... }

// A factory object used to create instances of ISerializable objects.
template<class T> class Factory
{
public:
    ...
    virtual ISerializable* createObject() const
    {
        return static_cast<ISerializable*>(new T()); // current factory code
    }
    ...
}

This question has good information on what the different types of casting do, but it's not helping me figure out this situation. Using static_cast and regular casting give me error C2594: 'static_cast': ambiguous conversions from 'MyObject *' to 'ISerializable *'. Using dynamic_cast causes createObject() to return NULL. The NormalObject style classes and the old version of MyObject work with the existing static_cast in the factory.

Is there a way to make this cast work? It seems like it should be possible.

A: 

Look up "dreaded diamond" and virtual inheritance. They may help you.

Noah Roberts
I would have given this a +1 if it included a link.
Mark Ransom
Answers here shouldn't all be about giving an exact answer for a specific question, but about giving the poster a direction to research the answer so that the then learn more and can answer it on their own next time. Why provide A link when there are so many that could be useful? Maybe something like this: http://tinyurl.com/2cermbg ? Seems kind of pointlessly rude and superfluous.
Noah Roberts
It's too bad you can't directly post lmtgfy links here. ;)
dash-tom-bang
-1 Having totally glossed over this answer as being unhelpful, I'd like to point out that I had already heard of the diamond and attempted virtual inheritance. You are right that this was the source of my problem, but I could not find any good information to help me solve my issue. Simon's answer with Mark's link helped me understand what I was doing wrong.
Jay Sheridan
@Jay - you might have said something about that in your OP. Since you did not I gave a very helpful answer that tells you exactly what terms you're looking for to learn what you need to solve the issue. As it is, I'll keep your attitude in mind next time I'm tempted to answer one of your questions.
Noah Roberts
@Sudit - IMHO comments are not here so that you can exercise your pointless vendetta. This constant tracking me down just to bitch is a sign of a serious mental disorder. If you don't stop taking things so damn seriously you're going to grow old before you reach 15.
Noah Roberts
A: 

Don't inherit virtually from both NewAbstract and OldAbstract. Pick one to inherit virtually from. I think that may take care of it.

Brent Arias
Don't virtually inherit from either of them - they should virtually inherit from the common base class (`ISerialiazable`).
Mike Seymour
+7  A: 

You have to virtually inherit from ISerializable (I just tested it with VS2010). This is a common issue called the Diamond Problem, where the compiler does not know wich hierarchy path to take.

EDIT:

This should do it:

class NewAbstract : public virtual ISerializable { ... } 
class OldAbstract : public virtual ISerializable { ... } 
Simon
+1: This worked on gcc too.
Troubadour
Exactly the solution the FAQ recommends: http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.8
Mark Ransom
This solved it. I didn't realize the abstracts were the ones that needed to virtually inherit. @Mark Ransom, thanks for the great link. That whole page is very helpful.
Jay Sheridan
+1  A: 

You can get round it by casting to one of your immediate bases first eg.

virtual ISerializable* createObject() const
{
    NewAbstract*const na = dynamic_cast< NewAbstract* >( new T() );
    return dynamic_cast< ISerializable* >( na );
}
Troubadour
It sounds like this could work. Though from other things I've read here, it sounds like you have to be really careful with this, depending on the bases. In any case, for this particular issue, NewAbstract is not something the factory object should know about.
Jay Sheridan
@Jay: Yeah, when I saw @Simon's answer I realised that that was the correct thing to do. I missed the fact the virtual inheritance was in the wrong place. I don't like deleting an answer even if it's rubbish so I've left it here to languish... ;)
Troubadour