|
-
August 9th, 2008, 05:43 AM
#1
Setting an iterator to "end"
Hey guys.
I've got this code where I set an iterator to point to the end of a vector so that a loop will end. I debugged it and it sets the iterator to the end because it points to 0xcdcdcdcd, but the for loop check doesn't seem to notice that the iterator points to the end because it tries to increment it and I get an error:

Code:
void Tile_Palette::Update(const SDL_Event* event_)
{
if(!event_) return;
if(event_->type == SDL_MOUSEBUTTONUP)
{
const SDL_Rect mouse = {event_->motion.x, event_->motion.y};
ptile_it it = camera_tiles.begin();
for(; it != camera_tiles.end(); ++it)
{
if(SDL_Tools::mouse_over_rect(mouse, (*it)->Get_Position()))
{ // Changed tile pen.
tile_pen = *(*it);
it = camera_tiles.end();
}
}
}
}
I can't see anything wrong with my code, but maybe someone else can...
Oh, and camera_tiles looks like this:
Code:
std::vector<XTile*> camera_tiles;
Cheers.
-
August 9th, 2008, 06:05 AM
#2
Re: Setting an iterator to "end"
I do nor see the problem with your code but you do not have to set the iterator to end, just break the loop
Code:
for(; it != camera_tiles.end(); ++it)
{
if(SDL_Tools::mouse_over_rect(mouse, (*it)->Get_Position()))
{ // Changed tile pen.
tile_pen = *(*it);
break;
}
}
Kurt
-
August 9th, 2008, 06:11 AM
#3
Re: Setting an iterator to "end"
 Originally Posted by ZuK
I do nor see the problem with your code but you do not have to set the iterator to end, just break the loop
Code:
for(; it != camera_tiles.end(); ++it)
{
if(SDL_Tools::mouse_over_rect(mouse, (*it)->Get_Position()))
{ // Changed tile pen.
tile_pen = *(*it);
break;
}
}
Kurt
Generally I do use break, but sometimes I try and make use of the loop condition. If I can't resolve this then I'll just use break but I'm interested to know why this is happening.
-
August 9th, 2008, 06:38 AM
#4
Re: Setting an iterator to "end"
Hello mybowlcut,
change your logical operator to
Code:
for(; it < camera_tiles.end(); ++it)
i think this should work without breaking the loop
-
August 9th, 2008, 07:09 AM
#5
Re: Setting an iterator to "end"
 Originally Posted by potatoCode
Hello mybowlcut,
change your logical operator to
Code:
for(; it < camera_tiles.end(); ++it)
No.
The correction is this:
Code:
for(; it != camera_tiles.end(); ++it)
The end() of a sequence can be any value that the implementor of the container class has created. The only guarantee is that when you increment past the end of the valid items in a container, the iterator is equal to end().
The real correction to MyBowlCut's code is to not mess around with (in other words change) the controlling loop iterator in the middle of the loop. Any code that I see doing that is always susceptible to some sort of bug.
Regards,
Pauil McKenzie
Last edited by Paul McKenzie; August 9th, 2008 at 07:12 AM.
-
August 9th, 2008, 07:21 AM
#6
Re: Setting an iterator to "end"
 Originally Posted by Mybowlcut
Generally I do use break, but sometimes I try and make use of the loop condition. If I can't resolve this then I'll just use break but I'm interested to know why this is happening.
Simple explanation.
You set the iterator to the end, then the for loop increments the iterator you set (the ++it in the for loop). One past the end(), and boom, your dead.
As stated to potatoCode, if you have a for loop(), and your controlling iterator is part and parcel of the for loop itself, do not change the value of the controlling iterator within the loop. Any code that does that is by its very nature suspicious. This is regardless of whether it is an iterator or a simple int variable controlling the loop:
Code:
for (int i = 0; i < some_condition; ++i)
{
///...
i = some_value;
//...
}
If it were up to me in a code review, I would reject any code that does this.
The proper loop construct if you want to change things around like this is a while loop, or at least a for loop that has some sort of terminating condition:
Code:
bool okToContinue = true;
for (int i = 0; i < somevalue && okToContinue; ++i)
{
// if ( whatever )
{
okToContinue = false;
}
}
Then the loop terminates automatically since the okToContine is false.
Regards,
Paul McKenzie
-
August 9th, 2008, 08:58 AM
#7
Re: Setting an iterator to "end"
 Originally Posted by Paul McKenzie
Simple explanation.
You set the iterator to the end, then the for loop increments the iterator you set (the ++it in the for loop). One past the end(), and boom, your dead.
As stated to potatoCode, if you have a for loop(), and your controlling iterator is part and parcel of the for loop itself, do not change the value of the controlling iterator within the loop. Any code that does that is by its very nature suspicious. This is regardless of whether it is an iterator or a simple int variable controlling the loop:
Code:
for (int i = 0; i < some_condition; ++i)
{
///...
i = some_value;
//...
}
If it were up to me in a code review, I would reject any code that does this.
The proper loop construct if you want to change things around like this is a while loop, or at least a for loop that has some sort of terminating condition:
Code:
bool okToContinue = true;
for (int i = 0; i < somevalue && okToContinue; ++i)
{
// if ( whatever )
{
okToContinue = false;
}
}
Then the loop terminates automatically since the okToContine is false.
Regards,
Paul McKenzie
Ahh yeah, forgot the order of operations in the loop. Cheers Paul.
One question though, in your code example, what happens if there is code after the if statement that shouldn't be executed if okToContinue is false? In situations like that, would it be better to use a break?
-
August 9th, 2008, 09:19 AM
#8
Re: Setting an iterator to "end"
 Originally Posted by Paul McKenzie
