views:

118

answers:

6

Hey,

I'm working on a small roguelike game, and for any object/"thing" that is not a part of the map is based off an XEntity class. There are several classes that depend on it, such as XPlayer, XItem, and XMonster.

My problem is, that I want to convert a pointer from XEntity to XItem when I know that an object is in item. The sample code I am using to pick up an item is this, it is when a different entity picks up an item it is standing over.

void XEntity::PickupItem()
{
    XEntity *Ent = MapList; // Start of a linked list

    while(true)
    {
        if(Ent == NULL) { break; }

        if(Ent->Flags & ENT_ITEM)
        {
            Ent->RemoveEntity(); // Unlink from the map's linked list

            XItem *Item = Ent // Problem is here, type-safety

            // Code to link into inventory is here

            break;
        }

        Ent = Ent->MapList;
    }
}

My first thought was to create a method in XEntity that returns itself as an XItem pointer, but it creates circular dependencies that are unresolvable.

I'm pretty stumped about this one. Any help is greatly appreciated.

+1  A: 
XItem * Item = dynamic_cast< XItem * >( Ent );

if ( Item )
    // do something with item

In order for that to work, you need to enable RTTI. Look here for more information.

Space_C0wb0y
Then check that Item isn't zero before you use it. Just in case ;-)
Paul Arnold
+1  A: 

Just cast it:

XItem* Item = (XItem*)Ent;

A better approach, overall, is this:

if (XItem *Item = dynamic_cast<XItem*>(Ent)) {
    Ent->RemoveEntity();

    // Code to link into inventory is here

    break;
}
Marcelo Cantos
-1 because the C++ casting operators should be used.
Space_C0wb0y
+1 because you answered the question asked *and* indicated a better way of doing this. C-style casting operators may be "Evil", but knowledge of them should not be banned.
John Dibling
Did not downvote but still should not lead with an answer that is generally discouraged
Harald Scheirich
+6  A: 

If you know that the XEntity is actuall and XItem then you can use a static cast.

XItem* Item = static_cast<XItem *>(Ent);

However, you should review you design and see if you can operate on the entity in a way that means that you don't need to know what derived type it is. If you can give the base class a sufficiently rich interface you may be able eliminate the flag check type inspection.

Charles Bailey
The current way of doing it is for a simiple prototype. Currently the design is pretty messy, and needs to be re-written in a cleaner way.
Shawn B
+3  A: 

Casting solves the problem as others have pointed out:

// dynamic_cast validates that the cast is possible. It requires RTTI 
// (runtime type identification) to work. It will return NULL if the 
// cast is not possible.
XItem* Item = dynamic_cast<XItem*>(Ent);
if(Item)
{
    // Do whatever you want with the Item.
}
else
{
    // Possibly error handling code as Ent is not an Item.
}

However I think that you sould step back and look at the design of the program, as downcasting is something that should and can be avoided by a proper object-oriented design. One powerful, even though a bit complex, tool might be the Visitor pattern.

Anders Abel
Unfortunately the visitor pattern has drawbacks of its own... like the necessary enumeration of the types :/
Matthieu M.
A: 

I used to believe that downcasting was always possible to avoid with a "proper" design. This just isn't the case though. A proper design very often needs to have sub-objects that implement new, and not just different behavior. Too often advocates of "proper" design will tell you to move the new behavior up the stack of abstraction to places where it doesn't belong. Not always, but if you keep trying to make sure all your classes can be used from the most abstract point this is very often where things end up going and it's just fugly.

One great way to deal with downcasting in a centralized manner is by using the Visitor pattern. There are several forms of visitor though, some require downcasting, some do not. The acyclic visitor, the one that does require downcasting, is easier to work with and is, in my experience, more powerful.

Another visitor that I've not attempted to work with claims to meet the same flexibility of acyclic visitor with the speed of the standard visitor; it's called "cooperative visitor". It still casts, it just does so in a quicker manner with it's own lookup table. The reason I have not tried the cooperative visitor is that I've not found a way to make it work on multiple higherarchies...but I've not spent a lot of time on it either because I've stuck myself (in my current project) with acyclic.

The real cool thing about the cooperative visitor is return types. However, I use my visitors to visit entire blocks of objects and do things with them. I have trouble envisioning how a return would work in these cases.

The standard visitor downcasts also it just does so through the virtual call mechanism, which is faster and sometimes safer than an explicit cast. The thing I don't like about this visitor is that if you need to visit WidgetX in the Widget higherarchy then you also have to implement visit() functionality for WidgetY and WidgetZ even though you don't care about them. With large and/or wide higherarchies this can be a PITA. Other options do not require this.

There is also a "higherarchal visitor". It knows when to quit.

If you're not inclined to use a visitor though and wish to just cast then you might consider using the boost::polymorphic_downcast function. It has the safety and warning mechanisms of dynamic cast with asserts in debug builds, and the speed of static cast in a release. It may not be necessary though. Sometimes you just know that you're casting right.

The important thing that you need to think about and what you want to avoid, is breaking the LSP. If you have a whole lot of code with "if (widget->type() == type1) { downcast...} else if (widget->type() == type2)..." then adding new widget types is a big issue that affects a lot of code in a bad way. Your new widget won't really be a widget because all your clients are too intimate with your higherarchy and don't know about it. The visitor pattern does not get rid of this issue but it does centralize, which is very important when you've got a bad smell, and it often makes it simpler to deal with on top of it.

Noah Roberts
A: 

As have been answered, there are 2 operators:

XItem* Item = static_cast<XItem*>(Ent);

And:

XItem* Item = dynamic_cast<XItem*>(Ent);

the second is slower but safer (it checks if it's possible) and might return null even if Ent is not.

I tend to use both wrapped in a method:

template <class T, class U>
T* my_cast(U* item)
{
#ifdef _NDEBUG_
  if (item) return &dynamic_cast<T&>(*item); // throw std::bad_cast
  else return 0;
#else
  return static_cast<T*>(item);
#endif
}

This way I get type checking while development (with an exception if something goes bad) and I get speed when I'm finished. You can use other strategies if you wish, but I must admit I quite like this way :)

Matthieu M.