Topic: Output iterators


Author: John_Maddock@compuserve.com (John Maddock)
Date: 1999/06/10
Raw View
>I can't see how an unnamed temporary could be any different from a
>named variable.
>
>  *a = t; ++ a;
>  *X(a) = t; ++ a;
>  X b(a); *b = t; ++ a; // ?
>  X b(a); *b = t; ++ b; a = b;
>
>The * and = for lines 1 and 3 must be the same.  The * and = for
>line 2 must act like line 1.  The copy ctor for lines 2 and 3 is
>the same.  I have concluded that all copies are allowed to act
>the same and must in these cases.
>
>  X b(a); *b = t; ++ b;
>might be the same as line 1 but would not be for pointers and should
>not be used in an algorithm.
>
>All four lines work for pointers and I think the fourth one is the
>one that you were concerned about.

Yes I think that that is correct, examples 1 to 4 look right, and
would seem to work both for stateful and stateless iterators, case 5
on the other hand is not allowed as you rightly say.

John Maddock
http://ourworld.compuserve.com/homepages/John_Maddock/


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1999/06/05
Raw View
John_Maddock@compuserve.com (John Maddock) wrote:


: >Pass by value is effectively pass by reference

: Sorry but I'm going to have to disagree with that one, and that really
: was the crux of my whole post.  Table 73 (with the typo) says that
: output to a copy is the same as output to the original - this is the
: post condition for the copy constructor - and IMO should be a post
: condition for the assignment operator also, but thats another issue.

: The thing is I think that your reading more into this than there is,

Well, you and James Kuyper had me convinced of that.  On a bit more
thought, my first conclusion may have been correct.  Here is a bunch
of code.  The important part is that the OSI has unshared state, and
my_copy uses the corrected table 73 entry.  The shared state version
posted to clc++m works the same as the std::ostream_iterator.  I think
that the table 73 entry requires all copies of output iterators to
share state or have none.  Whether it should is debatable.

Where are the experts?

John

#include <iostream>
#include <fstream>
#include <string>
template <class T>
struct BufferedOSI {
    typedef output_iterator_tag iterator_category;
    typedef void value_type;
    typedef void distance_type;
    struct Proxy {
        Proxy(BufferedOSI& i) : it(i) { }
        ~Proxy () { ++ it; }
        operator BufferedOSI const& () { return it; }
        BufferedOSI& operator* () { return it; }
        BufferedOSI& it;
        };
    BufferedOSI (ostream& o, char const* s = 0) : os(&o), sep(s) { }
    void operator= (T const& d) { data = d; }
    BufferedOSI (ostream& o, char const* s = 0) : os(&o), sep(s) { }
    void operator= (T const& d) { data = d; }
    BufferedOSI& operator++ () {
        *os << data;
        if (sep)
            *os << sep;
        return *this;
        }
    Proxy operator++ (int) { return *this; }
    BufferedOSI& operator* () { return *this; }
    ostream* os;
    char const* sep;
    T data;
    };
struct Line {
    Line (char const* v = "Using a broken OSI") : data(v) { }
    string data;
    };
ostream& operator<< (ostream& os, Line const& l) {
    return os << l.data;
    }
template <typename II, typename OI>
OI std_copy (II first, II last, OI result) {
    for (; first != last; ++ first, ++ result)
        *result = *first;
    return result;
    }
template <typename II, typename OI>
OI my_copy (II first, II last, OI result) {
    for (; first != last; ++ first, ++ result)
        *OI(result) = *first; // Note that this is equivalent!
                       // Table 73 says it is the same as above.
                       // Also note that it is the same for pointers
                       // Maybe Stepanov knew what he said ;-)
    return result;
    }
int main () {
    Line data[] = { "It is a wonderful", "world when you",
            "understand the", "standard!" };
    my_copy(data, data + 4, ostream_iterator<Line>(cout, "\n"));
    my_copy(data, data + 4, BufferedOSI<Line>(cout, "\n"));
    }

The output is:

It is a wonderful
world when you
understand the
standard!
Using a broken OSI
Using a broken OSI
Using a broken OSI
Using a broken OSI




[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: John_Maddock@compuserve.com (John Maddock)
Date: 1999/06/06
Raw View
>I think we all agree that the only way to use an output iterator is
>*it = v, ++it; *it = v, it++; or *it++ = v;.  Copy ctor and assignment
>work as usual for any object.  Doing anything with them other than
>passing parameters and return values to keep only one output iterator
>active in a range has unknown behavior.

All in agreement, must be a first around here :-)