If it were up to me in a code review, I would reject any code that does this.
The proper loop construct if you want to change things around like this is a while loop, or at least a for loop that has some sort of terminating condition:
Code:
bool okToContinue = true;
for (int i = 0; i < somevalue && okToContinue; ++i)
{
// if ( whatever )
{
okToContinue = false;
}
}
Then the loop terminates automatically since the okToContine is false.
Regards,
Paul McKenzie
Even this might raise some eyebrows. I prefer Kurt's suggestion as long as there is no practical reason not to use break.
Another option if you're so inclined is to use std::find_if, with a predicate along the lines of:
Code:
class Mouse_Over_Tile_Pred
{
public:
Mouse_Over_Tile_Pred(const SDL_Rect & mouse) :
m_mouse(mouse)
{
}
bool operator()(Tile_Pen * /* ? */ tile_pen) const
{
return SDL_Tools::mouse_over_rect(m_mouse, tile_pen->Get_Position());
}
private:
const STL_Rect m_mouse;
};
But that's a bit of overkill for such a simple predicate.
- Alon
-
August 9th, 2008, 10:33 AM
#9
Re: Setting an iterator to "end"
The real correction to MyBowlCut's code is to not mess around with (in other words change) the controlling loop iterator in the middle of the loop. Any code that I see doing that is always susceptible to some sort of bug.
How then do you deal with a situation where you need to manually control the iterator? Take for example deleting items from a STL list:
Code:
list<Foo>::iterator toDel;
for (list<Foo>::iterator it = myList.begin( ); i != myList.end( );)
{
if (it->NeedsDeletion( ))
{
toDel = it;
it++;
myList.erase(toDel);
}
else
{
it->SomeMethod( );
it++;
}
}
-
August 9th, 2008, 10:42 AM
#10
Re: Setting an iterator to "end"
erase returns an iterator to the first element after the erase(d) one(s).
So it could be done like that
Code:
for (list<Foo>::iterator it = myList.begin( ); i != myList.end( ) ) {
if (it->NeedsDeletion( )) {
it = myList.erase(it);
}
else
kurt
-
August 9th, 2008, 10:58 AM
#11
Re: Setting an iterator to "end"
Does it? I'd completely forgotten.
Edit: According to this it actually returns an iterator to the element after the one erased... so you still need to increment manually, or else you'll skip elements when you erase.
Last edited by Speedo; August 9th, 2008 at 11:01 AM.
-
August 9th, 2008, 11:43 AM
#12
Re: Setting an iterator to "end"
 Originally Posted by Speedo
How then do you deal with a situation where you need to manually control the iterator? Take for example deleting items from a STL list:
That is what remove_if() is for. Usage of the remove_if() algorithm makes writing loops as you've written a moot point.
Code:
#include <list>
#include <algorithm>
using namespace std;
class Widget
{
public:
bool NeedsToBeRemoved() const;
};
bool RemoveMe(const Widget& w)
{
return w.NeedsToBeRemoved();
}
int main()
{
list<Widget> WidgetList;
WidgetList.erase(remove_if(WidgetList.begin(), WidgetList.end(),
RemoveMe ), WidgetList.end());
}
Questions like "does the function return the next iterator?" or "do I increment it now?" or other questions like that are now no longer an issue.
Edit:
You also have algorithms such as partition() that moves "bad" elements to the end of the sequence. Then you apply erase() to those elements.
Regards,
Paul McKenzie
Last edited by Paul McKenzie; August 9th, 2008 at 11:48 AM.
-
August 9th, 2008, 11:50 AM
#13
Re: Setting an iterator to "end"
 Originally Posted by Mybowlcut
Ahh yeah, forgot the order of operations in the loop. Cheers Paul.
One question though, in your code example, what happens if there is code after the if statement that shouldn't be executed if okToContinue is false? In situations like that, would it be better to use a break?
break and continue are only marginally less evil than goto. What's wrong with if (!OkToContinue) { ... }? Mind you, in situations like that, I'd start to think about refactoring the loop.
Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
-- Sutter and Alexandrescu, C++ Coding Standards
Programs must be written for people to read, and only incidentally for machines to execute.
-- Harold Abelson and Gerald Jay Sussman
The cheapest, fastest and most reliable components of a computer system are those that aren't there.
-- Gordon Bell
-
August 9th, 2008, 12:09 PM
#14
Re: Setting an iterator to "end"
 Originally Posted by Paul McKenzie
That is what remove_if() is for. Usage of the remove_if() algorithm makes writing loops as you've written a moot point.
A little off topic here, but do you recommend simply writing the RemoveMe function as in your example, or would you also recommend using std::mem_fun_ref(&Widget::NeedsToBeRemoved) instead?
-
August 9th, 2008, 12:34 PM
#15
Re: Setting an iterator to "end"
 Originally Posted by laserlight
A little off topic here, but do you recommend simply writing the RemoveMe function as in your example, or would you also recommend using std::mem_fun_ref(&Widget::NeedsToBeRemoved) instead?
Either way is OK.
The bottom line is that using the algorithms is better than writing loops that I've seen too often than not cause all kinds of problems. Even a good coder could botch writing a loop that erases an item but then doesn't increment the iterator incorrectly, or in the wrong place, or whatever. Usage of the proper algorithm removes these problems.
Regards,
Paul McKenzie
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|