Topic: std::equal doesn't work with input iterators
Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 2000/08/11 Raw View
Wil Evers wrote:
> Please consider this simple problem: I'm trying to read from cin, to
> see if it contains the string "BEGIN" in its next five characters. In
> other words, given...
>
> const char searchFor[5] = "BEGIN";
>
> const char *b1 = searchFor;
> const char *e1 = searchFor + 5;
>
> std::istream_iterator<char> b2(cin);
> std::istream_iterator<char> e2;
>
> ..I need to know if the ranges [b1, e1) and [b2, e2) contain the same
> sequence of values. Although the standard says that std::equal...
>
> template <class InputIterator1, class InputIterator2>
> bool equal(InputIterator1 first1, InputIterator1 last1,
> InputIterator first2);
>
> ..only requires input iterators,
It only requires input interators to compile, indeed. To run, it needs
the ranges to be valid.
> that won't work.
That's right.
> I have two questions about this:
>
> (1) Isn't this a defect in the standard?
No.
> std::equal requires both
> [first1, last1) and [first2, (last1 - first1)) to be valid ranges.
How could it be otherwise ?
> It
> seems to me this is impossible to determine if all you know is that
> either InputIterator1 or InputIterator2 is an input iterator.
So you cannot use equal. Note that the standard doesn't care about
your (un)ability to use equal. It's your problem.
> (2) Did I miss some other STL algorithm that will do the job, or do I
> really need to roll my own loop? That sounds horribly primitive...
What about doing it the good old way:
string s;
cin.width (5+1) >> s; // width is silly
bool b = s == "BEGIN";
--
Valentin Bonnard
---
[ 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: Michiel Salters <salters@lucent.com>
Date: 2000/08/11 Raw View
Wil Evers wrote:
> Hi,
> Please consider this simple problem: I'm trying to read from cin, to
> see if it contains the string "BEGIN" in its next five characters. In
> other words, given...
> const char searchFor[5] = "BEGIN";
> const char *b1 = searchFor;
> const char *e1 = searchFor + 5;
> std::istream_iterator<char> b2(cin);
> std::istream_iterator<char> e2;
> ..I need to know if the ranges [b1, e1) and [b2, e2) contain the same
> sequence of values. Although the standard says that std::equal...
> template <class InputIterator1, class InputIterator2>
> bool equal(InputIterator1 first1, InputIterator1 last1,
> InputIterator first2);
> ..only requires input iterators, that won't work. If I write...
> std::equal(b1, e1, b2);
> ..I run into trouble if cin reaches end-of-file before reading five
> characters.
[SNIP]
What troubles? Trying to read from cin when it's at eof?
You could prevent that: cin.exceptions(ios_base::eofbit) will
cause an exception to be thrown when std::equal attempts to read
5 characters when they're not available.
> I have two questions about this:
> (1) Isn't this a defect in the standard? std::equal requires both
> [first1, last1) and [first2, (last1 - first1)) to be valid ranges. It
> seems to me this is impossible to determine if all you know is that
> either InputIterator1 or InputIterator2 is an input iterator.
But as shown, you don't have to know that - as long as you can use
exceptions to deal with that. I wouldn't call this a defect. Besides,
you might know something about the InputIterators in another way
(e.g. Using some OS specific call to get a filesize.) so std::equal
shouldn't worry about that.
> (2) Did I miss some other STL algorithm that will do the job, or do I
> really need to roll my own loop? That sounds horribly primitive...
The problem can be solved outside the scope of STL.
HTH,
Michiel Salters
---
[ 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: Wil Evers <bouncer@dev.null>
Date: 2000/08/11 Raw View
In article <39937124.2A3F@wanadoo.fr>, Valentin Bonnard wrote:
> So you cannot use equal. Note that the standard doesn't care about
> your (un)ability to use equal. It's your problem.
Indeed it is. On the other hand, the standard does seem to care when
I want to know if one range is less than some other:
std::lexicographical_compare doesn't require the two ranges to have
the same number of elements. From a design point of view, I'd say this
is at best inconsistent.
> > (2) Did I miss some other STL algorithm that will do the job, or do I
> > really need to roll my own loop? That sounds horribly primitive...
>
> What about doing it the good old way:
>
> string s;
> cin.width (5+1) >> s; // width is silly
> bool b = s == "BEGIN";
No thanks. Now if only I could remember how to write a loop...
- Wil
--
Wil Evers, DOOSYS IT Consultants, Maarssen, Holland
[Wil underscore Evers at doosys dot 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: Dietmar Kuehl <dietmar_kuehl@yahoo.com>
Date: 2000/08/11 Raw View
Hi,
In article <39937124.2A3F@wanadoo.fr>,
Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote:
> string s;
> cin.width (5+1) >> s; // width is silly
> bool b = s == "BEGIN";
Why do you want to read *six* (== '5 + 1') characters to compare them
against a string with *five* (== 'std::string("BEGIN").size()')
characters? The "sillyness" you refer to happens only when storing
the characters extracted with 'operator>>()' in a built-in array (I'm
somewhat assuming that you didn't us
using std::cin;
typedef char string[6];
in your example; but then the comparison would not work anyway...).
When storing characters into a built-in array, the 'width()' specifies
how many characters are available in the array, not how many are to be
extracted. When storing the characters into an 'std::string', the
'width()' specifies the maximum number of characters to be extracted.
--
<mailto:dietmar_kuehl@yahoo.com>
<http://www.dietmar-kuehl.de/>
Sent via Deja.com http://www.deja.com/
Before you buy.
---
[ 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: Wil Evers <bouncer@dev.null>
Date: 2000/08/11 Raw View
In article <3993C474.CBC20FC4@elbanet.co.at>, Heinz Huber wrote:
> Wil Evers wrote:
>
> > const char searchFor[5] = "BEGIN";
>
> [snipped problem description]
>
> You're defining an array with 5 chars, but the string you use in the
> initializer has 6 chars (don't forget the terminating '\0').
After checking the standard, I must admit there is a problem here.
According to section A8.7 of K&R 2nd edition, the line in question
is equivalent to
const char searchFor[5] = { 'B', 'E', 'G', 'I', 'N' };
- a five-element char array with no terminating '\0'. And that is
what I meant.
As it turns out, the rules in C++ are different: section 8.5.2 of the
C++ standard says you can't initialize a 5-char array this way. I
wonder if this is a deliberate departure from C89. Anyone?
> On comparing it might be that the algorithm looks for the '\0'
> and doesn't find one because the array only contains
> 'B','E','G','I','N'.
No, std::equal doesn't check for a terminating '\0' when comparing
sequences of characters. The number of values it compares is equal to
the distance between its first and second arguments, which is why it's
unusable with input iterators.
- Wil
--
Wil Evers, DOOSYS IT Consultants, Maarssen, Holland
[Wil underscore Evers at doosys dot 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: "Bill Wade" <bill.wade@stoner.com>
Date: 2000/08/11 Raw View
"Wil Evers" <bouncer@dev.null> wrote
> (2) Did I miss some other STL algorithm that will do the job, or do I
> really need to roll my own loop? That sounds horribly primitive...
In practice I'd probably do something that looks like the primitive thing.
When parsing I usually want code that tells me what it saw that was
unexpected. With your code the only way I could do that is with some kind
of seek operation after equal returns false. Since I usually can't
reposition cin, I'd be out of luck.
Instead of rolling your own loop you could roll your own istream_iterator so
that dereferencing and incrementing end() are well defined and useful for
your needs.
HTH
---
[ 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: "Bill Wade" <bill.wade@stoner.com>
Date: 2000/08/11 Raw View
"Michiel Salters" <salters@lucent.com> wrote
> What troubles? Trying to read from cin when it's at eof?
> You could prevent that: cin.exceptions(ios_base::eofbit) will
> cause an exception to be thrown when std::equal attempts to read
> 5 characters when they're not available.
Unfortunately if you are comparing five characters, the istream_iterator
will look at six, possibly causing an exception even when the strings are
equal.
Assuming equal() is written (approximately) as
bool equal(T1 b1, T1 e1, T2 b2)
{
while(b1 != e1 && *b1++ == *b2++){}
return b1 == e1;
}
The standard requires that istream_iterator::operator++(int) perform a read
in preparation for the next dereference (however the next dereference is
illegal if the read failed). That means that if T2 is an istream_iterator,
and five characters get compared, b2 will have read six characters. If the
istream fails by throwing an exception, the caller of equal() won't be able
to tell if the exception occurred while attempting to read one of the first
five characters, or while attempting to read the sixth character. Even if
the caller could tell, the exception while reading the sixth character would
have occurred before the fifth characters were compared meaning that the
caller still can't figure out what the result of the comparison was.
The OP is certainly correct that equal() doesn't work very well with
std::istream_iterator.
---
[ 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: Wil Evers <bouncer@dev.null>
Date: 2000/08/10 Raw View
Hi,
Please consider this simple problem: I'm trying to read from cin, to
see if it contains the string "BEGIN" in its next five characters. In
other words, given...
const char searchFor[5] = "BEGIN";
const char *b1 = searchFor;
const char *e1 = searchFor + 5;
std::istream_iterator<char> b2(cin);
std::istream_iterator<char> e2;
..I need to know if the ranges [b1, e1) and [b2, e2) contain the same
sequence of values. Although the standard says that std::equal...
template <class InputIterator1, class InputIterator2>
bool equal(InputIterator1 first1, InputIterator1 last1,
InputIterator first2);
..only requires input iterators, that won't work. If I write...
std::equal(b1, e1, b2);
..I run into trouble if cin reaches end-of-file before reading five
characters. On the other hand, I if write...
std::equal(b2, e2, b1);
..I run into trouble if cin yields more than five characters.
I have two questions about this:
(1) Isn't this a defect in the standard? std::equal requires both
[first1, last1) and [first2, (last1 - first1)) to be valid ranges. It
seems to me this is impossible to determine if all you know is that
either InputIterator1 or InputIterator2 is an input iterator.
(2) Did I miss some other STL algorithm that will do the job, or do I
really need to roll my own loop? That sounds horribly primitive...
Thanks,
- Wil
--
Wil Evers, DOOSYS IT Consultants, Maarssen, Holland
[Wil underscore Evers at doosys dot 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: Heinz Huber <Heinz.Huber@elbanet.co.at>
Date: 2000/08/11 Raw View
Hi,
might it be that the problem is in your first line?
Wil Evers wrote:
>
> Hi,
>
> Please consider this simple problem: I'm trying to read from cin, to
> see if it contains the string "BEGIN" in its next five characters. In
> other words, given...
>
> const char searchFor[5] = "BEGIN";
[snipped problem description]
You're defining an array with 5 chars, but the string you use in the
initializer has 6 chars (don't forget the terminating '\0'). On
comparing it might be that the algorithm looks for the '\0' and doesn't
find one because the array only contains 'B','E','G','I','N'.
hth,
Heinz
---
[ 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 ]