创建一个 std::iostream 适配器

Creating a std::iostream adapter

本文关键字:std iostream 适配器 一个 创建      更新时间:2023-10-16

我想创建一个iostream适配器类,它允许我即时修改写入流或从流读取的数据。适配器本身应该是一种iostream,以允许对第三方代码真正透明。

派生自std::ostreamStreamEncoder类示例:

// External algorithm, creates large amounts of log data
int foo(int bar, std::ostream& logOutput);
int main()
{
    // The target file
    std::ofstream file("logfile.lzma");
    // A StreamEncoder compressing the output via LZMA
    StreamEncoder lzmaEncoder(file, &encodeLzma);
    // A StreamEncoder converting the UTF-8 log data to UTF-16
    StreamEncoder utf16Encoder(lzmaEncoder, &utf8ToUtf16);
    // Call foo(), but write the log data to an LZMA-compressed UTF-16 file
    cout << foo(42, utf16Encoder);
}

据我所知,我需要创建一个新的basic_streambuf派生并将其嵌入到basic_ostream子类中,但这似乎非常复杂。

有没有更简单的方法来实现这一点?

奇怪的是,至少当事情真正打算工作时,这一切都不应该直接涉及iostreams和/或streambufs。

我会认为iostream是一个媒人类。iostream有一个streambuf,它为某种外部数据源/接收器提供了一个缓冲接口。它还有一个区域设置,用于处理所有格式。iostream只不过是让这两个人很好地一起玩耍的操场主管(可以这么说(。由于您正在处理数据格式,因此所有这些都(或应该(在区域设置中处理。

不过,区域设置不是单一的 - 它由许多facet组成,每个专用于数据格式的一个特定部分。在这种情况下,您可能关心的部分是codecvt方面,它(几乎专门(用于在从iostream读取/写入iostream的数据的外部和内部表示之间进行转换。

但是,无论好坏,区域设置一次只能包含一个编解码器分面,而不是像您正在考虑的那样包含它们的链。因此,您真正需要/想要的是一个包装类,它提供编解码器作为其外部接口,但允许您链接一些任意一组要在 I/O 期间对数据执行的转换。

对于utf

到utf的转换,Boost.locale提供了一个utf_to_utf函数和codecvt包装代码,因此完成这部分转换既简单又直接。

为了避免有人建议用ICU做这样的事情,我要补充一点,Boost.Locale几乎是ICU的包装器,所以这或多或少是相同的答案,但形式对C++更友好(而ICU本身相当类似于Java,并且几乎公开敌视C++(。

另一方面,编写编解码器方面会给相当简单的任务增加很多复杂性。过滤流(例如(通常编写起来要简单得多。它仍然不像您想要的那么容易,但并不像编解码器方面那么糟糕。正如@Flexo已经提到的,Boost iostreams库已经包含一个执行zip压缩的过滤streambuf。对 lzma(或 lzh、算术等压缩(做大致相同的操作相对容易,至少假设你有易于使用的压缩函数(你基本上只是为它们提供一个输入缓冲区,它们提供结果缓冲区(。