Skip to main content
GameDev.net gamedev.net
🔒 Locked

RTTI question

Started by JimPrice Nov 25, 2004 at 6:25 PM 16 replies 1.2k views
Original Post
JimPrice
JimPrice
Hi, I'm trying to loop through a map to find if any particular instance (EntityBuilding) of a derived class (from Entity)exists, code snippet as follows:


for(std::map<unsigned int, Entity*>::iterator it = entityList_.begin(); it != entityList_.end(); ++it)
	{
		Entity* curEntity = &(*it->second);
		if(typeid(*curEntity) == typeid(EntityBuilding))
		{
				return true;
		}


However, it keeps on crashing on me, and I'm not sure why. Using VC2003, it's an unhandled exception. (And yes, I know I should use std::for_each!) Thanks, Jim.
snk_kid
snk_kid
Quote:
Original post by JimPrice
(And yes, I know I should use std::for_each!)


Well you wouldn't use for_each to do a search there are STL algorithms for that particular task, and you would probably been less likely to have had bugs if you had started off trying to use STL algorithms in the first place first [smile], anyways this code should do it for you (using boost smart pointers, can't be bothered with raw pointers to much work for no gain).

#include <boost\smart_ptr.hpp>#include <typeinfo>#include <algorithm>#include <map>#include <iostream>struct b {  virtual ~b() {} };struct d : b {};struct c : b {};struct e : b {};typedef boost::shared_ptr< b > b_ptr;typedef std::map< unsigned int, b_ptr > bmap;struct type_equals {	const std::type_info& info;	type_equals(const std::type_info& f): info(f) {}	bool operator()(const bmap::value_type& b) const {		return typeid(*b.second) == info;	}};int main() {		bmap m;	b_ptr foo(new d);	m[0] = foo;	foo.reset(new c);	m[1] = foo;	foo.reset(new e);	m[2] = foo;	foo.reset(new c);	m[3] = foo;	if(std::find_if(m.begin(), m.end(), type_equals(typeid(e))) != m.end())		std::cout << "found\n";	else		std::cout << "not found\n";}


[Edited by - snk_kid on November 25, 2004 7:01:11 PM]
JimPrice
JimPrice
Quote:

Well you wouldn't use for_each to do a search there are STL algorithms for that particular task, and you would probably been less likely to have had a bugs if you had started off trying to use STL algorithms in the first place first


OK, I feel sad now!!!

Thanks for the example - unfortunately I can't use find_if because the example I gave was simplified (I actually want to do an operation every time I get a type-hit in the map), but the code is a great step towards what I want.

Thanks!
Jim.
snk_kid
snk_kid
Quote:
Original post by JimPrice
Quote:

Well you wouldn't use for_each to do a search there are STL algorithms for that particular task, and you would probably been less likely to have had a bugs if you had started off trying to use STL algorithms in the first place first


OK, I feel sad now!!!


Sorry!!!, wasn't meant to be an attack on you!.

Actually i remembered something, did you enable RTTI in VC++ 2003?
JimPrice
JimPrice
Quote:

Sorry!, wasn't meant to be an attack on you!.


No worries, it was tongue in cheek!

Quote:

Actually i remembered something, did you enable RTTI in VC++ 2003?


Er - do you have to do this? This is the first time I've used RTTI in VC2003 (been using DevC++ until recently). I just assumed once you'd included the requisite headers then Bob was your proverbial uncle - are you saying that's not the case?

Off to check the help files in VC.

Jim.
snk_kid
snk_kid
if you try:

Project Properties -> C++ -> Language -> Enable Run-time Type Info

it might do the trick
JimPrice
JimPrice
Quote:

it might do the trick


Yup, that seems to work. There is a brief mention of the extra flags in the typeid entry on the help engine.

Thanks for the help - would rate you up, but I can't rate you any higher!

Jim.

Edit : Do you know why RTTI isn't enabled by default?
Namethatnobodyelsetook
Namethatnobodyelsetook
My question would be why it lets you use typeid() with RTTI off and give you no errors. I got a warning when trying to dynamic_cast with RTTI off, so I'd expect typeid() to spew atleast a warning as well.
Qw3r7yU10p!
Qw3r7yU10p!
Entity* curEntity = &(*it->second);
is the same as
Entity* curEntity = it->second;
SiCrane
SiCrane
Quote:
Original post by JimPrice
Thanks for the example - unfortunately I can't use find_if because the example I gave was simplified (I actually want to do an operation every time I get a type-hit in the map), but the code is a great step towards what I want.


If you're acutally attempting an operation on the object if it's a certain type, then chances are that you should be using a dynamic_cast instead of typeid. That will determine if the object is of the correct type and get you a pointer to it all at the same time.
Qw3r7yU10p!
Qw3r7yU10p!
As always, using RTTI usually means poor design. We'd need to see the bigger picture to be able to make suggestions for improvements though.