>Bottom line, what does that first entry in the table mean in its
>current condition or with the typo corrected?  Nothing?  Lawyers?

I think you are right here, the second entry covers copy constructor
and assignment, so taking the first literally as:

*X(a) = t; // is equivalent to
*a = t;

then it would seem to be saying that output to a *temporary* copy is
the same as output to the original, which would imply that:

*X(a) = t;
++a;

is a legal sequence - in other words that output occurs on assignment
(at least in effect) - this would be consistent with regular pointers
(for example), but as usual I may be falling into the trap of reading
more into this than there is.


John Maddock
http://ourworld.compuserve.com/homepages/John_Maddock/
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jcoffin@taeus.com (Jerry Coffin)
Date: 1999/06/08
Raw View
In article <Ok%53.2635$OL.160922@newscene.newscene.com>,
jpotter@falcon.lhup.edu says...

[... posted the following sample: ]

I think anything that compiles this as-is is pretty badly broken in
many respects.

> #include <iostream>
> #include <fstream>
> #include <string>
> template <class T>
> struct BufferedOSI {
>     typedef output_iterator_tag iterator_category;

Apparently, output_iterator_tag is something declared in one of the
headers you've included, at least in the implementation you're using.
This typedef doesn't appear to accomplish anything, and will fail on
most implementations.

[ ... ]

>     BufferedOSI (ostream& o, char const* s = 0) : os(&o), sep(s) { }
>     void operator= (T const& d) { data = d; }
>     BufferedOSI (ostream& o, char const* s = 0) : os(&o), sep(s) { }
>     void operator= (T const& d) { data = d; }

This appears to violate the one-definition rule, twice.  Unless the
duplication happened in the course of posting, your compiler appears
to be badly broken if it accepts this.

[ ... ]

> int main () {
>    Line data[] = { "It is a wonderful", "world when you",
>            "understand the", "standard!" };
>    my_copy(data, data + 4, ostream_iterator<Line>(cout, "\n"));

Here you're using ostream_iterator, but you don't have an #include
<iterator>, so it shouldn't be defined.

>    my_copy(data, data + 4, BufferedOSI<Line>(cout, "\n"));

And, throughout, you haven't used `std::' where necessary, nor have
you included any "using" so you wouldn't have to qualify the names.

If your implementation is badly enough broken to compile the code at
all, I guess it comes as little surprise that it doesn't work
correctly when it's done.  It looks to me like you've got reason for
more complaints about the implementation than you realized...
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1999/06/08
Raw View
John_Maddock@compuserve.com (John Maddock) wrote:

: I think you are right here, the second entry covers copy constructor
: and assignment, so taking the first literally as:

: *X(a) = t; // is equivalent to
: *a = t;

: then it would seem to be saying that output to a *temporary* copy is
: the same as output to the original, which would imply that:

: *X(a) = t;
: ++a;

: is a legal sequence - in other words that output occurs on assignment
: (at least in effect) - this would be consistent with regular pointers
: (for example), but as usual I may be falling into the trap of reading
: more into this than there is.

I can't see how an unnamed temporary could be any different from a
named variable.

  *a = t; ++ a;
  *X(a) = t; ++ a;
  X b(a); *b = t; ++ a; // ?
  X b(a); *b = t; ++ b; a = b;

The * and = for lines 1 and 3 must be the same.  The * and = for
line 2 must act like line 1.  The copy ctor for lines 2 and 3 is
the same.  I have concluded that all copies are allowed to act
the same and must in these cases.

  X b(a); *b = t; ++ b;
might be the same as line 1 but would not be for pointers and should
not be used in an algorithm.

All four lines work for pointers and I think the fourth one is the
one that you were concerned about.

   OI recur (OI it, otherstuff s) {
      if (something(s))
         it = recur(it , f(s));
      else {
         *it = g(s);
         ++ it;
         }
      return it;
      }

Looks good to me.

I think that the BufferedOSI that I posted here does not meet the
requirements.  I think that the BufferedOSI that I posted to clc++m
does meet the requirements.  There are algorithms which would work
one way for the standard OSI and another way for the BOSI and
pointers.  They should not be written with a claim that they use
an output iterator.

Has anyone filed a DR on the table entry?  It is not on the public
issue list, but it could be an issue in waiting.  (mod?)

John



[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1999/06/09
Raw View
jcoffin@taeus.com (Jerry Coffin) wrote:

: In article <Ok%53.2635$OL.160922@newscene.newscene.com>,
: jpotter@falcon.lhup.edu says...

: > #include <iostream>
: > #include <fstream>
: > #include <string>
: > template <class T>
: > struct BufferedOSI {
: >     typedef output_iterator_tag iterator_category;

: Apparently, output_iterator_tag is something declared in one of the
: headers you've included, at least in the implementation you're using.
: This typedef doesn't appear to accomplish anything, and will fail on
: most implementations.

See 24.3.3/1 for the five standard iterator tags.  Fairly new addition
and your implementations may not have them yet.

: [ ... ]

: >     BufferedOSI (ostream& o, char const* s = 0) : os(&o), sep(s) { }
: >     void operator= (T const& d) { data = d; }
: >     BufferedOSI (ostream& o, char const* s = 0) : os(&o), sep(s) { }
: >     void operator= (T const& d) { data = d; }

: This appears to violate the one-definition rule, twice.  Unless the
: duplication happened in the course of posting, your compiler appears
: to be badly broken if it accepts this.

Yes, it is a copy/paste error.

: > int main () {
: >    Line data[] = { "It is a wonderful", "world when you",
: >            "understand the", "standard!" };
: >    my_copy(data, data + 4, ostream_iterator<Line>(cout, "\n"));

: Here you're using ostream_iterator, but you don't have an #include
: <iterator>, so it shouldn't be defined.

Lazy.  <iostream> included it.  That is also where the iterator_tag
came from.

: >    my_copy(data, data + 4, BufferedOSI<Line>(cout, "\n"));

: And, throughout, you haven't used `std::' where necessary, nor have
: you included any "using" so you wouldn't have to qualify the names.

Lazy, my implementation is not yet up to requiring namespace std.  It
will accept it, but I only put it in real stuff, not toys like this.

Sorry for the confusion.  Please look again, I think it proves my point.

John
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1999/06/03
Raw View
John Potter wrote:
....
[Re: Table 71 requirements on copy construction of output iterators]

> The typo seemed to say that it = v was required and the fact that it
> worked didn't help.  Without the typo, it requires that all copies
> act the same.  Pass by value effectively becomes pass by reference.

They must act the same in only that one specific way, and that way
applies equally well to ordinary pointers (assuming I'm right about the
typo). Copies of output iterators needn't remain equivalent to the
original after incrementation, and going out of your way to ensure that
yours do is wasted effort.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: John_Maddock@compuserve.com (John Maddock)
Date: 1999/06/03
Raw View
>No need :)  Pass by value is effectively pass by reference and it has
>been updated.  You can still do this to make your algorithm work with
>the other iterators as long as you have not violated the output
>iterator pattern.  Since the real output iterators have no state
>information about position, the assignment will be a nop for them.

Sorry but I'm going to have to disagree with that one, and that really
was the crux of my whole post.  Table 73 (with the typo) says that
output to a copy is the same as output to the original - this is the
post condition for the copy constructor - and IMO should be a post
condition for the assignment operator also, but thats another issue.

The thing is I think that your reading more into this than there is,
consider:

output_iterator_type i;
output_iterator_type j(i);
// i and j are now equivalent, this holds
// for all iterator types including for example
// forward iterators and above, assignment to one
// is equivalent to assignment to the other,
// but:

*j = val;
++j;
// oops i and j are no longer equivalent
// only an assignment i = j can restore that
// even though for some output iterator types
// this may in fact not be required, it *may* be
// necessary for others, and will be necessary
// if the algorithm was passed a forward iterator
// or above.

I hope this makes sence: just because all the output iterators you
have seen are stateless, this does not have to be the case, it would
be legal IMO for an output iterator to be stateful and maintain a
stream position for example, and still be in compliance with the
standards requirements.  In fact this is exactly the case that ensues
when using a forward iterator  as an output iterator.

To put this another way, the standard does not say that if

x == a

then

x == ++a

which seems to be what you are implying.


John Maddock
http://ourworld.compuserve.com/homepages/John_Maddock/


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1999/06/04
Raw View
John_Maddock@compuserve.com (John Maddock) wrote:

: >No need :)  Pass by value is effectively pass by reference and it has
: >been updated.  You can still do this to make your algorithm work with
: >the other iterators as long as you have not violated the output
: >iterator pattern.  Since the real output iterators have no state
: >information about position, the assignment will be a nop for them.

: Sorry but I'm going to have to disagree with that one,

You may be right.

: and that really
: was the crux of my whole post.  Table 73 (with the typo) says that

I assume you mean with the typo corrected.

: output to a copy is the same as output to the original - this is the
: post condition for the copy constructor - and IMO should be a post
: condition for the assignment operator also, but thats another issue.

After Kuyper's article, I think that both copy ctor and assignment are
covered normally without that entry.  What does it mean?

: The thing is I think that your reading more into this than there is,
: consider:

: output_iterator_type i;
: output_iterator_type j(i);
: // i and j are now equivalent, this holds
: // for all iterator types including for example
: // forward iterators and above, assignment to one
: // is equivalent to assignment to the other,
: // but:

: *j = val;
: ++j;
: // oops i and j are no longer equivalent
: // only an assignment i = j can restore that
: // even though for some output iterator types
: // this may in fact not be required, it *may* be
: // necessary for others, and will be necessary
: // if the algorithm was passed a forward iterator
: // or above.

Yes.  Since there are no semantics given for any of the operations
++j could change j without changing i or change both or do nothing.
*j = val is also not required to do anything.  See my BlackHole
iterator.

: I hope this makes sence: just because all the output iterators you
: have seen are stateless, this does not have to be the case, it would
: be legal IMO for an output iterator to be stateful and maintain a
: stream position for example, and still be in compliance with the
: standards requirements.  In fact this is exactly the case that ensues
: when using a forward iterator  as an output iterator.

Yes, my BufferedOSI was stateful but still kept all copies the same.
I have redone it to remove that part and think it still meets the
requirements.  Using the new version, it is possible to write an
algorithm which copies pairs of inputs in reverse order.  Using the
usual ostream_iterator, the pairs do not get reversed.  Just one
more example of doing things which are not assured by the standard.

: To put this another way, the standard does not say that if

: x == a

: then

: x == ++a

: which seems to be what you are implying.

Not at all.  Output iterators are not equality comparable <g>.  If
I had made that statement, it would be vacuously true.  There is
no way to tell.

I think we all agree that the only way to use an output iterator is
*it = v, ++it; *it = v, it++; or *it++ = v;.  Copy ctor and assignment
work as usual for any object.  Doing anything with them other than
passing parameters and return values to keep only one output iterator
active in a range has unknown behavior.

Bottom line, what does that first entry in the table mean in its
current condition or with the typo corrected?  Nothing?  Lawyers?

John
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1999/06/04
Raw View
James Kuyper <kuyper@wizard.net> wrote:

: John Potter wrote:
: ....
: [Re: Table 71 requirements on copy construction of output iterators]

: > The typo seemed to say that it = v was required and the fact that it
: > worked didn't help.  Without the typo, it requires that all copies
: > act the same.  Pass by value effectively becomes pass by reference.

: They must act the same in only that one specific way, and that way
: applies equally well to ordinary pointers (assuming I'm right about the
: typo). Copies of output iterators needn't remain equivalent to the
: original after incrementation, and going out of your way to ensure that
: yours do is wasted effort.

Hum.  You may have a point.  With the typo, it is clearly wrong.  With
your correction (matches the original HP documentation) it has no
meaning.  See followup to Maddock.

John
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1999/06/01
Raw View
John_Maddock@compuserve.com (John Maddock) wrote:

: I have followed your thead elsewhere and was unsure whether to post
: here or there.

This is a good place for your post.  I was trying to sort out the
meaning from a programming point and felt that clc++m was better
for that.  I am now willing to submit my conclusions to the scrutiny
of the language lawyers here.

: I don't think that you are correct that all instances of an output
: iterator must be equivalent - even though its true in this one case -

Not sure what "this one case" means.

The first line of table 73 which should say "*a = t is equivalent to
*X(a) = t" sure seems to say that.  The implementations of
ostream_iterator and the various insert_iterators do everything on
assignment with the other operators being nops that satisfy the
requirements.

: since the standard makes it clear that any algorithm that requires an
: output iterator, must also work with input, bidirectional and random
                                      forward
: access iterators (24.1 part 3).

That was my problem.  What it really says is that any algorithm which
is written to work with *any* output iterator will work with forward
and up iterators.  An output range can only support one iterator; so,
all copies act the same.  The only action which will work with all
output iterators is *a = t, ++a(a++); or *a++ = t;.  When these are
the only operations on the iterator, of course, the algorithm will
also work with the other iterators.

: In other words it must be legal (if
: unadvisable) to use char* as an output iterator

Not _as_, but _in_ any algorithm which works for all conceivable
output iterators.  A forward iterator ISA output iterator because
it may be used anyplace that all output iterators may be used not
because it acts anything like an output iterator.  The requirements
(really lack of requirements) for iterators place demands on the
algorithms which advertise that they will work with a certain kind
of iterator.

: - hence my need for
: ostream_iterator to be assignable as my algorithm is recursive (yes I
: know that serves me right) and therefore returns the new iterator
: position in order to update the iterator position in the caller.

No need :)  Pass by value is effectively pass by reference and it has
been updated.  You can still do this to make your algorithm work with
the other iterators as long as you have not violated the output
iterator pattern.  Since the real output iterators have no state
information about position, the assignment will be a nop for them.

: BTW table 73 (24.1.2) seems to imply that assignment to a copy is the
: same as assignment to the original, this would not be true once one or
: other has been incremented.

Since increment is a nop in the usual implementation, you may increment
any and all copies as many times as you like and do nothing to change
the fact that assignment to any copy acts the same.  Note that I took
special care in my BufferedOSI to be sure that all copies act alike.

: >The requirements on an algorithm which uses output iterators are that
: >it must not do anything that depends on behavior other than what is
: >assured by output iterator.  There is almost nothing assured by
: >output iterator.

: I'm beginning to see that....

Matt said it clearly in his book.  It took me a lot of work to finally
accept that he is correct.

The standard does not require output iterators to be unbuffered;
however, it allows it.  When there is no buffer, assignment increments
and increment must be a nop.

John
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: John_Maddock@compuserve.com (John Maddock)
Date: 1999/06/01
Raw View
>It's more of an evolved requirement. In the initial STL spec, output iterators
>did not have to be assignable. In fact, certain output iterators (such as
>insert_iterator, as I recall) stored references and hence couldn't possibly
>be assignable. Then along came ostreambuf_iterators which pretty much
>had to be assignable, lest library code be even more unreadable. So
>WG21 agreed to change the requirement on output iterators. They also
>eliminated any stored references from output iterators in the library so
>they could henceforth be assignable.

OK that's fair enough, evidently my compiler vendor hasn't caught up
yet (or more likely missed that point - they're pretty up to date on
everything else), thanks for the clarification.

John Maddock
http://ourworld.compuserve.com/homepages/John_Maddock/


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: John_Maddock@compuserve.com (John Maddock)
Date: 1999/06/01
Raw View
>Why inadvisable? That's the ordinary method to be used for storing

buffer overrun, unless you know in advance how much text (or whatever)
will be produced.  For the algorithm I'm working on, there is no way
to work out in advance how much text will be produced - other than
doing a dummy run first - hence the recomendation to output to
ostream_iterator or back_insert_iterator or something.

>I just noticed what may be a typo in that table. It says to "a = t is
>equivalent to X(a) = t", where 'X' is the iterator type, 'a' is a
>iterator of that type, and 't' is an instance of the value type.
>Shouldn't that be "*a = t is the same as *X(a) = t" ?

I think so, although for current implimentations of most output
iterators the latter also happens to be true (because for simplicity
*iterator returns *this) which confuses things further.


John Maddock
http://ourworld.compuserve.com/homepages/John_Maddock/


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: James Kuyper <kuyper@wizard.net>
Date: 1999/06/02
Raw View
John Maddock wrote:

[Re: using 'char *' as an output iterator:]
> >Why inadvisable? That's the ordinary method to be used for storing
>
> buffer overrun, unless you know in advance how much text (or whatever)

I agree that you shouldn't use it if you don't know an upper limit to
the amount of text that will be produced. However, it's not uncommon to
have precisely that information, and it's not inadvisable to use 'char
*' as an output iterator when you do.

> >I just noticed what may be a typo in that table. It says to "a = t is
> >equivalent to X(a) = t", where 'X' is the iterator type, 'a' is a
> >iterator of that type, and 't' is an instance of the value type.
> >Shouldn't that be "*a = t is the same as *X(a) = t" ?
>
> I think so, although for current implimentations of most output
> iterators the latter also happens to be true (because for simplicity

But OutputIterators aren't required to support a=t, so requiring it to
have the same effects as X(a)=t would be more than a little bizarre.
They are required to support *a=t, which is why I presume there's a
typo.

> *iterator returns *this) which confuses things further.

Are you implying that they overload operator=(), or define a conversion
ctor, or define a cast conversion operator? Why? Without one of those
three, I think such code would ordinarily be illegal.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1999/06/02
Raw View
James Kuyper <kuyper@wizard.net> wrote:

: John Maddock wrote:

: > I think so, although for current implimentations of most output
: > iterators the latter also happens to be true (because for simplicity

: But OutputIterators aren't required to support a=t, so requiring it to
: have the same effects as X(a)=t would be more than a little bizarre.
: They are required to support *a=t, which is why I presume there's a
: typo.

: > *iterator returns *this) which confuses things further.

: Are you implying that they overload operator=(), or define a conversion
: ctor, or define a cast conversion operator? Why? Without one of those
: three, I think such code would ordinarily be illegal.

A typical output_iterator using a self proxy:

  Something* c;
  OI& operator=(const T& v) { /* *c << v; c->push_back(v); whatever */
                              return *this; }
  OI& operator*()           { return *this; }
  OI& operator++()          { return *this; }
  OI& operator++(int)       { return *this; }

The overloaded operator= does everything.  The generated copy
operator= and ctor do shallow copy making all copies the same.  The
other functions satisfy the requirements of the standard and it is
pure simplicity.  It allows writing lots of silly things but also
supports writing the things which make sense.

  *it = v; ++it; // algorithms must use one of these two
  *it++ = v;

  (it = it = v1) = v2;  // Go crazy, but don't use with a FI.
  *++****++++**it++++ = v;

The typo seemed to say that it = v was required and the fact that it
worked didn't help.  Without the typo, it requires that all copies
act the same.  Pass by value effectively becomes pass by reference.

John
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jcoffin@taeus.com (Jerry Coffin)
Date: 1999/05/29
Raw View
In article <374d272f.3786818@news.freeserve.net>,
John_Maddock@compuserve.com says...
> I have an algorithm which needs to work on Output Iterators, but am
> having problems deducing what is and is not allowed:
>
> 24.1.2 states that X is required to be an assignable type, however the
> definition of ostream_iterator does not include an:
>
> ostream_iterator& operator=(const ostream_iterator&);
>
> and no implimentations I have seen have included it,

The assignment operator is special -- if you don't write one yourself,
the compiler will automatically generate one that creates a member-
wise copy of the right-hand argument.  A class only needs to provide
one of its own if that isn't sufficient.  The most common reason to
write your own assignment operator is if your class contains one or
more pointers.  In this case, you'll likely end up having to implement
either a deep-copy, or else some sort of reference-counting mechanism
in the assignment operator.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: John_Maddock@compuserve.com (John Maddock)
Date: 1999/05/29
Raw View
>The assignment operator is special -- if you don't write one yourself,
>the compiler will automatically generate one that creates a member-
>wise copy of the right-hand argument.  A class only needs to provide
>one of its own if that isn't sufficient.  The most common reason to
>write your own assignment operator is if your class contains one or
>more pointers.  In this case, you'll likely end up having to implement
>either a deep-copy, or else some sort of reference-counting mechanism
>in the assignment operator.

A fact that I'm well aware of, however in at least some of the
implimentations I've seen (and no I won't name the guilty) the
ostream_iterator is implemented with a reference to ostream& as a
private data member - hence compiler generated assignment is not
possible - perhaps I should have made that clear in my post.

In other words I guess I was checking that assignment was required
before accusing the vendor of a bug:-)

