IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

iostream メモ

とりあえず、何が行われているか知らないと怖くて使えないので

bits/char_traits.h

namespace __gnu_cxx
  template<typename _CharT>
    struct _Char_types
    {   
      typedef unsigned long   int_type;
      typedef std::streampos  pos_type;
      typedef std::streamoff  off_type;
      typedef std::mbstate_t  state_type;
    };

なんで、 _CharT 使ってないのにこんな事してるんだろう。。。
あとで分かった
traits 内の型に関するところだけ specialization 出来る余地を残しているのか。テンプレートパラメータって、定義で使われなくても意味があるんだなあ。
次

      static void
      assign (char_type& __c1, const char_type& __c2)
      { __c1 = __c2; };

代入の定義、 const が付いてないってことで用途がすぐ分かる。 const 重要だなあ。
次

      static bool
      eq(const char_type& __c1, const char_type& __c2)
      { return __c1 == __c2; }

      static bool
      lt(const char_type& __c1, const char_type& __c2)
      { return __c1 < __c2; }

gt とか neq とかは、この二つが分かれば自明なので定義しなくていいのかな。
次

  template<typename _CharT>
    int
    char_traits<_CharT>::
    compare(const char_type* __s1, const char_type* __s2, std::size_t __n)
    {
      for (std::size_t __i = 0; __i < __n; ++__i)
        if (lt(__s1[__i], __s2[__i]))
          return -1;
        else if (lt(__s2[__i], __s1[__i]))
          return 1;
      return 0;
    }

比較。compare は lt で実装、 lt だけ実装すれば compare は実装しなくてもいいかもしれないってことか
次

  template<typename _CharT>
    std::size_t
    char_traits<_CharT>::
    length(const char_type* __p)
    {
      std::size_t __i = 0;
      while (!eq(__p[__i], char_type()))
        ++__i;
      return __i;
    }

文字列の長さ。 char_type のデフォルトコンストラクタと同じになるまで繰り返してる。文字の型はデフォルトコンストラクタの返す値を終端にすると便利ってことか。
次

  template<typename _CharT>
    const typename char_traits<_CharT>::char_type*
    char_traits<_CharT>::
    find(const char_type* __s, std::size_t __n, const char_type& __a)
    {
      for (std::size_t __i = 0; __i < __n; ++__i)
        if (eq(__s[__i], __a))
          return __s + __i;
      return 0;
    }

探索。 eq 実装すればいい
次

  template<typename _CharT>
    typename char_traits<_CharT>::char_type*
    char_traits<_CharT>::
    move(char_type* __s1, const char_type* __s2, std::size_t __n)
    {
      return static_cast<_CharT*>(__builtin_memmove(__s1, __s2,
                                                    __n * sizeof(char_type)));
    }

  template<typename _CharT>
    typename char_traits<_CharT>::char_type*
    char_traits<_CharT>::
    copy(char_type* __s1, const char_type* __s2, std::size_t __n)
    {
      // NB: Inline std::copy so no recursive dependencies.
      std::copy(__s2, __s2 + __n, __s1);
      return __s1;
    }

  template<typename _CharT>
    typename char_traits<_CharT>::char_type*
    char_traits<_CharT>::
    assign(char_type* __s, std::size_t __n, char_type __a)
    {
      // NB: Inline std::fill_n so no recursive dependencies.
      std::fill_n(__s, __n, __a);
      return __s;
    }

move, copy, assign は __builtin_memmove, std::copy, std::fill_n を呼び出しているだけ
次

      static char_type
      to_char_type(const int_type& __c)
      { return static_cast<char_type>(__c); }

      static int_type
      to_int_type(const char_type& __c)
      { return static_cast<int_type>(__c); }

      static bool
      eq_int_type(const int_type& __c1, const int_type& __c2)
      { return __c1 == __c2; }

      static int_type
      eof()
      { return static_cast<int_type>(EOF); }

      static int_type
      not_eof(const int_type& __c)
      { return !eq_int_type(__c, eof()) ? __c : to_int_type(char_type()); }

