xcrypt
xcrypt

Reputation: 3386

How to find out the type of a variable, with inheritance/polymorphism involved

How do I find out the type of a variable when inheritance is involved?

I'm having a little situation, I'll describe it in pseudocode:

class A
{
public:
A();
virtual ~A();

protected:
//some members
};

class B : public A
{
public:
B();
virtual ~B();

protected:
//some members
};

///////////////////////

int main()
{
A* pA = new B();
std::cout<<"type of pA: "<< ???;
}

How can I find out the type of pA? the result should be B. Also, what should I do if I want the result to be A?

Thanks.


EDIT:

I'll let you be the judge of whether it is bad design or not. If you think it is, then please tell me a better alternative.

Code:

class MyContactReport : public NxUserContactReport
{
    void OnContactNotify(NxContactPair& pair, NxU32 events)
    {
        if (pair.actors[0]->userData == NULL || pair.actors[1]->userData == NULL) return;

        LevelElement* otherObject = (LevelElement*)pair.actors[1]->userData;
        LevelElement* triggerObject = (LevelElement*)pair.actors[0]->userData;

        switch(events)
        {
        case NX_NOTIFY_ON_START_TOUCH:
            triggerObject->OnContactStartTouch(otherObject);
            break;
        case NX_NOTIFY_ON_END_TOUCH :
            triggerObject->OnContactEndTouch(otherObject);
            break;
        case NX_NOTIFY_ON_TOUCH:
            triggerObject->OnContactTouch(otherObject);
            break;
        }
    }
} *myReport;

pair.actors[1]->userData gives me access to the userData from an actor, an actor is something of the PhysX framework, that determines collisions and physics etc. The userdata is of type void*. This is also the only way to find out object the actor actually belongs to.

Then there's class LevelElement, an abstract class where every object in my level inherits from (level as in a game-level)

LevelElement has protected virtual methods: OnContactTouch(LevelElement* pOtherElement) etc... In those methods, I need to find out what type of LevelElement it is, to take certain specific measures.

Is this bad design? If yes, please help!

Upvotes: 0

Views: 1886

Answers (4)

Emil Styrke
Emil Styrke

Reputation: 1071

Use the typeid operator, as described for example here.

Basically:

#include <typeinfo>

[...]

std::cout << "typeid(*pA): " << typeid(*pA).name() << std::endl;
std::cout << "typeid(pA): " << typeid(pA).name() << std::endl;

Results, with g++ 4.4.5:

typeid(*pA): 1B
typeid(pA): P1A

I.e. there is some mangling involved at least using gcc. Check out this question for how to deal with this.


EDIT: As for your design question, instead of checking what type the otherObject is, a better solution is to just tell this object what to do. Assuming you want to code the onContactTouch for a hypothetical Bullet object; instead of

switch (type) {
case PLAYER:
    (Player*)otherObject->dealDamage(10);
    break;
case BULLETPROOF_GLASS:
    (BulletproofGlass*)otherObject->ricochet();
    break;
}

do this:

otherObject->onHitByBullet(this);

This is sometimes called the tell, don't ask principle.

Upvotes: 3

Tim
Tim

Reputation: 1011

This is not strictly a symptom of bad design. Although you certainly could abstract the types out of the type checking. For the sake of performance and runtime customization, you should almost definitely NOT use RTTI or other direct type checking systems. Implementing your own (much simplified) type checking dynamic allows objects to define collision behavior at an object level rather than a class level and to redefine collision behavior at runtime.

//PSEUDO CODE
enum CollisionTypes = {HARD_THING, SOFT_THING, EXPLODING_THING};

class FragileThing is a GameObject
{
    public function getCollisionType()
    {
        return SOFT_THING;
    }
    public function collideWith(GameObject obj)
    {
        if (obj.getCollisionType() == SOFT_THING)
            print "whew...";
        else
            print "OUCH!";
    }
}

class CollisionDispatcher is a PhysXCollisionListener
{
    public function getCollisionFromPhysX(Collision col)
    {
        col.left.collideWith(col.right);
        col.right.collideWith(col.left);
    }
}

Another system that I have seen used (in books) is to use an engine wide messaging framework to dispatch collisions with type information embedded into the message. Unrelated types can simply ignore the messages. I have not tried this though. I would also recommend examining your LevelObject class to determine if there isn't some kind of common functionality you can add to it that would sidestep this problem. For instance, if there are very few fundamental types of objects (Red and Green perhaps, or Ethereal, Soft, Hard, Explosive) then you can, instead of conditional statements, encode those types into function calls: function collideWithSomethingHard(LevelObject obj) thus allowing objects to define only the collision behavior they care about. Hope this helps a little.

Upvotes: 1

Software_Designer
Software_Designer

Reputation: 8587

When you use the function typeid(pA).name() . The result is class A *.

// typeid
#include <iostream>
#include <typeinfo>
using namespace std;

class A
{
    public:
        A(){}
        virtual ~A(){}

    protected:

};

class B : public A
{
    public:
        B(){}
        virtual ~B(){}

    protected:

};


int main()
{
A* pA = new B();

   cout<<"The expression [A* pA = new B()]: \n";
   cout<<"Has datatype --> "<< typeid(pA).name()  <<"<-- \n";
   cout<<" \n";

   return 0;
}

Output :

The expression [A* pA = new B()]:
Has datatype --> class A *<--

Press any key to continue

Upvotes: 0

Mark B
Mark B

Reputation: 96301

If you need to do that, then most likely your interface is lacking or designed sub-optimally. Instead revisit your interface and provide an appropriate set of (possibly abstract) virtual methods to implement your required interface. Then you won't need to worry about specific subypes.

If you really need the information you can use typeid as seen in the answer from @Emil Styrke

Upvotes: 0

Related Questions