如果不在派生类实现中执行此操作,"basic_streambuf"是否会创建自己的获取/放置区域?



我已经多次看到创建自定义 streambufs 的说明: 您需要做的就是在std::basic_streambuf的后代中正确实现overflowunderflowpbackfail,然后您可以创建一个使用它格式化数据的流。这三个例程定义了自定义流的"受控序列"。

std::basic_streambuf的保护成员名单中潜伏着其他怪物,即setgsetp。 它们为输入和输出设置缓冲区。 获取和设置数据的公共成员首先尝试访问这些区域,然后再跟踪受控序列。

对于几个不同的自定义流,如果流设置自己的获取/放置区域,则可能会出现麻烦。所以我希望这样的 streambufs 避免使用 get/put 区域,并始终使用overflowunderflowpbackfail,没有任何中间缓冲。

对于一个朴素的简化示例,如果您要包装另一个 streambuf,则underflow的实现可能如下所示:

template <class C, class TR>
typename TR::int_type wrapping_streambuf<C, TR>::underflow()
{
return m_wrapped_streambuf->sgetc();
}

让包裹的 streambuf 处理所有肮脏的工作。 这是另一个计算行数的幼稚示例:

template <class C, class TR>
typename TR::int_type tracking_streambuf<C, TR>::uflow()
{
auto rv = m_wrapped_streambuf->sbumpc();
if (rv == (TR::int_type)'n') ++ m_input_line_count;
return rv;
}

对于此类流,没有有用的setg实现,因为您无法获取包装缓冲区的内部获取区域。 对于tracked_streambuf,强加获取/放置区域将使计数线与流的逻辑序列同步,变得不可能。

我认为答案是永远不要在后代类中称呼setgsetp。事实上,它们可能应该覆盖setgsetpgbumppbump来抛出异常。

查看<streambuf>标题,我看到自定义流 如果我这样做,在我最喜欢的库中实现可能会以我想要的方式工作(有对空 gptr/pptr 的检查)。但这是一种保证吗?

std::basic_streambuf的默认构造函数将定义 get 和 put 区域的六个指针设置为 null 指针值,因此默认情况下它不会"创建自己的 get/put 区域"。

函数setgsetpgbumppbump是受保护的成员,默认情况下不会被公共成员函数调用,因此您不必担心它们。当然,覆盖它们以引发异常也不错。

此外,没有中间缓冲区的自定义流缓冲区类也应重写uflow函数,公共成员函数可以调用该函数来处理需要高级 get 指针值的溢出情况。默认情况下,其默认行为为(引自 [streambuf.virt.get]/16):

默认行为:调用underflow()。如果underflow()返回traits​::​eof(),则返回traits​::​eof()。否则,返回traits​::​to_­int_­type(*gptr())的值并递增输入序列的下一个指针的值。

因此,如果不重写此函数,则会导致通过 null 指针的间接寻址出现未定义的行为。

最新更新