int_type 関連。 int_type って何に使うんだろう?
eof は eof を int_type にして返してる

namespace std
  template<class _CharT>
    struct char_traits : public __gnu_cxx::char_traits<_CharT>
    { };

std::char_traits を定義、 __gnu_cxx のやつをそのまま
次

  template<>
    struct char_traits<char>
    {...}

  template<>
    struct char_traits<wchar_t>
    {...}

char_traits の specialization。なんか、冗長な気がするなあ。いちいち全部の関数やらずに、個々の関数だけ specialization すればいいのに。

iosfwd

  template<typename _CharT, typename _Traits = char_traits<_CharT> >
    class basic_ios;

  template<typename _CharT, typename _Traits = char_traits<_CharT> >
    class basic_streambuf;

  template<typename _CharT, typename _Traits = char_traits<_CharT> >
    class basic_istream;

  template<typename _CharT, typename _Traits = char_traits<_CharT> >
    class basic_ostream;

  template<typename _CharT, typename _Traits = char_traits<_CharT> >
    class basic_iostream;

  template<typename _CharT, typename _Traits = char_traits<_CharT>,
            typename _Alloc = allocator<_CharT> >
    class basic_stringbuf;

  template<typename _CharT, typename _Traits = char_traits<_CharT>,
           typename _Alloc = allocator<_CharT> >
    class basic_istringstream;

  template<typename _CharT, typename _Traits = char_traits<_CharT>,
           typename _Alloc = allocator<_CharT> >
    class basic_ostringstream;

  template<typename _CharT, typename _Traits = char_traits<_CharT>,
           typename _Alloc = allocator<_CharT> >
    class basic_stringstream;

  template<typename _CharT, typename _Traits = char_traits<_CharT> >
    class basic_filebuf;

  template<typename _CharT, typename _Traits = char_traits<_CharT> >
    class basic_ifstream;

  template<typename _CharT, typename _Traits = char_traits<_CharT> >
    class basic_ofstream;

  template<typename _CharT, typename _Traits = char_traits<_CharT> >
    class basic_fstream;

  template<typename _CharT, typename _Traits = char_traits<_CharT> >
    class istreambuf_iterator;

  template<typename _CharT, typename _Traits = char_traits<_CharT> >
    class ostreambuf_iterator;

おおお。 char_traits 大活躍

streambuf

namspace std

basic_streambuf クラステンプレート

      char_type*                _M_in_beg;     // Start of get area. 
      char_type*                _M_in_cur;     // Current read area. 
      char_type*                _M_in_end;     // End of get area. 
      char_type*                _M_out_beg;    // Start of put area. 
      char_type*                _M_out_cur;    // Current put area. 
      char_type*                _M_out_end;    // End of put area.

      locale                    _M_buf_locale;

      basic_streambuf()
      : _M_in_beg(0), _M_in_cur(0), _M_in_end(0),
      _M_out_beg(0), _M_out_cur(0), _M_out_end(0),
      _M_buf_locale(locale())
      { }

コンストラクタで _M_in_beg, _M_in_cur, _M_in_end, _M_out_beg, _M_out_cur, _M_out_end ポインタの初期化
次

      char_type*
      eback() const { return _M_in_beg; }

      char_type*
      gptr()  const { return _M_in_cur;  }

      char_type*
      egptr() const { return _M_in_end; }

      char_type*
      pbase() const { return _M_out_beg; }

      char_type*
      pptr() const { return _M_out_cur; }

      char_type*
      epptr() const { return _M_out_end; }

ポインタのアクセサ。 p は put の p 、 g は get の g
次

      void 
      setg(char_type* __gbeg, char_type* __gnext, char_type* __gend)
      {
        _M_in_beg = __gbeg;
        _M_in_cur = __gnext;
        _M_in_end = __gend;
      }   

      void
      setp(char_type* __pbeg, char_type* __pend)
      {
        _M_out_beg = _M_out_cur = __pbeg;
        _M_out_end = __pend;
      }

