27

我需要一些指导或指针来了解如何实现自定义 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 会分别通知谁?

4

4 回答 4

30

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<<(ProcessID const& pid),它存储进程 ID 直到它被写入。效果很好!

于 2013-01-09T10:03:59.837 回答
23

最简单的方法是仅继承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<<于用户定义的任何自定义类。

于 2012-12-04T13:32:20.233 回答
4

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

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

于 2012-12-04T13:56:01.610 回答
1

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

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

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

于 2012-12-04T14:29:01.850 回答