Checking whether an object is an instance of a class or its subclasses

Pages: 12
Consider the following pseudo-code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <vector>

using namespace std;

class A {
public:
    A() {};
    virtual void foo() { cout << "A!" << endl; };
};

class B: public A {
public:
    B() {};
    virtual void foo() { cout << "B!" << endl; };
};

class C: public B {
public:
    C() {};
    virtual void foo() { cout << "C!" << endl; };
};

void check(A a) {
    vector<A*> v;
    v.push_back(new A());
    v.push_back(new B());
    v.push_back(new C());

    for (A* x : v) {
        if (type of x is type of a)  {
            x->foo();
        }
    }
    cout << "---------" << endl;
    cout << endl;
}

int main() {
    check(A());
    check(B());
    check(C());
    return 0;
}


Basically I want to implement the check function so that the program prints this:
A!
B!
C!
---------

B!
C!
---------

C!
---------


I did STFW, but this is all I could find:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void check(A a) {
    vector<A*> v;
    v.push_back(new A());
    v.push_back(new B());
    v.push_back(new C());

    for (A* x : v) {
        A* y = dynamic_cast<A*>(x);
        if (y != nullptr)  {
            x->foo();
        }
    }
    cout << "---------" << endl;
    cout << endl;
}

That would work, but I want "x" to be checked against the type of "a", not always "A*".

I also tried this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void check(A a) {
    vector<A*> v;
    v.push_back(new A());
    v.push_back(new B());
    v.push_back(new C());

    for (A* x : v) {
        decltype(a)* y = dynamic_cast<decltype(a)*>(x);
        if (y != nullptr)  {
            x->foo();
        }
    }
    cout << "---------" << endl;
    cout << endl;
}

But as expected, it doesn't work as I want either. decltype always returns "A" because "a" is declared as "A".

Halp? :)
1. a is being passed by value, so no polymorphism there. a is and instance of A, no matter what you passed to the function. To have polymorphism, you need pointers or references.
2. To compare the types of two polymorphic objects through their pointers: typeid(*x)==typeid(*y)
a is being passed by value, so no polymorphism there. a is and instance of A, no matter what you passed to the function. To have polymorphism, you need pointers or references.

Crap, I forgot. Slicing, right?

To compare the types of two polymorphic objects through their pointers: typeid(*x)==typeid(*y)

It doesn't do what I want:
A!
---------

B!
---------

C!
---------

(And yes, I changed the first argument of "check" to A*.)

Thank you for your answer!
Last edited on
This works for me:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <typeinfo>

struct A{
	virtual const char *f()=0;
};

struct B:public A{
	const char *f(){
		return "A\n";
	}
};

struct C:public A{
	const char *f(){
		return "C\n";
	}
};

struct D:public A{
	const char *f(){
		return "D\n";
	}
};

int main(){
	A *v[]={
		new B,
		new C,
		new D
	};
	A *p=new C;
	for (int a=0;a<3;a++)
		if (typeid(*p)==typeid(*v[a]))
			std::cout <<v[a]->f();
	return 0;
}
Output:
C
Right, but if you, for instance, change "D" to be a subclass of "C", it should print this:
C
D
Last edited on
Oh.

Mmh, here's the problem with your logic:
typeof(*p)={A,C}
typeof(*v[2])={A,C,D}
You're looking for a function common_ancestor() such that common_ancestor(x,y)=(typeof(x) ∩ typeof(y) != {}). The problem is that
typeof(*v[0])={A,B}

Now, you could argue that there's no reason why we couldn't have a function typeof'() such that
typeof'(*p)={C}
But the problem with that is that there's no general way to construct typeof'() that wouldn't cause this:
typeof'(*v[2])={D}
Note that the combined behavior of typeid() and the operator==() overload is the same as typeof'() and common_ancestor()'s.

The solution: add a special member to A that keeps track of the various types that an instance is an instance of, such that you can implement typeof(). Then implement a limited_common_ancestor() that will return true only when the left operand is a direct subclass of the right operand.
I don't see any general way to implement the behavior you're looking for. At best, you could implement a most_recent_common_ancestor() and check the return value. For example:
1
2
3
4
5
6
7
8
9
10
11
12
int main(){
	A *v[]={
		new B,
		new C,
		new D
	};
	A *p=new C;
	for (int a=0;a<3;a++)
		if (v[a]->most_recent_common_ancestor(p)==typeid(C))
			std::cout <<v[a]->f();
	return 0;
}
I believe the OP is looking for something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include <vector>