ポインタの設定。実装側はこれを使って、バッファを指定するのかな??
次

      void
      gbump(int __n) { _M_in_cur += __n; }

      void
      pbump(int __n) { _M_out_cur += __n; }

ポインタを進める関数
次

      int_type 
      sputc(char_type __c)
      {   
        int_type __ret;
        if (__builtin_expect(this->pptr() < this->epptr(), true))
          {
            *this->pptr() = __c;
            this->pbump(1);
            __ret = traits_type::to_int_type(__c);
          }
        else
          __ret = this->overflow(traits_type::to_int_type(__c));
        return __ret;
      }   

一文字バッファに書き込む、 pptr を上書きして pbump で一文字進む。 pptr と epptr が等しかったら(もう書き込める箇所が無かったら)、 overflow を呼び出す。
書き込んだ文字を返す。
ちなみに __builtin_expect は、分岐予測のヒントを与えるためのもので

__builtin_expect(expr, true) // expr はたいてい true になるよね!いう意味

単に expr と書くのと意味的には等価
次

      virtual int_type
      overflow(int_type /* __c */ = traits_type::eof())
      { return traits_type::eof(); }

overflow は何もしない。 virtual なので、実装してくださいってことか
次

      int_type
      sgetc()
      {
        int_type __ret;
        if (__builtin_expect(this->gptr() < this->egptr(), true))
          __ret = traits_type::to_int_type(*this->gptr());
        else
          __ret = this->underflow();
        return __ret;
      }

gptr から一文字読み込む。ポインタは進めない。 gptr と egptr が等しかったら(バッファに読み込む文字がないなら)、 underflow を呼び出す。
次

      int_type
      snextc()
      {
        int_type __ret = traits_type::eof();
        if (__builtin_expect(!traits_type::eq_int_type(this->sbumpc(),
                                                       __ret), true))
          __ret = this->sgetc();
        return __ret;
      }

      int_type
      sbumpc()
      {
        int_type __ret;
        if (__builtin_expect(this->gptr() < this->egptr(), true))
          {
            __ret = traits_type::to_int_type(*this->gptr());
            this->gbump(1);
          }
        else
          __ret = this->uflow();
        return __ret;
      }

sbumpc は一文字進めて、進める前にポインタが指していた文字を返す。 snextc は一文字進めて、進めた後にポインタが指している文字を返す。
文字が無い場合は、 uflow を呼び出す。
次

      virtual int_type
      underflow()
      { return traits_type::eof(); }

      virtual int_type
      uflow()
      {
        int_type __ret = traits_type::eof();
        const bool __testeof = traits_type::eq_int_type(this->underflow(),
                                                        __ret);
        if (!__testeof)
          {
            __ret = traits_type::to_int_type(*this->gptr());
            this->gbump(1);
          }
        return __ret;
      }

underflow も uflow も virtual なので拡張可能。 underflow は読み込む文字が無くなったら確実に呼び出される。 uflow は読み込む文字が無くてあれば次に進みたいときに呼び出される。 underflow で、値を返せば、 uflow は一文字進めることができる。
次

      streamsize
      sputn(const char_type* __s, streamsize __n)
      { return this->xsputn(__s, __n); }

      streamsize
      sgetn(char_type* __s, streamsize __n)
      { return this->xsgetn(__s, __n); }

sputn と sgetn は xsputn と xsgetn を呼び出すだけ。
次

  template<typename _CharT, typename _Traits>
    streamsize
    basic_streambuf<_CharT, _Traits>::
    xsputn(const char_type* __s, streamsize __n)
    {
      streamsize __ret = 0;
      while (__ret < __n)
        {
          const streamsize __buf_len = this->epptr() - this->pptr();
          if (__buf_len)
            {
              const streamsize __remaining = __n - __ret;
              const streamsize __len = std::min(__buf_len, __remaining);
              traits_type::copy(this->pptr(), __s, __len);
              __ret += __len;
              __s += __len;
              this->pbump(__len);
            }

          if (__ret < __n)
            {
              int_type __c = this->overflow(traits_type::to_int_type(*__s));
              if (!traits_type::eq_int_type(__c, traits_type::eof()))
                {
                  ++__ret;
                  ++__s;
                }
              else
                break;
            }
        }
      return __ret;
    }

traits_type::copy を使ってサイズ分コピーして、バッファの最後まで行ったら overflow を呼び出して、eof を返さなかったらさらにコピー。
__n 文字書き込むか、 overflow が eof を返すまで書き込む。
次

  template<typename _CharT, typename _Traits>
    streamsize
    basic_streambuf<_CharT, _Traits>::
    xsgetn(char_type* __s, streamsize __n)
    {   
      streamsize __ret = 0;
      while (__ret < __n)
        {
          const streamsize __buf_len = this->egptr() - this->gptr();
          if (__buf_len)
            {
              const streamsize __remaining = __n - __ret;
              const streamsize __len = std::min(__buf_len, __remaining);
              traits_type::copy(__s, this->gptr(), __len);
              __ret += __len;
              __s += __len;
              this->gbump(__len);
            }

          if (__ret < __n)
            {
              const int_type __c = this->uflow();
              if (!traits_type::eq_int_type(__c, traits_type::eof()))
                {
                  traits_type::assign(*__s++, traits_type::to_char_type(__c));
                  ++__ret;
                }   
              else
                break;
            }   
        }   
      return __ret;
    }

traits_type::copy を使ってサイズ分コピーして、バッファの最後まで行ったら overflow を呼び出して、eof を返さなかったらさらにコピー(最初の一文字は traits_type::assign を使う)。
__n 文字読み込むか、 overflow が eof を返すまで読み込む。

他にもいろいろあるけど

とりあえず、 put と get 関係はこんなものなので、これ以上 streambuf はここはみない

ostream

      typedef basic_streambuf<_CharT, _Traits>          __streambuf_type;

__streambuf_type。
次

      explicit
      basic_ostream(__streambuf_type* __sb)
      { this->init(__sb); }

basic_ostream のコンストラクタ。 init に basic_streambuf を与える。暗黙の型変換は行われない。
次

  template<typename _CharT, typename _Traits>
    void
    basic_ios<_CharT, _Traits>::init(basic_streambuf<_CharT, _Traits>* __sb)
    {   
      // NB: This may be called more than once on the same object.
      ios_base::_M_init();

      // Cache locale data and specific facets used by iostreams.
      _M_cache_locale(_M_ios_locale);

      // NB: The 27.4.4.1 Postconditions Table specifies requirements
      // after basic_ios::init() has been called. As part of this,
      // fill() must return widen(' ') any time after init() has been
      // called, which needs an imbued ctype facet of char_type to
      // return without throwing an exception. Unfortunately,
      // ctype<char_type> is not necessarily a required facet, so
      // streams with char_type != [char, wchar_t] will not have it by
      // default. Because of this, the correct value for _M_fill is
      // constructed on the first call of fill(). That way,
      // unformatted input and output with non-required basic_ios
      // instantiations is possible even without imbuing the expected
      // ctype<char_type> facet.
      _M_fill = _CharT();
      _M_fill_init = false;

      _M_tie = 0;
      _M_exception = goodbit;
      _M_streambuf = __sb;
      _M_streambuf_state = __sb ? goodbit : badbit;
    }   

init の中身とりあえず、 _M_streambuf が設定されているのが分かる。 _M_streambuf_state は __sb がちゃんと設定されたかを示している。
次

      basic_streambuf<_CharT, _Traits>*
      rdbuf() const
      { return _M_streambuf; }

