Andrew
Andrew

Reputation: 24866

What is a good way to store object type in C++?

Imagine I have such class hierarchy:

Base
A : Base
B : Base
C : B

I want to be able to retrive a string type from Base object (I need string, not enum). I want also to be able to compare object type to A type for example:

Object *object = new A();
if (object->type() == A::typename())
{
   //hooray!
}

For now I'm planing to add a static function to each class:

static string typename() {return "Different name for each class";}

and then I will have to reimplement Base function virtual string type() for every derived class:

A: virtual string type() {return typename();} //A::typename
B: virtual string type() {return typename();} //B::typename
...

I think such design looks ugly. Is there some better way to achieve my goal?

Why I need this: I'm developing a game. There is a tile map. Each tile has an array of objects on it. Some objects can be placed over the others. So i want to check if it is allow to put the object at the specific tile. For example: if tile has object with type "pot" then the flower can be put there.

Upvotes: 0

Views: 143

Answers (2)

user1142580
user1142580

Reputation: 163

I was searching for a comfortable way of doing this for days. Here's how I did it finally. The solution is pragmatic, compiles fast and is portable and works without RTTI. However it uses #define, which C++ folks try to avoid often. Maybe someone can turn that code into one that uses templates, which I would also be interested in.

Basically, the pointer to a static method is used for comparison with the one returned by "other" object in "static bool IsTypeOf(_TypeCheckBase& other)" so as to provide a type-check. In addition you can get the name of the object.

#define TYPE_CHECK_IMPL(T)  \
   static bool IsTypeOf(_TypeCheckBase& other) { \
                    return other.GetType() == (unsigned int)&IsTypeOf; } \
   virtual unsigned int GetType() { \
                    return (unsigned int)&IsTypeOf; } \
   public: virtual const string& GetTypeName() { \
                    static string typeName = #T; \
                    return typeName; }

#define TYPE_CHECK_DECL(T) \
   typedef T _TypeCheckBase;\
   TYPE_CHECK_IMPL(T)


class root
{
    public:
        TYPE_CHECK_DECL(root)
};

class A: public root
{
    public:
        TYPE_CHECK_IMPL(A)
};

class AA: public A
{
    public:
        TYPE_CHECK_IMPL(AA)
};

class B: public root
{
    public:
        TYPE_CHECK_IMPL(B)
};

No you can do the following:

inline void prn(std::string txt, bool val)
{
    cout << txt << ": " << (val ? "true":"false") << endl;
}

#define CMP(foo,bar)  prn(#foo "\tis type of " #bar " TypeName:\"" + bar.GetTypeName() + "\"", foo::IsTypeOf(bar));

int main(void)
{

    A a; AA aa;
    B b;

    cout << endl;

    CMP(A,a);
    CMP(AA,a);
    CMP(B,a);

    CMP(A,aa);
    CMP(AA,(*((A*)&aa)));
    CMP(B,aa);

    CMP(A,b);
    CMP(AA,b);
    CMP(B,b);
}

The main methods you use here are:

  • bool Foo::IsTypeOf(bar) with Foo being a class type and bar being an object, derived directly or indirectly from your root class type.

  • string bar.GetTypeName()

Upvotes: 0

Luchian Grigore
Luchian Grigore

Reputation: 258678

You can achieve the same thing with dynamic_cast. Your classes are polymorphic anyway.

Note that this is at least a code smell. You shouldn't need to find the actual type of classes in a well-thought design. What underlying problem are you trying to solve?

Also, typename is a keyword in C++, you should name your method differently.

EDIT: A possible better solution for this would be to have a list of pairs of objects that can be stacked, and have virtual methods:

class Object
{
   virtual bool canStack(const std::string& baseObject) = 0;
};

class Flower
{
   virtual bool canStack(const std::string& baseObject)
   {
       if ( baseObject == "pot" ) 
           return true;
       return false;
   }
};

Now I see why you'd want the get name.

Upvotes: 3

Related Questions