创建std :: iostream适配器

时间:2012-07-15 15:13:45

标签: c++ iostream std

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

StreamEncoder派生的std::ostream类的示例:

// 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子类中,但这似乎相当复杂。

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

1 个答案:

答案 0 :(得分:4)

奇怪的是,至少在事情确实有效的情况下,这一切都不应该直接涉及到iostreams和/或streambuf。

我认为iostream是一个匹配者类。 iostream有一个streambuf,它为某种外部数据源/接收器提供缓冲接口。它还有一个区域设置,可以处理所有格式。 iostream不仅仅是游乐场的主管,让这两个人在一起玩得很好(可以这么说)。由于您正在处理数据格式化,因此所有这些都是(或应该)在语言环境中处理。

虽然语言环境不是单一的 - 它由许多facet组成,每个{s}都专门用于数据格式化的一个特定部分。在这种情况下,您可能关心的部分是codecvt facet,它(几乎专门)用于在从iostreams读取/写入的数据的外部和内部表示之间进行转换。

然而,无论好坏,一个语言环境一次只能包含一个 codecvt方面,而不是像你正在考虑的那样连锁。因此,您真正需要/想要的是一个包装类,它提供了一个codecvt作为其外部接口,但允许您在I / O期间链接一些任意的变换集。

对于utf-to-utf转换,Boost.locale提供了utf_to_utf函数和codecvt包装器代码,因此这部分转换操作简单明了。

为了避免有人建议用ICU做这些事情,我会补充说Boost.Locale几乎是ICU的包装器,所以这或多或少是相同的答案,但是对C ++更友好的形式(而ICU本身就像Java一样,而且几乎完全不喜欢C ++)。

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