【问题标题】:A custom ostream自定义 ostream
【发布时间】:2012-12-04 13:15:19
【问题描述】:

我需要一些指导或指针来了解如何实现自定义 ostream。我的要求是:

  1. 一个带有“
  2. 目的是将输出发送到数据库。每条“行”都应该有一个单独的记录。
  3. 每条记录最重要的字段是文本(或blob),但其他一些字段,如时间等,大多可以自动推导出来
  4. 缓冲很重要,因为我不想为每条记录都去数据库。

首先,是否值得从 ostream 派生?我从 ostream 派生得到什么?如果我的类只实现了几个operator<< 方法(包括一些自定义数据类型)怎么办。我可以从 ostream 获得哪些功能?

假设我想要的是一个从 ostream 派生的类,我需要一些指导来理解 ostream 和 streambuf 类之间的关系。我需要实施哪一项?查看一些示例,似乎我根本不需要从 ostream 派生,只需给 ostream 构造函数一个自定义的 streambuf。真的吗?这是规范的方法吗?

我需要在自定义 streambuf 中实现哪些虚函数?我看过一些示例(包括这个站点:herehere,等等),一些覆盖了 sync 方法,还有一些覆盖了 overflow 方法。我应该覆盖哪一个?此外,查看 stringbuf 和 filebuf 源(Visual Studio 或 GCC),这两个缓冲区类都实现了 streambuf 的许多方法。

如果需要从 streambuf 派生的自定义类,那么从 stringbuf(或任何其他类)而不是直接从 streambuf 派生是否有任何好处?

至于“线”。我希望至少当我的班级用户使用“endl”操纵器成为一个新行(即数据库中的记录)时。也许 - 取决于努力 - 每个 '\n' 字符也应该被视为一个新记录。我的自定义 ostream 和/或 streambuf 会分别通知谁?

【问题讨论】:

  • 您可能应该创建自己的streambuf 类来处理所有繁重的工作,然后创建一个非常简单的ostream 类,它继承std::basic_ostream 并使用您的streambuf 对象进行初始化.
  • 您应该查看Boost.Iostreams - 它使创建自定义流和缓冲区变得更加简单。
  • 感谢您的编辑,@MarkusParker

标签: c++


【解决方案1】:

ostream 的自定义目标意味着实现您自己的 ostreambuf。如果您希望您的 streambuf 实际缓冲(即不要在每个字符之后连接到数据库),最简单的方法是创建一个继承自 std::stringbuf 的类。您需要重写的唯一函数是sync() 方法,每当刷新流时都会调用该方法。

class MyBuf : public std::stringbuf
{
public:
    virtual int sync() {
        // add this->str() to database here
        // (optionally clear buffer afterwards)
    }
};

然后您可以使用缓冲区创建std::ostream

MyBuf buff;
std::ostream stream(&buf)

大多数人建议不要将流重定向到数据库,但他们忽略了我的描述,即数据库基本上只有一个 blob 字段,所有文本都将转到该字段。 在极少数情况下,我可能会将数据发送到不同的字段。这可以通过我的流理解的自定义属性来促进。例如:

MyStream << "Some text " << process_id(1234) << "more text" << std::flush

上面的代码将在数据库中创建一条记录:

blob: 'Some text more text'
process_id: 1234

process_id() 是一个返回结构ProcessID 的方法。然后,在我的 ostream 的实现中,我有一个 operator&lt;&lt;(ProcessID const&amp; pid),它存储进程 ID 直到它被写入。效果很好!

【讨论】:

  • 您的MyStream 类是否继承自std::ostream?它会覆盖任何方法吗?我问这个是因为我收到一个错误,指出构造函数受到保护
  • 我发现“清除缓冲区”的最佳方法是this-&gt;str("")
  • 即使您不刷新流,sync 是否也会在一段时间后被调用?我知道 fstream 会发生这种情况。
  • @maxbc - 如果“一段时间后”是指在一定数量的字节之后,那么是的。
  • @Uri 我当时不明白的是,为什么我们需要一个overflow 函数?没有可用空间 -> flush() -> 将字符放入 stringbuf。为什么还需要overflow()
【解决方案2】:

最简单的方法是继承std::streambuf 并仅覆盖两个方法:

  • std::streamsize xsputn(const char_type* s, std::streamsize n) – 附加一个给定的缓冲区,其大小提供给您的内部缓冲区,例如std::string
  • int_type overflow(int_type c) - 将单个 char 附加到您的内部缓冲区。

您的 streambuf 可以根据您的需要构建(例如 DB 连接)。将某些内容附加到内部缓冲区后,您可以尝试将其拆分为行并将某些内容推送到 DB(或只是缓冲 SQL 语句以供稍后执行)。

要使用它:只需使用构造函数将您的streambuf 附加到任何std::ostream

简单!我已经完成了类似的操作来将字符串输出到 syslog - 对于用户定义的类,任何自定义 operator&lt;&lt; 都可以正常工作。

【讨论】:

  • 不会实现 xsputn 破坏 streambuf 的所有目的吗? filebuf 和 stringbuf 都不会覆盖这个方法,只会溢出(被 stringbuf 调用)。
  • 实际上只有溢出方法被覆盖。默认情况下,sputn 对每个字符执行 sputc。
【解决方案3】:

my2c - 我认为你处理这个问题的方式是错误的。流听起来可能是个好主意,但是您也需要一种方法来指示行的末尾(如果有人忘记了怎么办?)我会建议一些类似于 java PreparedStatements 和批处理如何工作的内容,如提供一组接受类型和列索引的方法,然后提供一个“批处理”方法,明确表明您确实在批处理该行,然后执行以将批处理推入。

任何基于流的操作都将依赖于类型(通常)来指示要填充哪一列 - 但是如果您有两个整数怎么办? IMO,作为用户,将记录插入数据库并不是一种自然的方式......

【讨论】:

    【解决方案4】:

    要将字符输入/输出的新源或目标添加到 iostreams 机制,您应该创建一个新的streambuf 类。流缓冲区类的任务是与将存储字符的“外部设备”通信并提供缓冲设施。

    使用 iostreams 与数据库通信的问题是数据库表与字符序列的概念不匹配。有点像在方孔中推一个圆钉。 streambuf 只对字符起作用。这是唯一呈现给它的东西。这意味着streambuf 必须解析呈现给它的字符流以找到字段和记录分隔符。 如果你决定走这条路,我预测你最终会在streambuf 中编写一个 CSV 到 SQL 转换器,只是为了让它工作。

    您可能会更好地为您的类添加一些operator&lt;&lt; 重载。您可以在此处查看 Qt 框架的想法。他们还可以使用operator&lt;&lt; 将项目添加到集合等。

    【讨论】:

      猜你喜欢
      • 2010-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多