In article <11**********************@50g2000hsm.googlegroups. com>,
ge*********@gmail.com says...
I really don't understand what exactly is going on here :(
Sorry 'bout that. I undoubtedly should have included quite a bit more
commentary about the code when I posted it.
As I mentioned previously, our first class is a proxy. It's mostly a
stand-in for std::string, but it changes a few specific things about how
a string acts.
The first thing we change is how we read our special string type.
Instead of reading a word at a time, we read an entire line at a time:
std::istream &read(std::istream &is) {
return std::getline(is, data);
}
That's all this does: make it so line.read(some_istream) reads a whole
line of data into a string.
line(std::string const &init) :data(init) {}
This just lets us initialize a line from a string. It just copies the
string into the line's data member (which is a string, so it doesn't
really change anything).
line(){}
This default ctor would have been there by default, but since we added
the ctor that creates a line from a string, we have to explicitly add it
back in or it won't be there -- and we generally need a default ctor for
anything we're going to put into any standard container.
std::ostream &write(std::ostream &os) const {
std::stringstream x(data);
This much takes the line that we read in, and puts it into a
stringstream.
std::copy(std::istream_iterator<std::string>(x),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(os, " "));
Now we take our stringstream, read out one word at a time, and write it
out to a stream. It's roughly equivalent to code like:
std::string temp;
while ( x >temp) {
os << temp;
os << " ";
}
The big difference is that std::copy can take essentially any pait of
iterators as input, and write those items to essentially any iterator
for the output.
operator std::string() const { return data; }
This is a crucial part of allowing our 'line' class to act like a
string: we allow it to be converted to a string at any time. This
becomes crucial in how we use it below.
std::istream &operator>>(std::istream &is, line &d) {
return d.read(is);
}
std::ostream &operator<<(std::ostream &os, line const &d) {
return d.write(os);
}
These two functions just overload operator>and << to use our member
funtions. They allow code to read a line with code like:
std::cin >some_line;
and it reads an entire line of data instead of just one word.
int main(int argc, char **argv) {
std::vector<std::stringsvec;
std::ifstream in(argv[1]);
This just takes the first name that was passed on the command line and
opens it as a file.
std::copy(std::istream_iterator<line>(in),
std::istream_iterator<line>(),
std::back_inserter(svec));
Now, here's were we get a little bit tricky: even though we have a
vector of string, we use an istream_iterator<line>. That means the
istream_iterator uses the operator>we defined above (which, in turn,
calls line::read) to read in each item from the stream. It then uses
push_back to add that item to svec. This, in turn, uses the operator
string we defined above, so it ends up using the entire string that we
read in (i.e. the entire line we read with readline in line.read().
std::copy(svec.begin(), svec.end(),
std::ostream_iterator<line>(std::cout, " "));
Here we reverse things: we copy the contents of svec out to an
ostream_iterator for std::cout -- but it's an ostream_iterator<line>, so
when it writes each complete line out to std::cout, it does so using
line.write. That, in turn, creates a stringstream and writes out each
word in the line individually instead of writing out the whole line.
Our proxy (line) is pretty close to what most people initially think
they will do with inheritance -- create a new class that acts like the
existing class (string, in this case) except for changing a couple of
specific operations (input and output, in this case). The ctor and cast
operator we provided in line allow us to freely convert between this
type and string whenever we want/need to. In this case, we read in
lines, convert and store them as strings, then convert them back to
lines as we write them out.
Given that we never do anything _else_ with the strings, we could
eliminate all the conversions by creating svec as a vector<lineinstead
of a vector<string>. This, however, would have a substantial
disadvantage if we did much of anything else with our strings -- we'd
have to explicitly provide access to any other string operations, and as
we all know, there are a LOT of string operations. Providing the
conversions allows us to support all the string operations with very
little work.
--
Later,
Jerry.
The universe is a figment of its own imagination.