也谈构建自己的标准流缓冲 【操作Pipe的流缓冲】

研究了半天总算有了一点概念。

下面是 basic_streambuf 里和缓冲区相关的几个成员函数。

对于输入缓冲区:
eback() : 指向输入缓冲起始的位置
gptr() : 返回指向下一个读取元素所在的位置
egptr() : 返回指向最后一个可读元素(注意,这个指向的不是你创建的缓冲区的结尾)

// 对于输出缓冲区
pbase(), 指向输出缓冲的起始位置
pptr(), 指向下一个可写的位置
epptr(), 指向输出缓冲的末尾(也就是你创建的输出缓冲区的末尾)

其他不多说看代码,这个是吧 Windows 的 Pipe (管道) 操作封装在流里面的例子 :

template<class _Elem, class _Traits>
class basic_pipebuf : public std::basic_streambuf<_Elem, _Traits>
{
private:
 typedef basic_streambuf<_Elem, _Traits> _Mysb;
public:
 basic_pipebuf( HANDLE handle=INVALID_HANDLE_VALUE ) : _handle_pipe(INVALID_HANDLE_VALUE)
 {
  //_Mysb::_Init();
  open(handle);
 }
 ~basic_pipebuf()
 {
  close();
  if( pbase() ) delete [] pbase();
  if( eback() ) delete [] eback();
 }
 
 bool open( HANDLE handle )
 {
  close();
  if( handle==INVALID_HANDLE_VALUE ) return false;
  return (DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), &_handle_pipe, 0, FALSE, DUPLICATE_SAME_ACCESS)==TRUE);
 }
 void close()
 {
  if( _handle_pipe!=INVALID_HANDLE_VALUE ){
   CloseHandle(_handle_pipe); 
   _handle_pipe=INVALID_HANDLE_VALUE;
  }
 }
 bool is_open(){ return _handle_pipe!=INVALID_HANDLE_VALUE; }
public:
 static const int pipe_buffer_size=1024; // 缓冲区大小
protected:
 void init_out_buf()
 {
  _Elem* _out = new _Elem[pipe_buffer_size];
  setp( _out, _out + pipe_buffer_size);
 }
 void init_in_buf()
 {
  _Elem* _in = new _Elem[pipe_buffer_size];
  setg(_in, _in, _in);
 }
 // 把输出缓冲区的数据写入管道
 virtual int sync()
 {
  if(( !is_open() ) || (pptr()==pbase())) return 0;
  // 写入
  DWORD cbWritten=0; // 写入的字节数 
  BOOL fSuccess = WriteFile( 
   _handle_pipe,                // 管道句柄
   pbase(),       // 缓冲开始位置
   (DWORD)(pptr()-pbase()) * sizeof(_Elem),    // 数据的长度
   &cbWritten,     // 写入的字节数
   NULL);       // 不使用 overlapped (交迭)
  if (fSuccess){ 
   // 如果成功,清空缓冲,并且返回写入的字节数
   setp( pbase(), pbase(), pbase() );
   return (int)(cbWritten / sizeof(_Elem));
  }
  return 0;
 }
 // 下溢,当输入缓存中的内容不够时被调用,也就是 sgetc 失败的时候
 virtual int_type underflow()
 {
  // 如果输入缓存还没被构建,构建缓存
  if(egptr() == 0)  init_in_buf();
  // 从管道读取数据填充流缓存
  DWORD cbRead=0;
  BOOL fSuccess=ReadFile( 
    _handle_pipe,  // 管道句柄
    eback(),    // 输入缓冲开始位置
    pipe_buffer_size * sizeof(_Elem),  // 缓冲大小
    &cbRead,   // 读取的字节数
    NULL);    // 不使用 overlapped (交迭)
  if( !fSuccess ){
   DWORD err=GetLastError();
   switch( err ){
    case ERROR_PIPE_NOT_CONNECTED: // 管道被关闭
     return _Traits::eof();
    case ERROR_MORE_DATA: // 管道中还有更多数据 ( 这里不太确定,这个错误似乎不会在 fSuccess==FALSE 的时候出现,也许可以移除? )
     break;
    default:
     throw err; // 对于异常,流基类会捕获并且设置为 badbit
   }
  }
  assert( cbRead >0 );
  assert( (cbRead % sizeof(_Elem))==0 );
  // 设置输入缓冲的指针
  setg( eback(), eback(), eback()+ ( cbRead / sizeof(_Elem) ) );
  return _Traits::to_int_type(*gptr()); // 返回第一个字符
 }
 virtual int_type overflow( int_type c = _Traits::eof() )  
 {
  //当sputc失败的时候, 会调用overflow
  //如果c不是eof(), 本函数尝试将traits::to_char_type(c)插入put buffer当中
  if(pbase() == 0) // 如果缓冲还未被创建
   init_out_buf();
  else if(sync()==0) // 清空缓冲
   throw NULL; // 如果清空失败,抛出异常,流状态会被设置为 badbit
  if (_Traits::eq_int_type(_Traits::eof(), c)) // 如果 c 是 eof
   return _Traits::not_eof(c);
  
  return sputc(_Traits::to_char_type(c));  //重新把 c 放入输出缓冲区
 }
private:
 std::string _name;
 HANDLE _handle_pipe;
};
typedef basic_pipebuf< char, std::char_traits<char> > pipebuf;
typedef basic_pipebuf< wchar_t, std::char_traits<wchar_t> > wpipebuf;

至于完整的流对象,构建应该相当简单,回头再放出。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值