Jump to content
  • Advertisement
Sign in to follow this  
speciesUnknown

two questions in one regarding STL and std:: containers.

This topic is 3780 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I've got two noob questions about c++:
std::map<std::string,GUI_widget *>::iterator it;
for(it = children.begin(); it!= children.end(); ++it)
{
	std::cout<<name<<" passing event to child widget"<<children[(*it).first]->name<<std::endl;
	children[(*it).first]->handle_event(E);
}




I'm not sure why all this is done this way. What advantage is there to this cryptic syntax? What can I do with this that is more powerful than using a foreach (if it existed)? Also, If I want to pick a random element out of the list, I don't really know how, since I'm not sure how to convert the "std::map<std::string,GUI_widget *>::iterator" into an integer to use with rand(). I've read the relevant sections on sgi's reference, but that doesn't help, and I'm not having any success with search engines. Thanks for any advice.

Share this post


Link to post
Share on other sites
Advertisement
1) That is essentially the same as a foreach loop. I don't personally use STL very much (I don't program at home as much anymore, and I don't use it at work since I need to keep executable sizes down), so I won't comment on the syntax of the iterator class.

2) If you want random access into a container, use a vector. Since vectors allow random access. A crude way would be to get a random integer between 0 and the number of elements and just iterate through the container and return the element at the random index. I'd just use a vector though (or wait for someone who uses STL to comment).

Share this post


Link to post
Share on other sites
Er... are you aware that the following code does the same thing (more efficiently)?

std::map<std::string,GUI_widget *>::iterator it;
for(it = children.begin(); it!= children.end(); ++it)
{
std::cout<<name<<" passing event to child widget"<<it->second->name<<std::endl;
it->second->handle_event(E);
}

Share this post


Link to post
Share on other sites
Hm, that example is pretty confusing. If I'm reading it right, it really should just be:

// This typedef would probably go in the header file...
typedef std::map<std::string,GUI_widget *> widgets_t;

// ...

for(widgets::iterator it = children.begin(); it!= children.end(); ++it)
{
std::cout << name << " passing event to child widget" <<
it->second->name << std::endl;
it->second->handle_event(E);
}


Also, there is a for_each() function in the standard library (although getting it to do what you want can occasionally require jumping through some hoops).

As for picking a random element, a map is not a random-access container, so you can't just index into it using a random value. You should however be able to achieve essentially the same thing by using std::advance().

Share this post


Link to post
Share on other sites
Quote:
Original post by jyk
Hm, that example is pretty confusing. If I'm reading it right, it really should just be:

*** Source Snippet Removed ***
Also, there is a for_each() function in the standard library (although getting it to do what you want can occasionally require jumping through some hoops).

As for picking a random element, a map is not a random-access container, so you can't just index into it using a random value. You should however be able to achieve essentially the same thing by using std::advance().


Thanks for your answers,

I tried defining a new type instead of using the std::container<types>::extra::conceptual::overhead::iterator syntax repeatedly, but got a linker error.

Is there an indexed container that allows random access?

why it->second rather than it->first? (Is that why my code has a bizarre error where I can dump all child widgets in a list but not pass stuff down the heirarchy?

The point of this (very crude: only buttons and windows) OpenGL GUI project is to learn about STL containers. I've got it drawing nicely, so not everything was a waste of time.

I'll look deeper into a c++ for_each. I would really like to just say for_each(children, GUI_widget * child){} or something similar.


Share this post


Link to post
Share on other sites
That iterator syntax is more or less standard. Like a lot of things in C++, it's very verbose but that can't be avoided. One thing you can do is jyk suggested and use a typedef to reduce the length of the map type. If you do that, you can declare the iterator inside the for statement and it looks less bulky and is easier to read.

As for your loop being inefficient, it's doing unnecessary lookups. A map is implemented as a binary tree and the iterator as (I'm guessing) a depth-first traversal. Looking up a specific element in a binary tree takes time, but the iterator has already traversed to that element so there's no need to look it up again. You can just call it->second->handle_event(E).