using namespace std;

class A {
public:
    A() {};
    virtual void foo() { cout << "A!" << endl; };
    virtual bool cast_is_allowed(const A * a) const {
        return dynamic_cast<const A *>(a) != 0;
    }
};

class B: public A {
public:
    B() {};
    virtual void foo() { cout << "B!" << endl; };
    virtual bool cast_is_allowed(const A * a) const {
        return dynamic_cast<const B *>(a) != 0;
    }
};

class C: public B {
public:
    C() {};
    virtual void foo() { cout << "C!" << endl; };
    virtual bool cast_is_allowed(const A * a) const {
        return dynamic_cast<const C *>(a) != 0;
    }
};

void check(const A & a) {
    vector<A*> v;
    v.push_back(new A());
    v.push_back(new B());
    v.push_back(new C());

    for (unsigned i = 0; i < v.size(); ++i) {
        if (a.cast_is_allowed(v[i]))  {
            v[i]->foo();
        }
    }
    cout << "---------" << endl;
    cout << endl;
}

int main() {
    check(A());
    check(B());
    check(C());
    return 0;
}
closed account (1yR4jE8b)
Isn't the point of polymorphism and virtual functions supposed to be that we aren't supposed to care about any of this?
What is ^ this ^ supposed to mean?
typeof(*p)={A,C}
typeof(*v[2])={A,C,D}
You're looking for a function common_ancestor() such that common_ancestor(x,y)=(typeof(x) typeof(y) != {}).

Yeah, more or less. But I don't want to know if they have a common ancestor, I want to know if "y" is an ancestor of "x".

The problem is that
typeof(*v[0])={A,B}

Now, you could argue that there's no reason why we couldn't have a function typeof'() such that
typeof'(*p)={C}
But the problem with that is that there's no general way to construct typeof'() that wouldn't cause this:
typeof'(*v[2])={D}
Note that the combined behavior of typeid() and the operator==() overload is the same as typeof'() and common_ancestor()'s.

How about doing this, then?
is_ancestor(x, y) = (typeof(x) ∩ typeof'(y) != {})
Though I have no idea how to.

The solution: add a special member to A that keeps track of the various types that an instance is an instance of, such that you can implement typeof().

And I'd go about doing that exactly how? :/
Last edited on
What you want involves double dispatch. There are several ways to implement
it, but the most straightforward is the one I posted above. Did you see my post?
Last edited on
Woops. Believe it or not, you posted when I was writing my last message. Yes, it takes me a long time to write ;).

I checked your post now. It's what I wanted :). The only problem is that it violates the DRY principle :S. But I guess it'll have to do for now.

Thank you, helios and m4ster r0shi.

The only problem is that it violates the DRY principle

But at least it satisfies the KISS principle, the Law of Demeter, and Archimedes' principle
closed account (1yR4jE8b)
What is ^ this ^ supposed to mean?


What I mean is, in my experience I have never encountered a situation where I've needed to explicitly know what the dynamic type of an object is.

From my experience, if you think you need to do this then either your class hierarchy may be mis-designed or you are trying to apply OOP to a situation where it is not necessarily appropriate.
But at least it satisfies the KISS principle, the Law of Demeter, and Archimedes' principle

Archimedes' principle?
Any floating object displaces its own weight of fluid.

That? :P

Anyway, I found out another way of doing it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <vector>
#include <typeinfo>
#include <string>
#include <assert.h>

using namespace std;

class A {
public:
    A() { add_parent(); };
    virtual void foo() { cout << "A!" << endl; };

    bool is_instance_of(const A& x) {
        assert(parents.size() != 0);
        string name = typeid(x).name();

        for (string parent : parents) {
            if (parent == name) {
                return true;
            }
        }
        return false;

        //return *find(parents.begin(), parents.end(), name) == name;
    };

protected:
    void add_parent() {
        parents.push_back(typeid(*this).name());
    };

private:
    vector<string> parents;
};