Get a copy of 'Refactoring: Improving the Design of Existing Code' if you can.
Zahlman
Zahlman
Quote:
Original post by petewood
Entity* curEntity = &(*it->second);
is the same as
Entity* curEntity = it->second;


No, it isn't. it->second is rather the same as (&*it)->second. The * and & may be inverse operations, but they can't commute across the member-selection operation.

Anyway, &(*it->second) doesn't seem to be right here because there are two dereferences - that is necessary when you are working with a vector of pointers (one dereference to get the pointer from the iterator, and another to get the object from the pointer), but not with a map, because the iterator dereference always returns an actual object (whatever is the name of the struct that has "first" and "second" members, I'm too lazy to look up its name). So it->second is probably what you want here, after all. :) That is, dereference once to get the object, member-select the "second" which is an Entity*, and then you're done.

The "type calculus" of C++ is not that hard to do once you get used to it, and it will catch lots of problems. Actually, I'm kind of surprised the code compiled as written.
SiCrane
SiCrane
Quote:
Original post by Zahlman
No, it isn't. it->second is rather the same as (&*it)->second. The * and & may be inverse operations, but they can't commute across the member-selection operation.

-> has higher precedence than unary *, so &(*it->second) is equal to &(*(it->second)). The type of it->second is an Entity *, so *(it->second) is an Entity &. So &(*it->second) is indeed the same as it->second.

Had the operator precedence been bound as you seemed to think it did, then the (&*it) would have referred to the address of a std::map::value_type, which in turn is a std::pair, so &(*it->second) would still be referring to the exact same thing as it->second.

So yes, &(*it->second) is, in fact, the same as it->second for this instantiation of std::map.
Qw3r7yU10p!
Qw3r7yU10p!
Quote:
Original post by Zahlman
The "type calculus" of C++ is not that hard to do once you get used to it, and it will catch lots of problems. Actually, I'm kind of surprised the code compiled as written.
[oh]
Qw3r7yU10p!
Qw3r7yU10p!
An iterator is designed to have pointer like symantics.

So, to access the thing it points to you use ->, i.e:

it->

Or you could dereference the iterator (pointer) and use the dot operator to access the thing it points to:

(*it).

In Jim's case the iterator points to an object of type map::value_type. This is actually a key/value pair. The key is first, the value is second. In this case the value is a pointer, Entity*, so accessing value.second gives you an Entity*.

it->second is of type Entity*

or

(*it).second is of type Entity*
JimPrice
JimPrice
Blimey - you go to sleep for a night, come back and everyone points out your inadequacies :)

Thanks for the help all!! Still getting my head around iterators - but reading Josuttis' book is helping.

Jim.
JimPrice
JimPrice
Quote:

As always, using RTTI usually means poor design. We'd need to see the bigger picture to be able to make suggestions for improvements though



OK - so as there's an offer on the table, I'll see if you'll take up on it. I am currently building a very simple AI sandpit - so it's topdown 2D only. The problem is as follows:


I have a std::map of - the unsigned int will change in the future, but is irrelevant to this topic. Assume as the moment that I have only 2 derived classes from Entity : EntityBuilding and EntityMonster.


At the beginning of a level, EntityManager::LoadLevel (original, huh?) loads in all the level data from a file. The next step is to create a set of navigation nodes for my pathfinding algorithm. Basically it superimposes a grid of points / nodes at specified intervals, and then tries to connect each grid point to its 8 neighbours. When evaluating whether 2 nodes are connected, a check is made to see if the line segment between the neighbours crosses any EntityBuilding. If it does, then they aren't connected; otherwise they are. Intersections between node-pairs and EntityMonsters are irrelevant.


The code snippet this all started with is basically the loop for checking whether this intersection occurs, by identifying those objects which are EntityBuildings only. I was using typeid instead of dynamic_cast for extensibility : I found it easier to write (if(typeid(*curEntity) == typeid(a) || typeid(*curEntity) == typeid(b))) - in previous languages I've used there is a very useful 'in' command - used like 'if(a in (b, c, d))', but I don't think there is that option in C++.


Using a virtual function in Entity would get around the evils of RTTI - I assume I'd have to have a 'CheckLineIntersection' function in Entity, which is overidden by the different derived classes - but this didn't feel like the sort of service an Entity should provide. Hence the use of RTTI.


Feel free to comment with any refactoring advice!

Jim.
Zahlman
Zahlman
Urrrrg, I got schooled. ;)

Anyway. Instead of making your method "checkLineIntersection" (which suggests putting putting part of the actual pathfinding logic into the Entities), just make it a bool "canBlockMovement" which is true for building-type Entities and false for anything that does not in fact block the pathfinder.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.