Topic: Suggestion: convergency between standard algorithms and standard streams


Author: kanze@gabi-soft.fr
Date: Mon, 27 Jun 2005 10:23:35 CST
Raw View
"Krzysztof Zelechowski" wrote:
> Uzytkownik <kanze@gabi-soft.fr> napisal w wiadomosci
> news:1119619494.596142.171090@z14g2000cwz.googlegroups.com...
> > "Krzysztof    elechowski" wrote:

> >> istream &istream::operator >>(unary_function<istream &, istream &> &)

> > I'm not even sure what this one should do.

> Apply the function to the istream.

What function?  unary_function only contains some typedef's.
It's not a polymorphic base class.

There is a good argument for something like:

    template< typename Fnc >
    istream&
    operator>>( istream& src, Fnc f )
    {
        return f( src ) ;
    }

The problem that I fear is that this would lead to far too many
ambiguities, or really strange error messages.  It's presence
(now) would certainly break existing code:

    class MyClass
    {
        //   ...
    public:
        operator std::string() const ;
    } ;

    MyClass c ;
    std::cout << c ;

Today, in the absense of an operator<< for MyClass, this calls
the implicit convertion to string, and outputs the string.  If
the proposed template function were present, it would
instantiate the template, and then generate an error in the
instantiation.

Perhaps with the addition of constraints, there would be a means
of ensuring that the function would only be considered if the
object had a member operator() with the correct signature.

--
James Kanze                                           GABI Software
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@gabi-soft.fr
Date: Mon, 27 Jun 2005 10:22:32 CST
Raw View
"Krzysztof Zelechowski" wrote:
> Uzytkownik <kanze@gabi-soft.fr> napisal w wiadomosci
> news:1119619494.596142.171090@z14g2000cwz.googlegroups.com...
> > "Krzysztof    elechowski" wrote:

> >> istream &istream::operator >>(unary_function<istream &, istream &> &)

> > I'm not even sure what this one should do.

> Apply the function to the istream.

> >> are not that important because they can be simulated with

> >> istream &istream::operator >>(streambuf *);

> >> However, there is no easy way to do the same thing with
> >> members of collate.

> > Nor the members of ctype.  It's a real problem, with no good
> > solutions that I know of.  (For starters, however: double
> > all of the functions taking charT* with functions taking
> > std::basic_string<charT>::iterator.)

> Well, because streambuf is a flexible base class and you can
> wrap lots of different things (pipes, sockets, shared memory)
> into it or even use it as a filter on an existing streambuf,

I am aware of that :-), however...

> charT * could be replaced by streambuf *.  It would not
> require a template member function and it would do just as
> well as with istream.

The problem is that streambuf is a "heavy weight" class; it's a
lot of overhead if all you want to do is collate strings.

I agree that we could use some simpler polymorphic "iterators".
I've had a couple of cases where I wanted to use streambuf (or
something like it) as an iterator myself -- a typical example is
parsing, where 1) the functions actually scanning the characters
are complicated enough that they shouldn't be templates (at
least in the absense of export), and 2) advancing the iterator
pretty much implied extracting the character; any characters
handled in the called function should be skipped by the calling
function.  I originally used streambuf* as my "iterator" type;
particularly pratical in my case because I already have an
IteratorInputStreambuf in my tool kit.  But at least with some
of the implementations I use, creating and accessing a streambuf
was a lot of overhead.  I now have a ParserIterator class, which
does the samething (with three virtual functions: get(), peek()
and bump()), but with a lot less overhead when given an
std::string or a char const* (and little additional overhead
when reading from a filebuf).

--
James Kanze                                           GABI Software
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kkz@duch.mimuw.edu.pl (Christopher Conrade Zseleghovski)
Date: Mon, 27 Jun 2005 22:08:52 GMT
Raw View
kanze@gabi-soft.fr wrote:
> "Krzysztof ?elechowski" wrote:
>
>> Given the fact that pointers are replaced by iterators in
>> standard algorithms, it seems natural and reasonable to use
>> iterators in other places as well.  What is the argument
>> against collate::hash(IteratorT, IteratorT)?  Is it because
>> some compilers (notably SCpp) do not handle template member
>> functions well?
>
> More likely it is because the required semantic of
> collate::hash is to call collate::do_hash with the same
> parameters.  And collate::do_hash is a virtual function, so
> cannot be a template.
>
> Of course, logically, one would expect
> std::basic_string<charT>::const_iterator, instead of charT* (or
> possibly both), but a template member is out, unless you can
> think of some way to make the code work without virtual
> functions.
>

It would require do_hash to be a character by character coroutine and to
maintain its internal state between calls.  Because the method is
constant, the internal state can change only via a data reference.  It
is possible to achieve it without dynamic allocation even:

class C { int pi, &ri; C(): ri(pi) {} void f() const { ri = 0 }};

This trick is probably very risky because a facet can be shared;
however, if do_hash gets called only from hash and is left in an initial
state upon exit from hash, it would not do any harm.

I think all facet methods can take iterators if they are reprogrammed
this way.

Christopher

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@gabi-soft.fr
Date: Tue, 28 Jun 2005 09:03:20 CST
Raw View
Christopher Conrade Zseleghovski wrote:
> kanze@gabi-soft.fr wrote:
> > "Krzysztof ?elechowski" wrote:

> >> Given the fact that pointers are replaced by iterators in
> >> standard algorithms, it seems natural and reasonable to use
> >> iterators in other places as well.  What is the argument
> >> against collate::hash(IteratorT, IteratorT)?  Is it because
> >> some compilers (notably SCpp) do not handle template member
> >> functions well?