class B: public A {
public:
    B() { add_parent(); };
    virtual void foo() { cout << "B!" << endl; };
};

class C: public B {
public:
    C() { add_parent(); };
    virtual void foo() { cout << "C!" << endl; };
};

void check(const A& a) {
    vector<A*> v;
    v.push_back(new A());
    v.push_back(new B());
    v.push_back(new C());

    for (A* x : v) {
        if (x->is_instance_of(a)) {
            x->foo();
        }
    }
    cout << "---------" << endl;
    cout << endl;
}

int main() {
    check(A());
    check(B());
    check(C());
    return 0;
}

Very far from perfect, but I prefer it because you only need to call add_parent in the constructor of all classes for it to work.
In is_instance_of there's a commented line. I tried to use that instead of the for (as it is probably faster), but it results in a segfault.
Last edited on
I probably didn't explain myself well, but what you did there is more or less what I said.
add a special member to A that keeps track of the various types that an instance is an instance of, such that you can implement typeof().
The difference is that it may be better to store std::type_info (what typeid() returns) in the vector, rather than strings. I imagine types are stored as integers.

As for the segmentation fault, when std::find() can't find anything, it returns an iterator that's one element past the end of the contained. Dereferencing this iterator is of course illegal.
I probably didn't explain myself well, but what you did there is more or less what I said.
Oh, I see.

The difference is that it may be better to store std::type_info (what typeid() returns) in the vector, rather than strings. I imagine types are stored as integers.
I thought it wasn't possible to store type_info in a vector. However it seems you can store const type_info*, so I changed that.

As for the segmentation fault, when std::find() can't find anything, it returns an iterator that's one element past the end of the contained. Dereferencing this iterator is of course illegal.
Hmm...! Fixed.

Here. v2.0:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <vector>
#include <typeinfo>
#include <algorithm>
#include <assert.h>

using namespace std;

class A {
public:
    A() { add_parent(); };
    virtual void foo() { cout << "A!" << endl; };

    bool is_instance_of(const type_info* x) {
        assert(parents.size() != 0);
        return find(parents.begin(), parents.end(), x) != parents.end();
    };

protected:
    void add_parent() {
        parents.push_back(&typeid(*this));
    };

private:
    vector<const type_info*> parents;
};

class B: public A {
public:
    B() { add_parent(); };
    virtual void foo() { cout << "B!" << endl; };
};

class C: public B {
public:
    C() { add_parent(); };
    virtual void foo() { cout << "C!" << endl; };
};

void check(const A& a) {
    vector<A*> v;
    v.push_back(new A());
    v.push_back(new B());
    v.push_back(new C());

    for (A* x : v) {
        if (x->is_instance_of(&typeid(a))) {
            x->foo();
        }
    }
    cout << "---------" << endl;
    cout << endl;
}

int main() {
    check(A());
    check(B());
    check(C());
    return 0;
}


Thanks for your help.


What I mean is, in my experience I have never encountered a situation where I've needed to explicitly know what the dynamic type of an object is.

From my experience, if you think you need to do this then either your class hierarchy may be mis-designed or you are trying to apply OOP to a situation where it is not necessarily appropriate.
I know what you mean, but I think it's the best thing to do in my case.
I need this for a game. In the game, the class Actor is parent of all the interactive elements (the player, the enemies, obstacles...), and the active instances are saved in a vector of Actor*. What I need is an easy way of applying something to a certain group of actors (for instance, removing all enemies from the scene).
Last edited on
Ideka wrote:
What I need is an easy way of applying something to a certain
group of actors (for instance, removing all enemies from the scene).

Is storing the active instances in separate vectors (depending on their type)
out of the question? If yes, another option is the visitor design pattern (*).

(*) http://stackoverflow.com/questions/255214/when-should-i-use-the-visitor-design-pattern/255300#255300
Is storing the active instances in separate vectors (depending on their type)
out of the question?
You bet it is.

If yes, another option is the visitor design pattern (*).
Whoa, that's nice. I don't think I can apply it to this situation though, but I'll definitely study it further.
m4ster r0shi wrote:
another option is the visitor design pattern
Ideka wrote:
I don't think I can apply it to this situation though

I assume that your class hierarchy looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Actor { /* ... */ };

