views:

178

answers:

3

Hey there everyone,

I am quite new to C++, but have worked with C# for years, however it is not helping me here! :)

My problem: I have an Actor class which Ball and Peg both derive from on an objective-c iphone game I am working on. As I am testing for collision, I wish to set an instance of Ball and Peg appropriately depending on the actual runtime type of actorA or actorB. My code that tests this as follows:

// Actors that collided
        Actor *actorA = (Actor*) bodyA->GetUserData();
        Actor *actorB = (Actor*) bodyB->GetUserData();

        Ball* ball;
        Peg* peg;
        if (static_cast<Ball*> (actorA)) { // true
            ball = static_cast<Ball*> (actorA);
        }
        else if (static_cast<Ball*> (actorB)) {
            ball = static_cast<Ball*> (actorB);
        }
        if (static_cast<Peg*> (actorA)) { // also true?!
            peg = static_cast<Peg*> (actorA);
        }
        else if (static_cast<Peg*> (actorB)) {
            peg = static_cast<Peg*> (actorB);
        }
        if (peg != NULL) {
            [peg hitByBall];
        }

Once ball and peg are set, I then proceed to run the hitByBall method (objective c).

Where my problem really lies is in the casting procedurel Ball casts fine from actorA; the first if (static_cast<>) statement steps in and sets the ball pointer appropriately.

The second step is to assign the appropriate type to peg. I know peg should be a Peg type and I previously know it will be actorB, however at runtime, detecting the types, I was surprised to find actually the third if (static_cast<>) statement stepped in and set this, this if statement was to check if actorA was a Peg, which we already know actorA is a Ball! Why would it have stepped here and not in the fourth if statement?

The only thing I can assume is how casting works differently from c# and that is it finds that actorA which is actually of type Ball derives from Actor and then it found when static_cast<Peg*> (actorA) is performed it found Peg derives from Actor too, so this is a valid test? This could all come down to how I have misunderstood the use of static_cast. How can I achieve what I need? :)

I'm really uneasy about what feels to me like a long winded brute-casting attempt here with a ton of ridiculous if statements. I'm sure there is a more elegant way to achieve a simple cast to Peg and cast to Ball dependent on actual type held in actorA and actorB.

Hope someone out there can help! :) Thanks a lot.

+2  A: 

To identify object type at run time you should dynamic_cast and not static_cast. But you should really reconsider your design. Probably (I don't anything about objective-c) you should make hitByBall a virtual method in base class and override the implementation if required in derived classes. Then you can call the method without any casts.

Naveen
error: cannot dynamic_cast 'actorA' (of type 'struct Actor*') to type 'struct Ball*' (source type is not polymorphic)
GONeale
can you post the declaration of struct A and struct Ball?
Naveen
"Do not use dynamic_cast except in test code. If you need to know type information at runtime in this way outside of a unittest, you probably have a design flaw." - Google , in support of your suggestion to reconsider design
Marlon
Thanks for your comments guys, I've reevaluated my approach and going with a polymorphic approach as Johnsyweb suggested. However this still leaves me with a gripe against static_cast<T>. Why was it returning true for both `Ball` and `Peg` when it was a `Ball` type?
GONeale
`static_cast` was not returning `true`, it was returning the pointer to either `actorA` or `actorB`. Unlike `dynamic_cast`, with RTTI, no runtime checks are performed.
Johnsyweb
+3  A: 

Since this is Objective-C code (not C++, as per the title), why not just call:

[actorA hitByBall];
[actorB hitByBall];

Updated: If the object you are sending the message to is nil it will be ignored. If the object you send the message to does not implement hitByBall, you'll get an exception, "selector not recognized", unless you have put an empty definition in your base class (Actor).

You can then remove your ball and peg declarations and all of the static_casts (which would have been incorrect even in C++).

Johnsyweb
Thanks Johnsy. After re-thinking my implemention as many have suggested I agree with you, after a bit more research and realising all objective c methods are effectively 'virtual' then I can simply not implement a base method and yes as you say, just implement a hitByBall for Peg and other classes will ignore. Thanks.
GONeale
This is not true in the general case. If an object does not implement a method, you will get a selector not recognized exception. What you are probably both talking about is overriding the base class implementation.
JeremyP
Thanks @JeremyP: I have clarified my answer accordingly.
Johnsyweb
+1  A: 

It seems you are looking for -isKindOfClass. If both objects conform to the NSObject protocol, you only need to check which is of which type - the actual message passing is not limited by the static type of the pointer:

if (   [actorA isKindOfClass:[Peg class ]] 
    && [actorB isKindOfClass:[Ball class]]) 
{
    [actorA hitByBall];
}
else if (   [actorB isKindOfClass:[Peg class ]]
         && [actorA isKindOfClass:[Ball class]]) 
{
    [actorB hitByBall];
}
Georg Fritzsche
Well both inherit NSObject at their root level, so good point. I did consider this, but I think after persisting with a c++ type test for some reason I never came back to it.. Thanks Georg.
GONeale