James said:
[...]
3. Use ofstream to create the appropriate file. Use "write"
member function to write the bytes to the file. If you have
only five files, you can use a switch statement
Or a container of file-writer objects, indexed by ID.
std::size_t const id_column = 5;
for (std::string line; getline(input, line)
{
writers[line.at(id_column)].write(line);
}
Just curious, but why at in one case, an [] in the other? (I've
never found a real use for container<>::at.)
I use at() with standard library containers when I want
bounds-checking. (String may be bounds-checked anyway, but I
want to be clear.). The container of writers is meant to be a
custom container, so [] can perform bounds-checking directly.
OK. I didn't catch the difference. (Of course, at() guarantees
that you'll do the wrong thing in case of a bounds error, so I'm
not sure it's a valid alternative in most cases.)
Exactly. That's why I'm throwing an exception. You and I
have already had this debate, so suffice it to say that we
disagree on the best policy here. (Actually, an Andrei-style
policy-based input check would probably be preferable to a
hard-coded exception.)
In this particular case, I'm not arguing for or against the
exception. What I'm arguing is that we don't know what he's
supposed to do, and that we can't really worry about how to
write the code until we know this.
Ugh.
There's just no need for explicit pointers here,
especially pointers whose type is hard-coded to be raw, nor is
there any need for the macro.
You'd prefer some sort of Fallible? In this case, I'm dealing
with objects which have identity (ostream), and I have to deal
with the possibility of there being no appropriate object. And
I'm guessing that the correct behavior if the line doesn't have
at least five bytes, or the fifth byte is some other value than
the ones specified, is to do nothing. That is, of course, just
a guess; I suspect that it's probably closer to what is wanted
than an exception, but without more information, I really don't
know.
As for the macro, the only macro in the code is NULL, and that's
not mine---I wouldn't have defined it as a macro either, if I'd
have been designing C. (As soon as I can count on C++0x, I'll
replace it with nullptr.)
Whether to use <<, std:
stream::write, or something else is
probably best encapsulated in a policy, as well; hence the
writer objects in my example.
We're talking about a simple, one of application. Any use of
policies or templates here is just unnecessary complication.
In real code, I'd have used my own custom line type, rather
than std::string.
Probably. I have a type Line that I generally use for such
things. In which case, of course, I'd use the << (since it's
typed), without the trailing "<< '\n'" (since the class will
take care of it).
The point of the algorithm is to be an algorithm, not to
hard-code a bunch of low-level details that we should be able
to vary independently of each other.
We're not designing a general library.
To each his own, I guess.
Yes. There are two valid solutions here. In some ways, I
prefer the null stream version; in others, the version above.
Given the apparent lack of experience of the original poster, I
think that the solution using a null pointer is probably easier
for him to understand and implement. In my own work, I'd
probably use the null stream (especially since I have one ready
made in my library).
But you just said it. What happens if a sixth type comes along,
and it isn't consecutive?
Of course, given that there are a maximum of 256 values, the
easiest solution might simply be:
std:
stream* files[ UCHAR_MAX + 1 ] :
Or tr1::array, if you have that available. (Or in my case,
Gabi::ArrayOf< std:
stream*, CHAR_MIN, CHAR_MAX + 1 >, so you
can index directly with the char, without worrying about
converting it to unsigned char. But unlike C style arrays or
tr1::array, ArrayOf doesn't support static initialization.)