class Player           : public Actor { /* ... */ };
class AbstractEnemy    : public Actor { /* ... */ };
class AbstractObstacle : public Actor { /* ... */ };

// no more direct Actor subclasses (this is important)

class ConcreteEnemyA : public AbstractEnemy { /* ... */ };
class ConcreteEnemyB : public AbstractEnemy { /* ... */ };
class ConcreteEnemyC : public AbstractEnemy { /* ... */ };

// more concrete enemies ...

class ConcreteObstacleA : public AbstractObstacle { /* ... */ };
class ConcreteObstacleB : public AbstractObstacle { /* ... */ };
class ConcreteObstacleC : public AbstractObstacle { /* ... */ };

// more concrete obstacles ... 

The facts that...

(1) The direct Actor subclasses are likely to remain
unchanged throughout the development of your game.

(2) The direct Actor subclasses represent concepts that don't look like
they should have a common interface (other than basic actor stuff).

...indicate that the visitor pattern could be of help.

Suppose that you want to add a function that will toughen up all enemies on the screen by
improving their weapon(s) and / or armor. Since different concrete enemies have different kinds
of weapons and armors, it makes sense to add a virtual function in your enemy class hierarchy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
AbstractEnemy : public Actor
{ /* ... */
    virtual void ImproveWeaponAndArmor() = 0;
/* ... */ };

ConcreteEnemyA : public AbstractEnemy
{ /* ... */
    virtual void ImproveWeaponAndArmor() { /* ... */ }
/* ... */ };

ConcreteEnemyB : public AbstractEnemy
{ /* ... */
    virtual void ImproveWeaponAndArmor() { /* ... */ }
/* ... */ };

// etc... 

However, it doesn't make sense to add that virtual function earlier in the hierarchy
(i.e. in the Actor class), because it doesn't make sense to have such a function for a
player (as your player is a monk and doesn't use weapons or armor) or an obstacle.

Here's where the visitor pattern comes into play:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class OperationOnActor
{
public:

    virtual void OperateOnPlayer  (class Player           * p) {}
    virtual void OperateOnEnemy   (class AbstractEnemy    * e) {}
    virtual void OperateOnObstacle(class AbstractObstacle * o) {}
};

class Actor { /* ... */ virtual void ApplyOperation(OperationOnActor * op) = 0; /* ... */ };

class Player : public Actor
{ /* ... */
    virtual void ApplyOperation(OperationOnActor * op)
        { op->OperateOnPlayer(this); }
/* ... */ };

class AbstractEnemy : public Actor
{ /* ... */
    virtual void ApplyOperation(OperationOnActor * op)
        { op->OperateOnEnemy(this); }
/* ... */ };

class AbstractObstacle : public Actor
{ /* ... */
    virtual void ApplyOperation(OperationOnActor * op)
        { op->OperateOnObstacle(this); }
/* ... */ };

class ImproveEnemyWeaponAndArmor : public OperationOnActor
{
public:

    virtual void OperateOnEnemy(AbstractEnemy * e) { e->ImproveWeaponAndArmor(); }
};

Doing the dispatch this way saves you space (you don't have to store
anything about the type of your objects), keeps the interface clean and
also makes darkestfright happy, as you don't have to use dynamic_cast.

The drawback is that the operation is attempted to be
executed on every actor, and that is a waste of time.

m4ster r0shi wrote:
Is storing the active instances in separate vectors
(depending on their type) out of the question?
Ideka wrote:
You bet it is.

Why is that? Does that vector represent some kind of 2D map? In this
case, you could use additional containers to keep track of your objects:

1
2
3
4
5
6
std::vector<Actor *> game_map;

std::set<AbstractEnemy *>    enemies;
std::set<AbstractObstacle *> obstacles;

Player * player;

Just make sure that every time you add / remove an actor
to / from your game_map you also update the respective set.

Doing it like this saves you time, because now you don't have to iterate over the whole
game_map to apply an operation on your enemies. You just use the appropriate set.

However, mind that it wastes memory.

The way you do it now neither saves time (as you check every actor of your
vector to find out if it's of the type you want) or space (because you store type
information on every object) nor makes darkestfright happy (as you still have
to use dynamic_cast after you find out that the object is of the type you want) :P
Last edited on
Pages: 12