I also felt that providing an explicit assignment operator in the
ostream_iterator spec would have discouraged this kind of
implimentation.

John Maddock
http://ourworld.compuserve.com/homepages/John_Maddock/


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: John_Maddock@compuserve.com (John Maddock)
Date: 1999/05/30
Raw View
>The catch is that all copies of an output iterator act alike.  If you
>do anything to one of them, the others all behave as if it had been
>done to them.  There is no default constructor.  I guess you could
>swap two output iterators which are working on different ranges.  The
>note in the standard hints that algorithms must be one pass and Matt
>Austern states clearly in his book that the only thing which an
>algorithm may do is sequences of *it = t; ++it; pairs.  *it++ = t is
>one version of that pair.  If you do anything else, output iterators
>and other forward and up iterators will not act the same.  The other
>iterators point to something, output iterators do not.

I have followed your thead elsewhere and was unsure whether to post
here or there.

I don't think that you are correct that all instances of an output
iterator must be equivalent - even though its true in this one case -
since the standard makes it clear that any algorithm that requires an
output iterator, must also work with input, bidirectional and random
access iterators (24.1 part 3).  In other words it must be legal (if
unadvisable) to use char* as an output iterator - hence my need for
ostream_iterator to be assignable as my algorithm is recursive (yes I
know that serves me right) and therefore returns the new iterator
position in order to update the iterator position in the caller.

