Reputation: 337
I'm writing a C++ library to parse WARC files, it contains the classes to store the WARC record and fields data as well as the operator to read the data from some input stream. It has the operator>>
to read from a istream
and populate the WARCRecord
object. I'm able to read from the istream
stream and parse it to a WARCRecord
object. However, I would like to use a istream_iterator
to read all the WARC records from the input stream.
When I use the istream_iterator
to iterate until the end of the stream my operator>>
receives the same(copy?) object of the last call. Is this the expected behavior or I'm missing some operator/constructor in my class definition?
This is the repository link with unit test to check this behavior: https://github.com/jvanz/libwarc
A simple example to demonstrate the above text:
#include <iostream>
#include <istream>
#include <sstream>
#include <vector>
#include <iterator>
class MyClass
{
public:
std::vector<unsigned int> fields;
friend std::istream& operator>> (std::istream& is, MyClass& obj);
};
std::istream& operator>> (std::istream& is, MyClass& obj){
std::istream::sentry s(is);
if (s) {
for (unsigned i = 0; i < 3; i++) {
unsigned f;
is >> f;
obj.fields.push_back(f);
}
}
return is;
}
int main(void)
{
std::stringstream ss;
ss << 1 << " " << 2 << " " << 3 << " " << 4 << " " << 5 << " " << 6;
std::istream_iterator<MyClass> it(ss);
std::cout << it->fields.size() << std::endl;
it++;
std::cout << it->fields.size() << std::endl;
return 0;
}
The output of this program is:
$ ./a.out
3
6
Notice the accumulation of the fields
member size of the MyClass
object
Upvotes: 0
Views: 204
Reputation: 337
I checked the /usr/include/c++/7/bits/stream_operator.h
from my machine to take a look in the istream_iterator
class. As far as I could see, in fact, the class reuses the same object.
/// Provides input iterator semantics for streams.
template<typename _Tp, typename _CharT = char,
typename _Traits = char_traits<_CharT>, typename _Dist = ptrdiff_t>
class istream_iterator
: public iterator<input_iterator_tag, _Tp, _Dist, const _Tp*, const _Tp&>
{
public:
typedef _CharT char_type;
typedef _Traits traits_type;
typedef basic_istream<_CharT, _Traits> istream_type;
private:
istream_type* _M_stream;
_Tp _M_value;
bool _M_ok;
public:
/// Construct end of input stream iterator.
_GLIBCXX_CONSTEXPR istream_iterator()
: _M_stream(0), _M_value(), _M_ok(false) {}
/// Construct start of input stream iterator.
istream_iterator(istream_type& __s)
: _M_stream(std::__addressof(__s))
{ _M_read(); }
istream_iterator(const istream_iterator& __obj)
: _M_stream(__obj._M_stream), _M_value(__obj._M_value),
_M_ok(__obj._M_ok)
{ }
const _Tp&
operator*() const
{
__glibcxx_requires_cond(_M_ok,
_M_message(__gnu_debug::__msg_deref_istream)
._M_iterator(*this));
return _M_value;
}
const _Tp*
operator->() const { return std::__addressof((operator*())); }
istream_iterator&
operator++()
{
__glibcxx_requires_cond(_M_ok,
_M_message(__gnu_debug::__msg_inc_istream)
._M_iterator(*this));
_M_read();
return *this;
}
istream_iterator
operator++(int)
{
__glibcxx_requires_cond(_M_ok,
_M_message(__gnu_debug::__msg_inc_istream)
._M_iterator(*this));
istream_iterator __tmp = *this;
_M_read();
return __tmp;
}
bool
_M_equal(const istream_iterator& __x) const
{ return (_M_ok == __x._M_ok) && (!_M_ok || _M_stream == __x._M_stream); }
private:
void
_M_read()
{
_M_ok = (_M_stream && *_M_stream) ? true : false;
if (_M_ok)
{
*_M_stream >> _M_value;
_M_ok = *_M_stream ? true : false;
}
}
};
Notice the _M_read()
does not create new object before call the operator>>
Upvotes: 0