研究了半天总算有了一点概念。
下面是 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;
至于完整的流对象,构建应该相当简单,回头再放出。