POCO C++库学习和分析 -- 流 (二)

这篇博客深入探讨了POCO C++库中关于流的实现,包括如何创建自定义流,如BasicUnbufferedStreamBuf、BasicBufferedStreamBuf和BasicBufferedBidirectionalStreamBuf。同时,详细介绍了Base64和HexBinary编码解码的原理及Poco库中的实现,如Poco::Base64Encoder、Poco::Base64Decoder、Poco::HexBinaryEncoder和Poco::HexBinaryDecoder,并通过类图展示了它们的结构。文中还通过示例解释了如何使用这些编码解码器进行数据转换。
摘要由CSDN通过智能技术生成

POCO C++库学习和分析 -- 流 (二)


2. 创建自己的流

2.1 自定义流的结构

          在Poco中提供了一个框架用于创建自己的流,并且创建的流都符合C++标准。想一下标准库中流的层次和结构。每一个流都必须有对应的流缓冲,并且在流初始化时提供此流缓冲的指针。Poco中提供了3种流缓冲供选择,分别是BasicBufferedStreamBuf、BasicUnbufferedStreamBuf、BasicBufferedBidirectionalStreamBuf。首先来看一个例子:

#include "Poco/UnbufferedStreamBuf.h"
#include <ostream>
#include <cctype>
class UpperStreamBuf: public UnbufferedStreamBuf
{
public:
	UpperStreamBuf(std::ostream& ostr): _ostr(ostr)
	{}
protected:
	int writeToDevice(char c)
	{
		_ostr.put(toupper(c));
		return charToInt(c);
	}
private:
	std::ostream& _ostr;
};

class UpperIOS: public virtual std::ios
{
public:
	UpperIOS(std::ostream& ostr): _buf(ostr)
	{
		poco_ios_init(&_buf);
	}
protected:
	UpperStreamBuf _buf;
};

class UpperOutputStream: public UpperIOS, public std::ostream
{
public:
	UpperOutputStream(std::ostream& ostr):
	  UpperIOS(ostr),
		  std::ostream(&_buf)
	  {}
};

int main(int argc, char** argv)
{
	UpperOutputStream upper(std::cout);
	upper << "Hello, world!" << std::endl;
	return 0;
}

          从上面的例子中,我们看到用户自定流缓冲类型UpperStreamBuf,并自定义流类型UpperIOS和UpperOutputStream。其中UpperIOS从std::ios虚拟继承。注意这里是虚拟继承,这是为了保证UpperOutputStream多重继承时的菱形结构(std::ostream也是从std::ios虚拟继承的)。再回忆一下,std::ios的作用,其中最重要的就是定义了缓冲区指针。那么很自然的,定义UpperIOS,并让UpperOutputStream从UpperIOS继承的原因,就是为了让UpperOutputStream类拥有自己的缓冲区。再来看从流std::ostream继承的原因,很简单,为了继承"<<"操作符。那么清楚了,如果是输入流,从std::istream继承;是输入输出流从std::iostream继承。

          再从上面的例子看:为什么说,Poco中流框架提供的是一个中介流的框架。当创建UpperOutputStream对象时,我们看它做了什么.
          在调用UpperOutputStream upper(std::cout)时,传入参数是一个std::cout,这是一个已定义的流对象。这个流对象被赋予给UpperIOS类,用来初始化UpperIOS类中的UpperStreamBuf对象;而UpperStreamBuf又并不负责直接输出,它的输出依赖于内部的std::ostream对象,在这里就是std::cout。另一方面由于UpperOutputStream对象同时也继承自std::ostream,而std::ostream必须要求用一个流缓冲对象来初始化,所以在UpperIOS类中的UpperStreamBuf对象初始化后,又被赋给std::ostream对象用作初始化。

          再来看 "upper << "Hello, world!" << std::endl "语句的执行过程:
          1. "<<"操作符是std::ostream的成员函数。也就是说 "Hello, world!"被首先调用了std::ostream函数"<<"。
          2.  我们知道,流的输入输出是委托给其内部的缓冲区对象的。也就是说,在这里"<<"操作符,会委托到std::ostream类内部关联的流缓冲UpperIOS::UpperStreamBuf对象_buf上。
          2. UpperStreamBuf类型的_buf对象并不输出,它把输出任务继续委托给内部的流对象_ostr。在程序里,_ost对象为std::cout。
          3. std::cout对象在收到数据后,委托给std::cout内部关联的流缓冲对象,最终输出。至于std::cout对象关联的流对象是什么,那就不用去管了。这是系统内部实现的。

          转了半天,输出最终还是依赖于系统的默认输出。如果仅是如此,就没有意思了。Poco流在类BasicBufferedStreamBuf、BasicUnbufferedStreamBuf、BasicBufferedBidirectionalStreamBuf上开了一个小口,供用户自定义中间操作。这两个小口就是:
virtual int readFromDevice(char_type* buffer, std::streamsize length);
virtual int writeToDevice(const char_type* buffer, std::streamsize length);


          通过子类继承,用户可以自定义转换行为。任何从Poco流自定义框架出来的类都必须重新定义这两个虚函数。readFromDevice和writeToDevice会在std::ostream中的虚函数overflow和underflow中被调用。

          在了解了Poco自定义流类的流转次序后,再去看Poco中的自定义流Base64Decoder,Base64Encoder等的数据流转,会发现过程都是类似的。下面就不再对这一方面进行叙述。只是讲一下Poco中已定义的3个流缓冲类BasicBufferedStreamBuf、BasicUnbufferedStreamBuf、BasicBufferedBidirectionalStreamBuf。