BTW table 73 (24.1.2) seems to imply that assignment to a copy is the
same as assignment to the original, this would not be true once one or
other has been incremented.

>
>The requirements on an algorithm which uses output iterators are that
>it must not do anything that depends on behavior other than what is
>assured by output iterator.  There is almost nothing assured by
>output iterator.

I'm beginning to see that....

John Maddock
http://ourworld.compuserve.com/homepages/John_Maddock/
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "P.J. Plauger" <pjp@plauger.com>
Date: 1999/05/30
Raw View
John Maddock wrote in message <374fce68.2257510@news.freeserve.net>...
>                                                                in at least some of the
>implimentations I've seen (and no I won't name the guilty) the
>ostream_iterator is implemented with a reference to ostream& as a
>private data member - hence compiler generated assignment is not
>possible - perhaps I should have made that clear in my post.
>
>In other words I guess I was checking that assignment was required
>before accusing the vendor of a bug:-)

It's more of an evolved requirement. In the initial STL spec, output iterators
did not have to be assignable. In fact, certain output iterators (such as
insert_iterator, as I recall) stored references and hence couldn't possibly
be assignable. Then along came ostreambuf_iterators which pretty much
had to be assignable, lest library code be even more unreadable. So
WG21 agreed to change the requirement on output iterators. They also
eliminated any stored references from output iterators in the library so
they could henceforth be assignable.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com





