|
Boost : |
From: Terje Slettebø (tslettebo_at_[hidden])
Date: 2003-02-07 10:27:57
Regarding this project. I've got doubts about the viability of it.
One thing is to create something useful. Another thing is to create
something useful as a _library_ component. As has been noted regarding
application and library development, application development and library
development is typically quite different. With an application, you typically
have quite specific requirements. With a library component, however, it's
about anticipating future use. Or making something general enough to be
useful as a library component.
I've thought a lot about this, these days, regarding the composite
operators. Trying to find the essential abstraction. Clearly, they are about
passing composite objects (such as arrays, standard containers, etc.) to a
stream, so that is the abstraction. This is also something which isn't
covered much in current libraries. The question is if there's enough
commonality in this abstraction, to warrant a library implementation.
For the composite operators to be genuinely useful in a broader context, I
think it's important that they do one thing, and one thing well.
The challenge is to make something general enough, yet reasonably easy to
use. Easy things should be easy, and hard things should be possible.
My concern is, for example, for outputting a
std::vector<std::pair<int,double> >:
typedef std::pair<int, double> Map;
typedef std::vector<Map> MapList;
MapList list;
You may do this:
for(MapList::const_iterator i=list.begin(); i!=list.end(); ++i)
std::cout << '[' << i->first << ',' << i->second << "]\n";
Or you may do:
std::cout << composite_format<Map>("[", "]", "\n") << list;
The question is, is it worth it?
Of course, having defaults, the latter may be reduced to:
std::cout << list;
It also comes back to the question of commonality and variability.
Currently, it uses static strings for start, end, and element delimiter.
Anything else, and you need to use something else.
I was first thinking of generalising it to any string type (rather than
std::basic_string), but I've found that even that doesn't go far enough, if
you want to change it radically. I've then been thinking of the possibility
of passing a functor, instead, to the manipulator. The manipulator could
then invoke this functor for each element to be output. This would enable
things like listing element number in the output, by passing in an
appropriate functor for this. The current semantics might be catered for by
another functor, and overloaded constructors for this case may also be
provided for convenience.
BGL is similar, in that graphs are also composite objects. It uses
"visitors", which are functors with multiple member functions (not just
overloaded operator()), which are called at specific points in an algorithm.
The same could be done with the composite operators, calling the functor at
start, end and for each element.
However, as the algorithm in this case is just iterating over a sequence,
calling a functor or outputting each element, it's more or less the same as
a for-loop, or for_each.
It's also a question of what design space we are targeting: Originally,
Vladimir Prus's suggestion was for something that could help with debug
output. It's a question whether or not this can be made general enough to be
useful beyond that, and if that is even desirable, concerning things like
usability.
Regards,
Terje
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk