一个自定义的ostream

时间:2012-12-04 13:15:19

标签: c++

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

  1. 带有'<<'的类几种数据类型的运算符。
  2. 目的是将输出发送到数据库。每条“线”都应该单独记录。
  3. 每个记录最重要的字段都是文本(或blob),但其他一些字段(如时间等)可以大部分自动推断
  4. 缓冲非常重要,因为我不想为每条记录访问数据库。
  5. 首先,是否值得从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 个答案:

答案 0 :(得分:20)

ostream的自定义目标意味着实现您自己的ostreambuf。如果您希望streambuf实际缓冲(即在每个字符后不连接到数据库),最简单的方法是创建一个继承自std::stringbuf的类。您需要覆盖的 only 函数是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直到它被写入。效果很棒!

答案 1 :(得分:17)

最简单的方法是继承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<<都能正常工作。

答案 2 :(得分:4)

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

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

答案 3 :(得分:1)

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

使用iostream与数据库通信的问题是数据库表与字符序列的概念不匹配。有点像在一个方孔中推一个圆钉。 streambuf仅对字符进行操作。这是唯一呈现给它的东西。这意味着streambuf必须解析呈现给它的字符流以查找字段和记录分隔符。 如果您决定采用这种方式,我预测您最终会在streambuf中编写一个CSV-to-SQL转换器,以使其正常工作。

如果只为您的类添加一些operator<<重载,您可能会更好。你可以在这里查看Qt框架的想法。他们还可以使用operator<<向集合中添加项目等。