iterate forward OR reverse

J

Jim Langston

I have a class I designed that stores text chat in a
std::vector<sd::string>. This class has a few methods to retrieve these
strings to be displayed on the screen.

void ResetRead( bool Reverse, bool wordWrap ); // Resets iterator. We can
ignore wordwrap for now.
std::string GetLine(); // Reads a line using iterator. Increments
Iterator.

Right now it only works in Reverse, since that's the method I used first,
but now I want to do it using a forward iterator. I want to avoid having to
duplicate code, as iterating forward and reverse are exactly the same with 2
diffeernces.

1. Declare forward iterator or reverse iterator.
2. use .begin() .end() or .rbegin() .rend()

I tried this in a test program:

std::vector<std::string>::iterator* MyIt;
MyIt = (std::vector<std::string>::iterator*) new
std::vector<std::string>::reverse_iterator;
for ( *MyIt = MyVector.begin(); *MyIt != MyVector.end(); ++*MyIt )
std::cout << **MyIt << std::endl;

But it iterators forward anyway. I can see one reason is because I'm using
..begin() and .end() but I would think the program wouldn't work because
++*MyIt on a reverse iterator *should* cause the iterator to go backwards,
which it doesn't. This leads me to believe that even though
reverse_iterator is derived from iterator, it's not using virtual classes,
or something. Not sure.

Can anyone come up with a way to do this without having to declare 2
separate methods for forward reading or reverse reading, or a if block
covering the whole for block?
 
D

Donovan Rebbechi

std::vector<std::string>::iterator* MyIt;
MyIt = (std::vector<std::string>::iterator*) new
std::vector<std::string>::reverse_iterator;

Nooooooo! Don't do that. Iterators are not supposed to be used
runtime-poymorphically.

for ( *MyIt = MyVector.begin(); *MyIt != MyVector.end(); ++*MyIt )
std::cout << **MyIt << std::endl;

Unless operator++ () is virtual, this will call the version in the
base class.

[snip]
++*MyIt on a reverse iterator *should* cause the iterator to go backwards,
which it doesn't. This leads me to believe that even though
reverse_iterator is derived from iterator, it's not using virtual classes,
or something. Not sure.

You only get polymorphic behaviour with virtual functions (NOT "virtual
classes")
Can anyone come up with a way to do this without having to declare 2
separate methods for forward reading or reverse reading, or a if block
covering the whole for block?

Use templates.
template <typename iter>
void printit ( iter start, iter end )
{
for (; start!=end; ++start)
std::cout << *start << std::endl;
}

or you could simplify by using the standard library, and generalise to
allow other ostreams:

template <typename iter>
void printit (iter start, iter end,std::eek:stream & out = std::cout)
{
std::copy(start,end,std::eek:stream_iterator(out, "\n"));
}


printit (MyVector.begin(),MyVector.end());
printit (MyVector.rbegin(),MyVector.rend());

Cheers,
 
B

benben

Jim Langston said:
I have a class I designed that stores text chat in a
std::vector<sd::string>. This class has a few methods to retrieve these
strings to be displayed on the screen.

void ResetRead( bool Reverse, bool wordWrap ); // Resets iterator. We can
ignore wordwrap for now.
std::string GetLine(); // Reads a line using iterator. Increments
Iterator.

Right now it only works in Reverse, since that's the method I used first,
but now I want to do it using a forward iterator. I want to avoid having to
duplicate code, as iterating forward and reverse are exactly the same with 2
diffeernces.

1. Declare forward iterator or reverse iterator.
2. use .begin() .end() or .rbegin() .rend()

I tried this in a test program:

std::vector<std::string>::iterator* MyIt;
MyIt = (std::vector<std::string>::iterator*) new
std::vector<std::string>::reverse_iterator;
for ( *MyIt = MyVector.begin(); *MyIt != MyVector.end(); ++*MyIt )
std::cout << **MyIt << std::endl;

Why do you:

1) Heap allocate the iterator?
2) Type cast the iterator?

To avoid duplicate try use standard algorithm facilities like for_each() and
accumulate():


void MyOp(std::string& o)
{
// play around with o
}

for_each(MyVector.begin(), MyVector.end(), MyOp); // foreward
for_each(MyVector.rbegin(), MyVector.rend(), MyOp); // backward
 
J

Jim Langston

benben said:
Why do you:

1) Heap allocate the iterator?
2) Type cast the iterator?

To avoid duplicate try use standard algorithm facilities like for_each()
and
accumulate():


void MyOp(std::string& o)
{
// play around with o
}

for_each(MyVector.begin(), MyVector.end(), MyOp); // foreward
for_each(MyVector.rbegin(), MyVector.rend(), MyOp); // backward

Please see my reply to Donovan. The actual code I am using uses 2 functions
within a class. The code I had shown that you responded to was a test I had
tried to see if I could use iterators as polymorphic.
 
B

benben

Please see my reply to Donovan.

