首页 > 代码库 > SGI 2.9.1源码手札 stream Iterator:istream_iterator 和ostream_iterator 知识核心点

SGI 2.9.1源码手札 stream Iterator:istream_iterator 和ostream_iterator 知识核心点

短期代码阅读主要基于SGI的STL,测试环境则是GCC_4.8.3_STL和VS_STL。暂时不去配置BOOST等库STL细节。待续


1、istream_iterator 输入流迭代器

1)没有operator=操作,因为只读,不可写,所以编译不支持。迭代器句柄保存当前已读取到的数据。

_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(&__s)      { _M_read(); }

在对象声明即初始化的世界,三个版本的构造函数代码是类似的,有个小坑:除了初始化的默认参数是不一样的之外,在使用绑定至正确输入对象时,自动调用了读取操作。

2)有个读取操作为protected,向外提供,使用句柄的形式听过数据访问操作.。在SGI中的read操作和GCC:_M_read实现比较类似,都是单独定义一个状态控制量,根据控制量去确定是否进行输入迭代,正确情况下会调用输入对象的输入符操作。重点:当出现错误的情况并不会修正迭代器指向,这点和VS:_Getval 的实现不一样。

void
      _M_read()
      {
	_M_ok = (_M_stream && *_M_stream) ? true : false;
	if (_M_ok)
	  {
	    *_M_stream >> _M_value;
	    _M_ok = *_M_stream ? true : false;
	  }
      }

而在VS:_Getval 中,实现由些差别:

void _Getval()
		{	// get a _Ty value if possible
		if (_Myistr != 0 && !(*_Myistr >> _Myval))
			_Myistr = 0;
		}
VS出现输入错误终止的情况下会把当前迭代器的值置为0,也就是说不在绑定至任何输入流,具有不可重复性操作。基于这点,我们无法再多平台上根据输入流的状态进行容错处理,至少vs是不支持的,在1:处理完输入流的状态后得2:重新设置迭代器的状态才行继续输入(SGI和GCC则没有步骤2可以继续使用输入迭代器)。


3、迭代器进行迭代时,

operator++
完成读取数据的操作。外界使用其接口读取数据则是另外一个接口:解引用取值操作。也就是时候但迭代失败时,保留的是上一个数据

istream_iterator&
      operator++()
      {
	__glibcxx_requires_cond(_M_ok,
				_M_message(__gnu_debug::__msg_inc_istream)
				._M_iterator(*this));
	_M_read();
	return *this;
      }

正确的使用方式如下:

std::istream_iterator<int> in_it(cin),eos;//输入输出的迭代器结束符和文件等结束符不一样,是迭代器的判定不一样,所以定义一个空迭代器判定是否为终止状态

	
	while (!in_it._Equal(eos))
	{
		cout<<*in_it;
		cout<<endl;
		++in_it;
	}


关于结束判定的这点,因为源码实现有点不一样,GCC实现如下:在控制量

 bool
      _M_equal(const istream_iterator& __x) const
      { return (_M_ok == __x._M_ok) && (!_M_ok || _M_stream == __x._M_stream); }

而SGI port的则增加输入流状态处理,如首先校验两个迭代器是否完成输入操作,没有的话继续执行(这个估计得看操作系统的操作,一旦IO设备未置位读取完成,就继续执行读取,不出现多次读取的处理是交由操作系统或相关api去处理,而不是在这里控制。)是:

bool _M_equal(const _Self& __x) const {
    if (!_M_read_done) {
      _M_read();
    }
    if (!__x._M_read_done) {
      __x._M_read();
    }
    return (_M_ok == __x._M_ok) && (!_M_ok || _M_stream == __x._M_stream);
  }

当然SGI的代码则是在operator==操作符中实现

template <class _Tp, class _Distance>
inline bool operator==(const istream_iterator<_Tp, _Distance>& __x,
                       const istream_iterator<_Tp, _Distance>& __y) {
  return (__x._M_stream == __y._M_stream &&
          __x._M_end_marker == __y._M_end_marker) ||
         __x._M_end_marker == false && __y._M_end_marker == false;
}


最后来到比较坑的代码中:

SGI:

istream_iterator() : _M_stream(&cin), _M_end_marker(false) {}
  istream_iterator(istream& __s) : _M_stream(&__s) { _M_read(); }


SGI PORT:

istream_iterator() : _M_stream(0), _M_ok(false), _M_read_done(true) {}
  istream_iterator(istream_type& __s) : _M_stream(&__s), _M_ok(false), _M_read_done(false) {} // 重点,没有进行首次读取
reference operator*() const { //采用惰性处理,蛮好的,适合理解    if (!_M_read_done) {      _M_read();    }    return _M_value;  }


GCC:

_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(&__s)
      { _M_read(); }


VS:

istream_iterator()
		: _Myistr(0)
		{	// construct singular iterator
		}

	istream_iterator(istream_type& _Istr)
		: _Myistr(&_Istr)
		{	// construct with input stream
		_Getval();
		}


实际上真正按照标准实现的是STL port,可是其他家成为事实的标准,都在有绑定状态优先执行首次读取。不过不用担心,所有的算法库都是先读取首操作采取执行operator++操作,实际上的数据处理不对出现说丢失状态。



无论哪个版本,最后表现的结果都是一致的(除效率外),基于这点,在基本原理实现上,我优先阅读SGI和GCC的源码,偶尔辅助VS的实现。


ostream_iterator 的源码思想也类似,重载了operator=,不提供指针偏移等操作(迭代操作在于原位),就不继续贴代码啦。

都是定义一个空迭代作为哨兵。

std::istream_iterator<int> in_it(cin),eos;

	
	while (!(in_it==eos))
	{
		cout<<*in_it;
		cout<<endl;
		++in_it;
	}



SGI 2.9.1源码手札 stream Iterator:istream_iterator 和ostream_iterator 知识核心点