2.2 BasicUnbufferedStreamBuf

          Poco::BasicUnbufferedStreamBuf是一个使用char作为模板参数类型的偏特化模板类。其完整定义为:

template <typename ch, typename tr> 
class BasicUnbufferedStreamBuf: public std::basic_streambuf<ch, tr>
	/// This is an implementation of an unbuffered streambuf
	/// that greatly simplifies the implementation of
	/// custom streambufs of various kinds.
	/// Derived classes only have to override the methods
	/// readFromDevice() or writeToDevice().
{
	// .....
	int_type _pb;
	bool     _ispb;
}

typedef BasicUnbufferedStreamBuf<char, std::char_traits<char> > UnbufferedStreamBuf;

          Poco::BasicUnbufferedStreamBuf可以说是最简单的创建用户自定流的方法,在其内部没有任何缓冲对象。从Poco::BasicUnbufferedStreamBuf继承的类需要重载readFromDevice和writeToDevice接口。

          对应重载的要求如下:
     int readFromDevice()
          读取并返回单字节无符号字符。如果没有更多的合适数据时,返回char_traits::eof()(-1)。需要注意的是,不要直接返回一个char类型值,因为char类型值是一个有符号数。调用int charToInt(char c)返回一个char转换到int的值。
     int writeToDevice(char c)
          写入一个单字节字符,如果成功返回写入的字符(已整形方式),否则返回char_traits::eof() (-1).

          在Poco中从UnbufferedStreamBuf继承的类见下图:



2.3 BasicBufferedStreamBuf

          Poco::BasicBufferedStreamBuf也同样是使用char作为模板参数类型的偏特化模板类。其完整定义为:

template <typename ch, typename tr, typename ba = BufferAllocator<ch> > 
class BasicBufferedStreamBuf: public std::basic_streambuf<ch, tr>
	/// This is an implementation of a buffered streambuf
	/// that greatly simplifies the implementation of
	/// custom streambufs of various kinds.
	/// Derived classes only have to override the methods
	/// readFromDevice() or writeToDevice().
	///
	/// This streambuf only supports unidirectional streams.
	/// In other words, the BasicBufferedStreamBuf can be
	/// used for the implementation of an istream or an
	/// ostream, but not for an iostream.
{
	// ...
	std::streamsize _bufsize;
	char_type*      _pBuffer;
	openmode        _mode;
}

typedef BasicBufferedStreamBuf<char, std::char_traits<char> > BufferedStreamBuf;

          在BasicBufferedStreamBuf内部存在一个_pBuffer,用作缓冲,供输出使用。Poco::BufferedStreamBuf的实例支持读写,但不支持同时读写。从Poco::BasicBufferedStreamBuf继承的类需要重载readFromDevice和writeToDevice接口。

          对应重载的要求如下:  
     int readFromDevice(char* buffer, std::streamsize length)
          读取指定长度的字符,并把它们放置在buffer中。返回读到的字节数,或者-1(当错误发生时)
     int writeToDevice(const char* buffer, std::streamsize length)
          写入buffer中指定数目的字节。返回读到的字节数,或者-1(当错误发生时)


          在Poco中从BasicBufferedStreamBuf继承的类见下图:




2.4 BasicBufferedBidirectionalStreamBuf

          Poco::BasicBufferedBidirectionalStreamBuf一样是使用char作为模板参数类型的偏特化模板类。其完整定义为:

template <typename ch, typename tr, typename ba = BufferAllocator<ch> > 
class BasicBufferedBidirectionalStreamBuf: public std::basic_streambuf<ch, tr>
	/// This is an implementation of a buffered bidirectional 
	/// streambuf that greatly simplifies the implementation of
	/// custom streambufs of various kinds.
	/// Derived classes only have to override the methods
	/// readFromDevice() or writeToDevice().
	///
	/// In contrast to BasicBufferedStreambuf, this class supports
	/// simultaneous read and write access, so in addition to
	/// istream and ostream this streambuf can also be used
	/// for implementing an iostream.
{
	// ....
	std::streamsize _bufsize;
	char_type*      _pReadBuffer;
	char_type*      _pWriteBuffer;
	openmode        _mode;
}

typedef BasicBufferedBidirectionalStreamBuf<char, std::char_traits<char> > BufferedBidirectionalStreamBuf;

          在BasicBufferedBidirectionalStreamBuf内部存在两个缓冲,_pReadBuffer和_pWriteBuffer,作为缓冲,分别供输入或输出使用。
          Poco::BasicBufferedBidirectionalStreamBuf的实例支持同时读写。从Poco::BasicBufferedBidirectionalStreamBuf继承的类需要重载readFromDevice和writeToDevice接口。

          对应重载的要求如下:
     int readFromDevice(char* buffer, std::streamsize length)
          读取指定长度的字符,并把它们放置在buffer中。返回读到的字节数,或者-1(当错误发生时)
     int writeToDevice(const char* buffer, std::streamsize length)
          写入buffer中指定数目的字节。返回读到的字节数,或者-1(当错
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值