Hmm, I don't see any of your reply to Donovan...or is it in another
discussion?
The actual code I am using uses 2 functions
within a class.

I don't think I understand this. Your actual code was

std::vector<std::string>::iterator* MyIt;
MyIt = (std::vector<std::string>::iterator*) new
std::vector<std::string>::reverse_iterator;
for ( *MyIt = MyVector.begin(); *MyIt != MyVector.end(); ++*MyIt )
std::cout << **MyIt << std::endl;

So what are the 2 functions within what class you are mentioning? My
previous post suggested a better alternative.
The code I had shown that you responded to was a test I had
tried to see if I could use iterators as polymorphic.

Iterators are not guarenteed to be polymorphic. Actually, you don't see many
virtual functions in the STL anyways.

ben
 
J

Jim Langston

benben said:
Hmm, I don't see any of your reply to Donovan...or is it in another
discussion?

Here is the reply I was talking about:


Donovan Rebbechi said:
[SNIP]
Use templates.
template <typename iter>
void printit ( iter start, iter end )
{
for (; start!=end; ++start)
std::cout << *start << std::endl;
}

or you could simplify by using the standard library, and generalise to
allow other ostreams:

template <typename iter>
void printit (iter start, iter end,std::eek:stream & out = std::cout)
{
std::copy(start,end,std::eek:stream_iterator(out, "\n"));
}


printit (MyVector.begin(),MyVector.end());
printit (MyVector.rbegin(),MyVector.rend());

I can see how either of these methods would work in the example I had given,
simply outputting the entire vector, but the iterator needs to suvive
between calls.

class CChatBuffer
{
private:
std::vector<std::string>::reverse_iterator r_MIter; // Message Iterator
std::vector<std::string> MessageBuffer; // Buffer to hold messages from
server
public:
void ResetRead( bool Reverse );
std::string GetLine();
// Other class variables/methods not shown
};

void CChatBuffer::ResetRead( bool Reverse )
{
if ( ! Reverse )
r_MIter = MessageBuffer.rend(); // Not allowing forward yet, so set to
end
r_MIter = MessageBuffer.rbegin();
}

std::string CChatBuffer::GetLine()
{
if ( r_MIter != MessageBuffer.rend() )
return (*r_MIter++);
else
return "";
}

I can see also storing iter_start and iter_end in my class (didn't think of
that) and setting them to .rbegin() or .rend() in my ResetRead. I can't
see, however, how I could save the iterator in my class between calls of
GetLine so that GetLine knows if it's a forward or reverse iterator. I
played around with some test code just now but still couldn't figure out how
to do it.
 
B

benben

Jim Langston said:
benben said:
Hmm, I don't see any of your reply to Donovan...or is it in another
discussion?

Here is the reply I was talking about:


Donovan Rebbechi said:
[SNIP]
Use templates.
template <typename iter>
void printit ( iter start, iter end )
{
for (; start!=end; ++start)
std::cout << *start << std::endl;
}

or you could simplify by using the standard library, and generalise to
allow other ostreams:

template <typename iter>
void printit (iter start, iter end,std::eek:stream & out = std::cout)
{
std::copy(start,end,std::eek:stream_iterator(out, "\n"));
}


printit (MyVector.begin(),MyVector.end());
printit (MyVector.rbegin(),MyVector.rend());

I can see how either of these methods would work in the example I had given,
simply outputting the entire vector, but the iterator needs to suvive
between calls.

class CChatBuffer
{
private:
std::vector<std::string>::reverse_iterator r_MIter; // Message Iterator
std::vector<std::string> MessageBuffer; // Buffer to hold messages from
server
public:
void ResetRead( bool Reverse );
std::string GetLine();
// Other class variables/methods not shown
};

void CChatBuffer::ResetRead( bool Reverse )
{
if ( ! Reverse )
r_MIter = MessageBuffer.rend(); // Not allowing forward yet, so set to
end
r_MIter = MessageBuffer.rbegin();
}

std::string CChatBuffer::GetLine()
{
if ( r_MIter != MessageBuffer.rend() )
return (*r_MIter++);
else
return "";
}

I can see also storing iter_start and iter_end in my class (didn't think of
that) and setting them to .rbegin() or .rend() in my ResetRead. I can't
see, however, how I could save the iterator in my class between calls of
GetLine so that GetLine knows if it's a forward or reverse iterator. I
played around with some test code just now but still couldn't figure out how
to do it.

Ok, I think I start to understand your problem now.

Basically, you are trying to dynamically reverse the access direction of an
iterator. What is in my mind, is about three different ways of achieving it.
But switching forward and reverse iterator is not a good idea because it is
typically resolved at compile time, not at runtime.

First, std::vector<T>::iterator defines a bidirectional iterator, so you can
use ++ for pointing to the next element, or -- for pointing to the previous
element.

Second, std::vector supports subscription. So using an integer to store the
index can make your class much easier to read and write.