rdbuf を使って _M_streambuf を取得出来る
次

  template<typename _CharT, typename _Traits>
    basic_ostream<_CharT, _Traits>&
    basic_ostream<_CharT, _Traits>::
    flush()
    {   
      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // DR 60. What is a formatted input function?
      // basic_ostream::flush() is *not* an unformatted output function.
      ios_base::iostate __err = ios_base::iostate(ios_base::goodbit);
      try 
        {
          if (this->rdbuf() && this->rdbuf()->pubsync() == -1) 
            __err |= ios_base::badbit;
        }
      catch(__cxxabiv1::__forced_unwind&)
        {
          this->_M_setstate(ios_base::badbit);    
          __throw_exception_again;
        }
      catch(...)
        { this->_M_setstate(ios_base::badbit); }
      if (__err)
        this->setstate(__err);
      return *this;
    }

flush というのは、 basic_streambuf の pubsync を呼び出すことに等しい。
次
ちょっと戻って basic_streambuf の pubsync を確認

      int 
      pubsync() { return this->sync(); }

      virtual int
      sync() { return 0; }

なるほど、つまり、 flush で何が行われるかは basic_streambuf の継承先の実装次第ってことか
次

  template<typename _CharT, typename _Traits>
    basic_ostream<_CharT, _Traits>&
    basic_ostream<_CharT, _Traits>::
    write(const _CharT* __s, streamsize __n)
    {   
      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // DR 60. What is a formatted input function?
      // basic_ostream::write(const char_type*, streamsize) is an
      // unformatted output function.
      // DR 63. Exception-handling policy for unformatted output.
      // Unformatted output functions should catch exceptions thrown
      // from streambuf members.
      sentry __cerb(*this);
      if (__cerb)
        {
          try
            { _M_write(__s, __n); }
          catch(__cxxabiv1::__forced_unwind&)
            {
              this->_M_setstate(ios_base::badbit);    
              __throw_exception_again;
            }
          catch(...)
            { this->_M_setstate(ios_base::badbit); }
        }
      return *this;
    }

write は、 _M_write を呼び出す。
__cerb はロックを書けるためのもの
次

      void
      _M_write(const char_type* __s, streamsize __n)
      {   
        const streamsize __put = this->rdbuf()->sputn(__s, __n);
        if (__put != __n)
          this->setstate(ios_base::badbit);
      }   

おおお。 sputn を読んでるのね。すっきり。
全部書き込めなかったら badbit が立つ
次

      __ostream_type&
      operator<<(long __n)
      { return _M_insert(__n); }

      __ostream_type&
      operator<<(unsigned long __n)
      { return _M_insert(__n); }

      __ostream_type&
      operator<<(bool __n)
      { return _M_insert(__n); }

      __ostream_type&
      operator<<(short __n);

      __ostream_type&
      operator<<(unsigned short __n)
      {
        // _GLIBCXX_RESOLVE_LIB_DEFECTS
        // 117. basic_ostream uses nonexistent num_put member functions.
        return _M_insert(static_cast<unsigned long>(__n));
      }

      __ostream_type&
      operator<<(int __n);

      __ostream_type&
      operator<<(unsigned int __n)
      {
        // _GLIBCXX_RESOLVE_LIB_DEFECTS
        // 117. basic_ostream uses nonexistent num_put member functions.
        return _M_insert(static_cast<unsigned long>(__n));
       }

#ifdef _GLIBCXX_USE_LONG_LONG
      __ostream_type&
      operator<<(long long __n)
      { return _M_insert(__n); }

      __ostream_type&
      operator<<(unsigned long long __n)
      { return _M_insert(__n); }
#endif

      __ostream_type&
      operator<<(double __f)
      { return _M_insert(__f); }

      __ostream_type&
      operator<<(float __f)
      {
        // _GLIBCXX_RESOLVE_LIB_DEFECTS
        // 117. basic_ostream uses nonexistent num_put member functions.
        return _M_insert(static_cast<double>(__f));
      }

      __ostream_type&
      operator<<(long double __f)
      { return _M_insert(__f); }

      __ostream_type&
      operator<<(const void* __p)
      { return _M_insert(__p); }