[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: James Kuyper <kuyper@wizard.net>
Date: 1999/05/31
Raw View
John Maddock wrote:
....
> I don't think that you are correct that all instances of an output
> iterator must be equivalent - even though its true in this one case -
> since the standard makes it clear that any algorithm that requires an
> output iterator, must also work with input, bidirectional and random
> access iterators (24.1 part 3).  In other words it must be legal (if
> unadvisable) to use char* as an output iterator - hence my need for

Why inadvisable? That's the ordinary method to be used for storing
'char' output from an algorithm. Take for instance the copy<> algorithm,
which takes a third argument which is an OutputIterator. The following
is perfectly reasonable (except for the simplifying but inadvisable use
of a "magic" number):

 #include <algorithm>

 char in[6]="hello", out[6];

 std::copy(in, in+6, out);

....
> BTW table 73 (24.1.2) seems to imply that assignment to a copy is the
> same as assignment to the original, this would not be true once one or
> other has been incremented.

I just noticed what may be a typo in that table. It says to "a = t is
equivalent to X(a) = t", where 'X' is the iterator type, 'a' is a
iterator of that type, and 't' is an instance of the value type.
Shouldn't that be "*a = t is the same as *X(a) = t" ?
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: John_Maddock@compuserve.com (John Maddock)
Date: 1999/05/27
Raw View
I have an algorithm which needs to work on Output Iterators, but am
having problems deducing what is and is not allowed:

24.1.2 states that X is required to be an assignable type, however the
definition of ostream_iterator does not include an:

ostream_iterator& operator=(const ostream_iterator&);

and no implimentations I have seen have included it, without this it
is not possible to assign one ostream_iterator to another, making it
impossible (under some curcumstances) to write algorithms which work
both with ostream_iterator and with built in types (pointers) as well
as forward iterators, all of which are required to be usable as valid
output iterators.



John Maddock
http://ourworld.compuserve.com/homepages/John_Maddock/
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "P.J. Plauger" <pjp@plauger.com>
Date: 1999/05/27
Raw View
John Maddock wrote in message <374d272f.3786818@news.freeserve.net>...
>I have an algorithm which needs to work on Output Iterators, but am
>having problems deducing what is and is not allowed:
>
>24.1.2 states that X is required to be an assignable type, however the
>definition of ostream_iterator does not include an:
>
>ostream_iterator& operator=(const ostream_iterator&);
>
>and no implimentations I have seen have included it,

You have the right to remain silent. You have the right to a copy
constructor and an assignment operator. If you cannot afford to
write these, they will be written for you by the compiler.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com



[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1999/05/27
Raw View
John_Maddock@compuserve.com (John Maddock) wrote:

: I have an algorithm which needs to work on Output Iterators, but am
: having problems deducing what is and is not allowed:

: 24.1.2 states that X is required to be an assignable type, however the
: definition of ostream_iterator does not include an:

: ostream_iterator& operator=(const ostream_iterator&);

: and no implimentations I have seen have included it, without this it
: is not possible to assign one ostream_iterator to another, making it
: impossible (under some curcumstances) to write algorithms which work
: both with ostream_iterator and with built in types (pointers) as well
: as forward iterators, all of which are required to be usable as valid
: output iterators.

I have been struggling with the output iterator (see output iterators
arn't iterators in comp.lang.c++.moderated).  I haven't figured out
what an algorithm would do with assignment; however, I submitted (today,
not yet posted) an ostream_iterator which does have a copy assignment
operator (Plauger answered why the others don't show one).  I think it
meets all of the requirements of an output iterator.  You may want to
include it in your list of output iterators with which your algorithm
must work.

The catch is that all copies of an output iterator act alike.  If you
do anything to one of them, the others all behave as if it had been
done to them.  There is no default constructor.  I guess you could
swap two output iterators which are working on different ranges.  The
note in the standard hints that algorithms must be one pass and Matt
Austern states clearly in his book that the only thing which an
algorithm may do is sequences of *it = t; ++it; pairs.  *it++ = t is
one version of that pair.  If you do anything else, output iterators
and other forward and up iterators will not act the same.  The other
iterators point to something, output iterators do not.

The requirements on an algorithm which uses output iterators are that
it must not do anything that depends on behavior other than what is
assured by output iterator.  There is almost nothing assured by
output iterator.

HTH,
John



[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: djz@west.net (Dan Zigmond)
Date: 1995/09/26
Raw View
I'm sorry if this has already been discussed here (or elsewhere).  I've
been reading the 28 April 1995 draft of the C++ standard and am wondering
why output iterators are not required to define operator== or operator!=
(or, more accurately, why it is not required that a == b or a != v be
defined when a and be are output iterators).

One consequence of this, as far as I can tell, is that algorithms like fill
and generate must be defined to take forward iterators rather than output
iterators.  I've looked at the code in the HP STL, and it looks like this:

template <class ForwardIterator, class Generator>
void generate(ForwardIterator first, ForwardIterator last, Generator gen) {
    while (first != last) *first++ = gen();
}

template <class ForwardIterator, class T>
void fill(ForwardIterator first, ForwardIterator last, const T& value) {
    while (first != last) *first++ = value;
}

Intuitively, this doesn't seem to require forward iterator semantics
because we are never dereferencing first or last for read purposes.
Comapre, for example, to transform (also from the HP implementation):

template <class InputIterator, class OutputIterator, class UnaryOperation>
OutputIterator transform(InputIterator first, InputIterator last,
                         OutputIterator result, UnaryOperation op) {
    while (first != last) *result++ = op(*first++);
    return result;
}

The only difference I see between result in transform anf first in fill and
generate is that result is not compared using operator!=, where as the
iterators in fill and generate are.

Is there some reason why output iterators cannot in general be compared
using the standard operators?  I'm sure there's a rationale for this that
I'm missing.  Could someone tell me what it is?

        Dan


-----------------------
Dan Zigmond
djz@west.net (prefered)
djz@avasoft.com (possible delay)
Avatar Software, Inc.
(303) 415-9815

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: 2.6.2

mQCPAzAmSSQAAAEEAMXr4YMu8GIhvvW4fEd+CEOsV7PkD/7plppDeN2C8iWgvj5K
+DHYlgzNh2sF1OsS/irKGNlbdXPJChOmsg5EelW/ZTVl6bJYqJSCBFTKSa1RA5P4
XFv511f+B/k8TCuIOPx+NyaTTrYxaTd841QLvadGjLjWBvNMJkV1/Q6ozQpNABEB
AAG0I0RhbmllbCBKLiBaaWdtb25kIDxkanpAYXZhc29mdC5jb20+
=Tx0L
-----END PGP PUBLIC KEY BLOCK-----



---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]