And finally, if applicable, you can merge ResetRead and GetLine, so GetLine
now takes an additional parameter bool reverse.

And worth to note, the iterator returned by calling begin() points to the
first element, end() the ONE PAST the last element; rbegin() the last
element, and rend() the ONE BEFORE the first element.

ben
 
J

Jim Langston

benben said:
Jim Langston said:
benben said:
Please see my reply to Donovan.

Hmm, I don't see any of your reply to Donovan...or is it in another
discussion?

Here is the reply I was talking about:


Donovan Rebbechi said:
[SNIP]
Use templates.
template <typename iter>
void printit ( iter start, iter end )
{
for (; start!=end; ++start)
std::cout << *start << std::endl;
}

or you could simplify by using the standard library, and generalise to
allow other ostreams:

template <typename iter>
void printit (iter start, iter end,std::eek:stream & out = std::cout)
{
std::copy(start,end,std::eek:stream_iterator(out, "\n"));
}


printit (MyVector.begin(),MyVector.end());
printit (MyVector.rbegin(),MyVector.rend());

I can see how either of these methods would work in the example I had given,
simply outputting the entire vector, but the iterator needs to suvive
between calls.

class CChatBuffer
{
private:
std::vector<std::string>::reverse_iterator r_MIter; // Message
Iterator
std::vector<std::string> MessageBuffer; // Buffer to hold messages
from
server
public:
void ResetRead( bool Reverse );
std::string GetLine();
// Other class variables/methods not shown
};

void CChatBuffer::ResetRead( bool Reverse )
{
if ( ! Reverse )
r_MIter = MessageBuffer.rend(); // Not allowing forward yet, so set to
end
r_MIter = MessageBuffer.rbegin();
}

std::string CChatBuffer::GetLine()
{
if ( r_MIter != MessageBuffer.rend() )
return (*r_MIter++);
else
return "";
}

I can see also storing iter_start and iter_end in my class (didn't think of
that) and setting them to .rbegin() or .rend() in my ResetRead. I can't
see, however, how I could save the iterator in my class between calls of
GetLine so that GetLine knows if it's a forward or reverse iterator. I
played around with some test code just now but still couldn't figure out how
to do it.

Ok, I think I start to understand your problem now.

Basically, you are trying to dynamically reverse the access direction of
an
iterator. What is in my mind, is about three different ways of achieving
it.
But switching forward and reverse iterator is not a good idea because it
is
typically resolved at compile time, not at runtime.

First, std::vector<T>::iterator defines a bidirectional iterator, so you
can
use ++ for pointing to the next element, or -- for pointing to the
previous
element.

Yes, I know this, but then I would need an if statement to determine weather
to increment or decrement it. I suppose the trinary function would work
though.

return ((reverse)?*r_MIter++:*r_MIter--));

Hmm.. this might work using the storing of the iterator begin and end in
variables. I'll have to test this.
Second, std::vector supports subscription. So using an integer to store
the
index can make your class much easier to read and write.

Doesnt' solve problem of wanting to use as few conditional statements as
possible.
And finally, if applicable, you can merge ResetRead and GetLine, so
GetLine
now takes an additional parameter bool reverse.

I could do this. But dont see how it will help. GetLine is called line by
line, not for the whole vector. It's being used (currently) to display text
to the screen using directx calls, later to write buffer to text file, or
any other thing my program needs to deal with text buffers.
And worth to note, the iterator returned by calling begin() points to the
first element, end() the ONE PAST the last element; rbegin() the last
element, and rend() the ONE BEFORE the first element.

Noted and understood, which is only reason same logic works for both forward
and reverse iterators as long os you use proper .begin() or .rbegin() etc...

thanks, got some testing to do.
 
M

msalters

Jim Langston schreef:
I have a class I designed that stores text chat in a
std::vector<sd::string>. This class has a few methods to retrieve these
strings to be displayed on the screen.

void ResetRead( bool Reverse, bool wordWrap ); // Resets iterator. We can
ignore wordwrap for now.
std::string GetLine(); // Reads a line using iterator. Increments
Iterator.

Right now it only works in Reverse, since that's the method I used first,
but now I want to do it using a forward iterator. I want to avoid having to
duplicate code, as iterating forward and reverse are exactly the same with 2
diffeernces.

In your code, yes. The generated code is probably slightly more
complex.
E.g. the forward iterator for vector can be a raw pointer, with the
built-in operator++. The reverse_iterator's operator++ definitely can't
be, which means it has to be a class. Of course, this method is usually
inlined so the generated code is the same.

As a result, begin() may return a T* (std::string* in your case)
while rbegin() must return a class.
Can anyone come up with a way to do this without having to declare 2
separate methods for forward reading or reverse reading, or a if block
covering the whole for block?

Refactor the contents of the "whole for block" into a separate
function.
You now have an if-block covering two two-line for-statements (or two
calls to std::for_each( ). Try that too, and see how nice it looks ).

Regards,
Michiel Salters
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top