J
Jonathan Mcdougall
Patrick said:On Mon, 14 Nov 2005, Jonathan Mcdougall wrote:
Hi Jonathan,
Basically yes it is a wrapper around standard container std::vector with
different "native" types (unsigned char, unsigned short and unsigned
Yes it might be used in different contexts in the sense that the input
may have different formats. As you wrote below, there might be
different separator.
I want to be able to input/import/set keys of different type to be used
in the encryption engine class for testing/benchmarking.
I am not sure to understand What do you mean by a state?
A object may have a state (member variables), but a namespace cannot
(well it can, but with namespace-global variables). This means you can
have two instances of a class (two objects), and by changing their
values, the member functions called on them will produce different
results. So if you need the parser to have a state, you need a class
and one (a stateful singleton) or several objects. If you don't, you
either need a stateless singleton (all-statics class) or simple free
functions in a namespace.
Wouldn't it make sense to define a structure
template<typename vec_type>
struct input_vec
{
// default is no separator
input_vec(const std::string sep=std::string()) : sep_(sep) {}
vec_type vec_;
std::string sep_;
};
and then have a single stream insertion operator for struct input_vec that
would use the member input_vec::sep_ ?
Oh well these were just examples and my examples are always scrap. I
never was a good teacher.
namespace parser
{
template <class C>
istream &separated_by_something(std::istream &i, C &c, const
std::string &sep="\n");
template <class C>
istream &fixed_size(std::istream &i, C &c, std::size_t size);
template <class C, class Decoder>
istream &encrypted_fixed_size(std::istream &i, C &c, Decoder &d,
std:size_t size);
}
You know, something like that. But if, in your case, they can all be
factored in one function (or a class), go for it.
template <typename vec:type>
std::istream & operator>>(std::istream & is, input_vec<vec_type> & v);
istream cin;
input_vec<Vec> in(",");
cin >> in;
Vec v(in.vec_);
Or perhaps better use a single class for input_vec and output_vec with
different constructors?
You need to understand here that was is "better" for me will not
necessarily be "better" for you. I don't know what you have to do, I
don't understand exactly what you need. You are asking a kind of
question that is very hard to answer: a "is this good design" question.
Answering this kind of question over the net, without knowing anything
about your job, is very difficult.
[I am not telling you to stop asking questions, I am saying getting a
definite answer will be very difficult to obtain.]
Now, depending on what you want or need, and depending on the size of
the classes and also depending of the logic involved in reading and
writing, you may want to separate these two classes. If the code is
small, and it makes sense to bundle input and output together, go for
it. If you find yourself trying to manage a mammoth class which does
two completly unrelated things, you should separate them.
Normally, operators << and >> are used when the receiver does not need
to be parametrized. Something like
input_vec<Vec> in(",");
cin >> in;
is not common in my opinion (but I may be wrong). Usually, you
parametrize the stream, not the object. When the output or input
depends on the state of an object, I usually use member functions:
input_vec<Vec> in;
in.read_from(std::cin, ",");
or a user-defined stream
my_stream s(std::cin, ",");
input_vec<Vec> v;
s >> v;
You don't usually "prepare" the object to be streamed, because you want
to separate the state from the input/output.
// wrong (IMO)
input_vec<Vec> in;
in.separator(",");
in.encryption(..);
in.bla();
std::cin >> in;
But that's getting a matter of taste more than anything. Since
input_vec is only a wrapper, it could make sense, I don't know. I just
feel it doesn't look "right". Here's what I would do, based on what I
understand:
class C;
class key_input
{
public:
key_input(std::istream &in, const std::string &sep)
: in_(in), sep_(sep)
{
}
friend std::istream &operator>>(key_input &key, C &c)
{
// read from in_ depending on sep_ and possibly other parameters
// put values in c
}
private:
std::istream &in_;
std::string sep_;
};
class C
{
public:
typedef std::vector<whatever> Container;
Container cont;
};
std::istream &operator>>(std::istream &in, C &c)
{
// read some general values into c (such as the number of keys, a
date or something)
// delegate to key_input, the separator may be defined elsewhere,
such as in C
key_input kin(in, ",");
kin >> c;
return in;
}
int main()
{
C c;
std::cin >> c;
}
So that's just a matter of
1) separating input/output from the class
2) having different functions (be they operators or not) to do
different jobs
Modularity, one function, one responsability, you know, the usual talk.
Also if I want to have both insertion operator for std::istream and
std::istringstream, do I need to actually have 2 sets of
definition/declaration for each of them?
No, std::[i/o]stringstream is derived from std::[i/o]stream, so passing
by reference will be ok. Actually, to be more generic, you could pass
basic_istream and basic_ostream around and add template parameters for
the character type.
Hope this helps,
Jonathan