数値に関する operator<< はひたすら _M_insert を呼び出す。これらはメンバ関数として定義されている。

  template<typename _CharT, typename _Traits>
    template<typename _ValueT>
      basic_ostream<_CharT, _Traits>&
      basic_ostream<_CharT, _Traits>::
      _M_insert(_ValueT __v)
      {
        sentry __cerb(*this);
        if (__cerb)
          {
            ios_base::iostate __err = ios_base::iostate(ios_base::goodbit);
            try
              {
                const __num_put_type& __np = __check_facet(this->_M_num_put);
                if (__np.put(*this, *this, this->fill(), __v).failed())
                  __err |= ios_base::badbit;
              }
            catch(__cxxabiv1::__forced_unwind&)
              {
                this->_M_setstate(ios_base::badbit);    
                __throw_exception_again;
              }
            catch(...)
              { this->_M_setstate(ios_base::badbit); }
            if (__err)
              this->setstate(__err);
          }
        return *this;
      }   

_M_num_put を使って数値を書き込んでるみたい。
this->fill() はスペースを返す。
次

  template<typename _CharT, typename _Traits>
    basic_ostream<_CharT, _Traits>&
    operator<<(basic_ostream<_CharT, _Traits>& __out, const char* __s)
    {
      if (!__s)
        __out.setstate(ios_base::badbit);
      else
        {
          // _GLIBCXX_RESOLVE_LIB_DEFECTS
          // 167.  Improper use of traits_type::length()
          const size_t __clen = char_traits<char>::length(__s);
          try
            {
              struct __ptr_guard
              {
                _CharT *__p;
                __ptr_guard (_CharT *__ip): __p(__ip) { }
                ~__ptr_guard() { delete[] __p; }
                _CharT* __get() { return __p; }
              } __pg (new _CharT[__clen]);

              _CharT *__ws = __pg.__get();
              for (size_t  __i = 0; __i < __clen; ++__i)
                __ws[__i] = __out.widen(__s[__i]);
              __ostream_insert(__out, __ws, __clen);
            }
          catch(__cxxabiv1::__forced_unwind&)
            {
              __out._M_setstate(ios_base::badbit);
              __throw_exception_again;
            }
          catch(...)
            { __out._M_setstate(ios_base::badbit); }
        }
      return __out;
    }

文字列の operator<< は、 __ostream_insert を呼び出す。
widen は char を char_type に拡張するためのもの。
__ptr_guard は、メモリを確保してスコープを抜けるときに自動で解放するためのもの。
こっちは、メンバ関数としてじゃなくて std::operator<< として定義さいれている。
次

  template<typename _CharT, typename _Traits>
    basic_ostream<_CharT, _Traits>&
    __ostream_insert(basic_ostream<_CharT, _Traits>& __out,
                     const _CharT* __s, streamsize __n)
    {
      typedef basic_ostream<_CharT, _Traits>       __ostream_type;
      typedef typename __ostream_type::ios_base    __ios_base;

      typename __ostream_type::sentry __cerb(__out);
      if (__cerb)
        {
          try 
            {   
              const streamsize __w = __out.width();
              if (__w > __n)
                {
                  const bool __left = ((__out.flags()
                                        & __ios_base::adjustfield)
                                       == __ios_base::left);
                  if (!__left)
                    __ostream_fill(__out, __w - __n);
                  if (__out.good())
                    __ostream_write(__out, __s, __n);
                  if (__left && __out.good())
                    __ostream_fill(__out, __w - __n);
                }
              else
                __ostream_write(__out, __s, __n);
              __out.width(0);
            }
          catch(__cxxabiv1::__forced_unwind&)
            {
              __out._M_setstate(__ios_base::badbit);
              __throw_exception_again;
            }
          catch(...)
            { __out._M_setstate(__ios_base::badbit); }
        }
      return __out;
    }

