Input Iterators
In the C++ Standard Template Library, iterators are a
generalization of C's pointers. An input iterator is an iterator
that supports the minimum operations necessary to get input:
- you're allowed to dereference them (so you can read data)
- you're allowed to increment them (so you can get to the next
datapoint).
Notice that this means you're not allowed to do things like write to an
input iterator (if you want to do that, you should be using an output
iterator of some sort...) In many cases, this is all right. A simple
example is if you're reading a set of ints from standard input:
vector<int> V;
copy(istream_iterator<int>(cin), istream_iterator<int>(), back_inserter(V));
In this tiny code snippet, we create a vector of ints where we're
going to store the values we read in. The copy function copies from
a range defined by a pair of input iterators to an output iterator. The
back_inserter function creates an output iterator that writes values to
the end of the supplied vector whenever it's written to.
The nice thing about these abstractions is that you can implement
algorithms that can run on certain sorts of streams, before you know how
the input stream is going to work. Not only that, but you can easily
substitute one iterator for another one that acts the same way.
Compare, for example, the following iterators:
- an iterator that's reading a sound sample from a microphone
- an iterator that's generating a sound sample by modelling a sine wave
- an iterator that's decoding a sound sample from an mp3 file
Why should you, as a programmer, care about where the sound sample is
coming from? If you're programming in the way STL encourages, playing a
sound from any of these sources can be done from the same function.
Using an input iterator
I hate it when people try to describe a concept in programming without giving a concrete example. So, in the following example, I'll show a program to
encode or decode a message using a vigenere square, given the key.
(If you don't have the key, then you'll probably want to look into
breaking the vigenere square.) The way I've written it below, it only
works with lowercase characters (it skips everything else) but by using
the appropriate template instantiation, it'll work on any values.
#include <vector>
#include <iterator>
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
template <class T=unsigned char, T low_value='a', T high_value='z'>
class CodeWheel : public iterator<input_iterator_tag, T> {
// a CodeWheel is an iterator that takes a message iterator and a
// key vector, and encodes the message pointed to by the message
// iterator using a vigenere cipher.
//
// the type T must be a numerical type.
private:
typedef typename vector<T>::iterator T_vector_iterator;
T_vector_iterator message_;
vector<T> key_;
T_vector_iterator key_iter_;
public:
CodeWheel(T_vector_iterator message, vector<T> key) :
message_(message), key_(key),
key_iter_(key_.begin()) {}
CodeWheel& operator++() {
message_++;
key_iter_++;
if (key_iter_ == key_.end())
key_iter_ = key_.begin();
return *this;
}
CodeWheel operator++(int) {
CodeWheel ret_val(*this);
message_++;
key_iter_++;
if (key_iter_ == key_.end())
key_iter_ = key_.begin();
return ret_val;
}
// This is where all the interesting work happens. Note that since
// this is an input iterator, it's returning const T (so we can't
// change the value of what we return.)
const T operator*() {
if (low_value <= (*message_) && (*message_) <= high_value) {
T rotated_value = *message_ + *key_iter_ - (low_value-1);
if (rotated_value > high_value)
return rotated_value - (high_value-low_value+1);
else
return rotated_value;
} else
return (*message_);
}
bool operator==(const CodeWheel& x) {
return x.message_ == message_;
}
bool operator!=(const CodeWheel& x) {
return x.message_ != message_;
}
};
int main(int argc, char *argv[]) {
if (argc < 2) {
cerr << "Usage: " << argv[0] << " key [decode]" << endl
<< "Encodes stdin as a vigenere cipher with the appropriate key."
<< endl
<< "If the second argument exists, decode, otherwise, encode."
<< endl;
return 1;
}
string key_string = string(argv[1]);
vector<unsigned char> key_vector;
for (string::const_iterator i = key_string.begin();
i != key_string.end();
i++) {
if (argc == 2) { // encode
key_vector.push_back(static_cast<unsigned char>(*i));
} else { // decode
// if (*i) is the nth letter of the alphabet, then (*i) - 'a' + 1
// is equal to n. In order to reverse the shift of the nth letter
// (thereby decoding it), we need to subtract n from 'z'.
key_vector.push_back(static_cast<unsigned char>('z' - ((*i) - 'a' + 1)));
}
}
// read in message
vector<unsigned char> message;
unsigned char c;
while (cin.get(static_cast<char>(c)))
message.push_back(tolower(c));
// output decoded message:
for (CodeWheel<> i = CodeWheel<>(message.begin(), key_vector);
i != CodeWheel<>(message.end(), key_vector);
i++)
cout << (*i);
return 0;
}
References:
http://www.sgi.com/tech/stl - the
sgi stl reference