And picking a random element is easy. You know the size of the map from with size method, and you can get an iterator on the first element with the begin method. After that, there's an advance function you can use, or you can use a for loop and increment the iterator.


// This is needed to use advance
#include <iterator>

// This is to save some typing
using namespace std;

map<string,GUI_widget*>::iterator i = children.begin();
advance(i, rand() % children.size());
cout << "Here's a random child widget: " << i->second->name << endl;

Share this post


Link to post
Share on other sites
Quote:
Original post by jonahrowley
That iterator syntax is more or less standard. Like a lot of things in C++, it's very verbose but that can't be avoided. One thing you can do is jyk suggested and use a typedef to reduce the length of the map type. If you do that, you can declare the iterator inside the for statement and it looks less bulky and is easier to read.

As for your loop being inefficient, it's doing unnecessary lookups. A map is implemented as a binary tree and the iterator as (I'm guessing) a depth-first traversal. Looking up a specific element in a binary tree takes time, but the iterator has already traversed to that element so there's no need to look it up again. You can just call it->second->handle_event(E).

And picking a random element is easy. You know the size of the map from with size method, and you can get an iterator on the first element with the begin method. After that, there's an advance function you can use, or you can use a for loop and increment the iterator.

*** Source Snippet Removed ***


Kule, this thread gives me some stuff to think about, plus a bug fix. Thanks, GDNet.

[edit]
er... I was under the impression that you can contaminate a namespace if you say using namespace? In what circumstances can this happen?

Share this post


Link to post
Share on other sites
Quote:
Original post by speciesUnknown
I tried defining a new type instead of using the std::container<types>::extra::conceptual::overhead::iterator syntax repeatedly, but got a linker error.

You probably don't want to do that. The containers in the standard library are adequate. C++ is verbose, you just have to live with it.

Quote:

Is there an indexed container that allows random access?

Define "indexed". Maps have random access like this: some_map["some key"].

Quote:

why it->second rather than it->first? (Is that why my code has a bizarre error where I can dump all child widgets in a list but not pass stuff down the heirarchy?

When you iterate over a map, you iterate over the map's pair element. If you have a map<string,int>, then the iterator points to a pair<string,int>. The pair has 2 methods of interest, first and second. The first method will return the string, the second method will return the int.

Quote:

I'll look deeper into a c++ for_each. I would really like to just say for_each(children, GUI_widget * child){} or something similar.


Sadly, you can't do that yet. The next version of C++ (called C++0x) will have lambdas (also called closures or anonymous functions). Then you'll be able to do that. Right now, you have to do something like this.


void print_pair(const pair<string,int>& p) {
cout << p.first() << ", " << p.second() << endl;
}

...
for_each( some_map.begin(), some_map.end(), print_pair );



As you can see, the actual code to be run by the for_each algorithm is disjointed from the for_each call itself. This is why people use the for loop with an iterator instead of a for_each call. This will be better in C++0x, you'll be able to do something like this.


for_each( some_map.begin(), some_map.end(), <>(const pair<string,int>& p) -> void {
cout << p.first() << ", " << p.second() << endl;
});

Share this post


Link to post
Share on other sites
Quote:
Original post by speciesUnknown
er... I was under the impression that you can contaminate a namespace if you say using namespace? In what circumstances can this happen?


That depends on your code. For small programs, there's no reason not to do it. For large programs, there are few reasons not to do it. You "contaminate" the global namespace with all the symbols from the std namespace, but that's usually not a problem. Of course, if you're using your own namespaces, importing everything from std into them is probably not a good idea.

Share this post


Link to post
Share on other sites
Quote:
Original post by speciesUnknown
I tried defining a new type instead of using the std::container<types>::extra::conceptual::overhead::iterator syntax repeatedly



typedef std::map<std::string,GUI_widget *>::iterator GUI_iterator;


Usage:

for (GUI_iterator it = children.begin(); it!= children.end(); ++it)
{
//...
}


Edit: Or, better yet

typedef std::map<std::string,GUI_widget *> GUI_map;
typedef GUI_map::iterator GUI_iterator;

GUI_map children;

for (GUI_iterator it = children.begin(); it!= children.end(); ++it)
{
//...
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!