views:

134

answers:

6

Dear reader,

I'm working on a aplication where I draw a couple of images, like this:

void TimeSlice::draw(float fX, float fY) {
list<TimeSliceLevel*>::iterator it = levels.begin();
float level_x = x;
float level_y = y;
while(it != levels.end()) {
    (*it)->draw(level_x,level_y);
    level_y += (*it)->height;
    ++it;
}

}

Though this is a bit incorrect. I need to position the TimeSliceLevel* on a X.. When I've got a for(int i = 0; i < slices.size(); ++i) loop, I can use x = i * width. Though I'm using an iterator as I've been told many times that's good programming :> and I'm wondering if the iterator has a "index" number of something which I can use to calculate the new X position? (So it's more a question about using iterators)

Kind regards, Pollux

+3  A: 

No, it doesn't. If you need an integer index, use a for-loop. Despite what some iterator extremists would have you believe, for-loops still have their place in C++ code.

anon
Over a list? Wouldn't that make it O(N^2)?
Paul Hankin
@Paul I almost never use lists, and I didn't notice the OP was doing so. If he really needs a list, then an iterator is the answer, but my experience is that most people choose list for no good reason at all, and will almost always be better served by using a vector or deque.
anon
He wants both an integer index (for the X offset) and an iterator (for the list access). I wouldn't bother, but still use a for loop: `for (iterator i = collection.begin(); i != collection.end(); ++i, x+=width) { /* draw(x,y) */ }`
MSalters
+8  A: 

They don't, as iterators can be used for other purposes besides looping from the beginning to the end of an ordered, indexed list. You'll need to keep track of an index separately and increment it every pass:

list<TimeSliceLevel*>::iterator it;
int index;

for(it = levels.begin(), index = 0; it != levels.end(); ++it, ++index) {
    ...
}
Michael Mrozek
A: 

You would have to write something like

size_t index = 0;
for (list<...>::const_iterator it = y.begin(); it != y.end(); ++it) {
   // Do your actions based on `index`
   ++index;
}

and, well, this is sometimes suitable.

On the other hand, you could refactor (replan) your application so that your actual drawing loop doesn't have to make all those x += something, y += something2, ..., but rather act the following way:

foreach (Level* level, list) {
    level->draw(backend);
}

It could sometimes be tricky, but to my mind this approach could save you a lot of time if your application grows to something "big".

Kotti
Thanks Kotti, how would I calculate the position then? and what's backend?
pollux
@pollux This is a tough question and it is connected with your problem domain. My main idea was that you could store your `x` and `y` in your level pieces (or whatever), or make them derive from some basic `Object` superclass. *Unfortunately, I can't advise you a lot, because I actually know nothing about how your application works*.
Kotti
@pollux `Backend` here is some abstract rendering engine / interface. For example, you could have *WinAPI backend, DirectX backend, OpenGL backend* and you should pass it to the object's draw method, so your object would know **"where to draw".**
Kotti
A: 

You CAN BUT ONLY for random-access iterator. If it's a random access iterator you can subtract your iterator from the begin iterator to obtain the index (without keeping a separate int index variable).

for (vector<int>::const_iterator cit = v.begin(); cit != v.end(); ++cit)
{
   cout << "This is element no: " << cit - v.begin() << endl;
}

In your example unfortunately you won't be able to do it, because you are using std::list, which is only a bidirectional iterator. Use std::vector and you can do it like my example.

ryaner
BTW, I have to mention I'm not really advocating this as the way to go. I don't see anything bad looping with index variable, if you do need an index.I'm merely pointing out that there's indeed a way.
ryaner
+1  A: 

For some iterator types, simply subtract the current iterator from the initial iterator:

index = it - levels.begin()

Since this does not work for std::list iterators, just track the index explicitly with a variable, as mentioned in the above answers. The benefit of using the iterator and the container is not lost. You're adding a requirement that the container doesn't provide.

John
+2  A: 

It is possible to go from iterator -> index. There are at least two ways:

  1. Use - for Random access iterators (i.e. i - container.begin())
  2. Use std::distance (i.e. std::distance(containter.begin(), i)). This is a more "generic" solution and will perform identically in the random access iterator case to - thanks to specialization, but will have a terrible performance impact otherwise

However, I would not recommend either of them, as it obfuscates the code (and can be unperformant). Instead as others have said, use an additional counter. There is nothing "wrong" with using indexes when needed, rather preferring iterators is meant to be a guideline to help in writing "generic" code, as then you can apply the algorithm to a different container, or a sub set of the container, etc.

Logan Capaldo
+1 for std::distance and also for pointing out that this is generally unnecessary. In the OP's case, just incrementing an index with each iteration would be fine.