> > More likely it is because the required semantic of
> > collate::hash is to call collate::do_hash with the same
> > parameters.  And collate::do_hash is a virtual function, so
> > cannot be a template.

> > Of course, logically, one would expect
> > std::basic_string<charT>::const_iterator, instead of charT*
> > (or possibly both), but a template member is out, unless you
> > can think of some way to make the code work without virtual
> > functions.

> It would require do_hash to be a character by character
> coroutine and to maintain its internal state between calls.
> Because the method is constant, the internal state can change
> only via a data reference.  It is possible to achieve it
> without dynamic allocation even:

> class C { int pi, &ri; C(): ri(pi) {} void f() const { ri = 0 }};

> This trick is probably very risky because a facet can be
> shared; however, if do_hash gets called only from hash and is
> left in an initial state upon exit from hash, it would not do
> any harm.

If you think about it, the standard already has a templated
function for this sort of thing: std::accumulate.  My own hash
code generators, but also CRC and MD5, use this -- all my
classes provide is an accumulator, it is std::accumulate which
provides the loop, e.g.:

    Gabi::HashType h = std::accumulate(
                            s.begin(), s.end(),
                            Gabi::HashAccumulator() ) ;

(For accumulators with significant state, such as MD5, this does
have significant performance implications.  One sort of wonders
why accumulate uses accu = accu + *iter rather than accu +=
*iter.)

For hash coding, it would thus be sufficient that the facet
provide an accumulator.

For many of the string versions of the functions in ctype,
find_if and transform provide similar functionality.

I don't see an immediate solution to functions like
ctype::widen, ctype::narrow, collate::compare and
collate::transform, but a priori, it should be possible to
separate the looping from the handling in a similar manner.  (In
theory, widen, narrow and transform could all be handled with
accumulate.  But the copy semantics used by accumulate when the
accumulated value is large become prohibitive.  And of course,
the use of an accumulator fixes the result type, unlike a
function which uses an output iterator.)

> I think all facet methods can take iterators if they are
> reprogrammed this way.

Where there's a will, there's a way:-).

I also think that the current functionality in streambuf could
be broken up.  A very lightweight base class, without any
buffering, from which the current streambuf derives.  Additional
derivations from this class could be used to convert STL
iterators into streambuf-like iterators.  The main problem I see
would be supporting put back (which is not needed when the class
is used as an iterator).  With something like this, the original
posters suggestion would be viable -- do everything with the
lightweight streambuf.

--
James Kanze                                           GABI Software
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@gabi-soft.fr
Date: 24 Jun 2005 14:40:02 GMT
Raw View
"Krzysztof    elechowski" wrote:

> Given the fact that pointers are replaced by iterators in
> standard algorithms, it seems natural and reasonable to use
> iterators in other places as well.  What is the argument
> against collate::hash(IteratorT, IteratorT)?  Is it because
> some compilers (notably SCpp) do not handle template member
> functions well?

More likely it is because the required semantic of
collate::hash is to call collate::do_hash with the same
parameters.  And collate::do_hash is a virtual function, so
cannot be a template.

Of course, logically, one would expect
std::basic_string<charT>::const_iterator, instead of charT* (or
possibly both), but a template member is out, unless you can
think of some way to make the code work without virtual
functions.

> The other examples:

> istream &operator >>(istream &, IteratorT)

This would lead to too many ambiguities, I think.  Suppose my
iterator type overloaded >> itself, for debugging purposes or
persistence.

> istream &istream::operator >>(unary_function<istream &, istream &> &)

I'm not even sure what this one should do.

> are not that important because they can be simulated with

> istream &istream::operator >>(streambuf *);

> However, there is no easy way to do the same thing with
> members of collate.

Nor the members of ctype.  It's a real problem, with no good
solutions that I know of.  (For starters, however: double all of
the functions taking charT* with functions taking
std::basic_string<charT>::iterator.)

--
James Kanze                                           GABI Software
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: yecril@bluebottle.com ("Krzysztof Zelechowski")
Date: Sun, 26 Jun 2005 15:11:40 GMT
Raw View
Uzytkownik <kanze@gabi-soft.fr> napisal w wiadomosci
news:1119619494.596142.171090@z14g2000cwz.googlegroups.com...
> "Krzysztof    elechowski" wrote:
>
>> istream &istream::operator >>(unary_function<istream &, istream &> &)
>
> I'm not even sure what this one should do.

Apply the function to the istream.

>> are not that important because they can be simulated with
>
>> istream &istream::operator >>(streambuf *);
>
>> However, there is no easy way to do the same thing with
>> members of collate.
>
> Nor the members of ctype.  It's a real problem, with no good
> solutions that I know of.  (For starters, however: double all of
> the functions taking charT* with functions taking
> std::basic_string<charT>::iterator.)

Well, because streambuf is a flexible base class and you can wrap lots of
different things (pipes, sockets, shared memory) into it or even use it as a
filter on an existing streambuf, charT * could be replaced by streambuf *.
It would not require a template member function and it would do just as well
as with istream.
Christopher



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: yecril@bluebottle.com ("Krzysztof elechowski")
Date: Thu, 23 Jun 2005 21:01:59 GMT
Raw View
Given the fact that pointers are replaced by iterators in standard
algorithms, it seems natural and reasonable to use iterators in other places
as well.  What is the argument against collate::hash(IteratorT, IteratorT)?
Is it because some compilers (notably SCpp) do not handle template member
functions well?

The other examples:

istream &operator >>(istream &, IteratorT)
istream &istream::operator >>(unary_function<istream &, istream &> &)

are not that important because they can be simulated with

istream &istream::operator >>(streambuf *);

However, there is no easy way to do the same thing with members of collate.

Chris


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]