__left のところは、左右に空白を埋めるかどうかの指定。
__ostream_write を呼び出している
次

  template<typename _CharT, typename _Traits>
    inline void
    __ostream_fill(basic_ostream<_CharT, _Traits>& __out, streamsize __n)
    {   
      typedef basic_ostream<_CharT, _Traits>       __ostream_type;    
      typedef typename __ostream_type::ios_base    __ios_base;

      const _CharT __c = __out.fill();
      for (; __n > 0; --__n)
        {
          const typename _Traits::int_type __put = __out.rdbuf()->sputc(__c);
          if (_Traits::eq_int_type(__put, _Traits::eof()))
            {
              __out.setstate(__ios_base::badbit);
              break;
            }   
        }   
    }

おおお。 sputc を呼んでるー。

istream

結構 ostream と同じ

  template<typename _CharT, typename _Traits>
    typename basic_istream<_CharT, _Traits>::int_type
    basic_istream<_CharT, _Traits>::
    peek(void)
    {
      int_type __c = traits_type::eof();
      _M_gcount = 0;
      sentry __cerb(*this, true);
      if (__cerb)
        {
          ios_base::iostate __err = ios_base::iostate(ios_base::goodbit);
          try
            {
              __c = this->rdbuf()->sgetc();
              if (traits_type::eq_int_type(__c, traits_type::eof()))
                __err |= ios_base::eofbit;
            }
          catch(__cxxabiv1::__forced_unwind&)
            {
              this->_M_setstate(ios_base::badbit);
              __throw_exception_again;
            }
          catch(...)
            { this->_M_setstate(ios_base::badbit); }
          if (__err)
            this->setstate(__err);
        }
      return __c;
    }

peek は先頭の一文字を読み込む、 sgetc なのでポインタは進まない
__cerb は mutex でロックをかけている
次

  template<typename _CharT, typename _Traits>
    basic_istream<_CharT, _Traits>&
    basic_istream<_CharT, _Traits>::
    read(char_type* __s, streamsize __n)
    {
      _M_gcount = 0;
      sentry __cerb(*this, true);
      if (__cerb)
        {
          ios_base::iostate __err = ios_base::iostate(ios_base::goodbit);
          try
            {
              _M_gcount = this->rdbuf()->sgetn(__s, __n);
              if (_M_gcount != __n)
                __err |= (ios_base::eofbit | ios_base::failbit);
            }
          catch(__cxxabiv1::__forced_unwind&)
            {
              this->_M_setstate(ios_base::badbit); 
              __throw_exception_again;
            }
          catch(...)
            { this->_M_setstate(ios_base::badbit); }
          if (__err)
            this->setstate(__err);
        }
      return *this;
    }

複数の文字を読み込んでいる sgetn なのでポインタは進まない
次
get や getline は snextc とかを使ってる
次

  template<typename _CharT, typename _Traits>
    basic_istream<_CharT, _Traits>&
    basic_istream<_CharT, _Traits>::
    operator>>(__streambuf_type* __sbout)
    {
      ios_base::iostate __err = ios_base::iostate(ios_base::goodbit);
      sentry __cerb(*this, false);
      if (__cerb && __sbout)
        {
          try
            {
              bool __ineof;
              if (!__copy_streambufs_eof(this->rdbuf(), __sbout, __ineof))
                __err |= ios_base::failbit;
              if (__ineof)
                __err |= ios_base::eofbit;
            }
          catch(__cxxabiv1::__forced_unwind&)
            {
              this->_M_setstate(ios_base::failbit);
              __throw_exception_again;
            }
          catch(...)
            { this->_M_setstate(ios_base::failbit); }
        }
      else if (!__sbout)
        __err |= ios_base::failbit;
      if (__err)
        this->setstate(__err);
      return *this;
    }

バッファをそのまま copy とか
次

  template<typename _CharT, typename _Traits>
    basic_istream<_CharT, _Traits>&
    basic_istream<_CharT, _Traits>::
    operator>>(int& __n)
    {
      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // 118. basic_istream uses nonexistent num_get member functions.
      long __l;
      _M_extract(__l);
      if (!this->fail())
        {
          if (__gnu_cxx::__numeric_traits<int>::__min <= __l
              && __l <= __gnu_cxx::__numeric_traits<int>::__max)
            __n = int(__l);
          else
            this->setstate(ios_base::failbit);
        }
      return *this;
    }

数値系の読み込みは _M_extract を使う
次

  template<typename _CharT, typename _Traits>
    template<typename _ValueT>
      basic_istream<_CharT, _Traits>&
      basic_istream<_CharT, _Traits>::
      _M_extract(_ValueT& __v)
      {
        sentry __cerb(*this, false);
        if (__cerb)
          {
            ios_base::iostate __err = ios_base::iostate(ios_base::goodbit);
            try
              {
                const __num_get_type& __ng = __check_facet(this->_M_num_get);
                __ng.get(*this, 0, *this, __err, __v);
              }
            catch(__cxxabiv1::__forced_unwind&)
              {
                this->_M_setstate(ios_base::badbit);
                __throw_exception_again;
              }
            catch(...)
              { this->_M_setstate(ios_base::badbit); }
            if (__err)
              this->setstate(__err);
          }
        return *this;
      }

文字から数値への変換は _M_num_get のお仕事
次

  template<typename _CharT, typename _Traits>
    basic_istream<_CharT, _Traits>&
    operator>>(basic_istream<_CharT, _Traits>& __in, _CharT* __s)
    {
      typedef basic_istream<_CharT, _Traits>            __istream_type;
      typedef basic_streambuf<_CharT, _Traits>          __streambuf_type;
      typedef typename _Traits::int_type                int_type;
      typedef _CharT                                    char_type;
      typedef ctype<_CharT>                             __ctype_type;

      streamsize __extracted = 0;
      ios_base::iostate __err = ios_base::iostate(ios_base::goodbit);
      typename __istream_type::sentry __cerb(__in, false);
      if (__cerb)
        {
          try
            {
              // Figure out how many characters to extract.
              streamsize __num = __in.width();
              if (__num <= 0)
                __num = __gnu_cxx::__numeric_traits<streamsize>::__max;

              const __ctype_type& __ct = use_facet<__ctype_type>(__in.getloc());

              const int_type __eof = _Traits::eof();
              __streambuf_type* __sb = __in.rdbuf();
              int_type __c = __sb->sgetc();

              while (__extracted < __num - 1
                     && !_Traits::eq_int_type(__c, __eof)
                     && !__ct.is(ctype_base::space,
                                 _Traits::to_char_type(__c)))
                {
                  *__s++ = _Traits::to_char_type(__c);
                  ++__extracted;
                  __c = __sb->snextc();
                }
              if (_Traits::eq_int_type(__c, __eof))
                __err |= ios_base::eofbit;

              // _GLIBCXX_RESOLVE_LIB_DEFECTS
              // 68.  Extractors for char* should store null at end
              *__s = char_type();
              __in.width(0);
            }
          catch(__cxxabiv1::__forced_unwind&)
            {
              __in._M_setstate(ios_base::badbit);
              __throw_exception_again;
            }
          catch(...)
            { __in._M_setstate(ios_base::badbit); }
        }
      if (!__extracted)
        __err |= ios_base::failbit;
      if (__err)
        __in.setstate(__err);
      return __in;
    }

文字列の読み込みでは、 ctype_base::space までしか読み込まれない。ここでスペースが特別扱いされているから。 cin >> str; とやったときに単語ごとに取れるのかー。へーへー。

まとめ

とりあえず。
cout とか cin の挙動は basic_streambuf の実装次第ってことが分かった。
sync, underflow, overflow あたりの実装が重要そうな感じ
もうちょっと勉強するべきだな