提问人:Uri London 提问时间:12/4/2012 最后编辑:CommunityUri London 更新时间:11/1/2017 访问量:23584
自定义 ostream
A custom ostream
问:
我需要一些指导或指针来了解如何实现自定义 ostream。我的要求是:
- 具有用于多种数据类型的“<<”运算符的类。
- 目的是将输出发送到数据库。每个“行”都应该转到单独的记录。
- 每条记录最重要的字段是文本(或 blob),但其他一些字段(如时间等)大多可以自动推断
- 缓冲很重要,因为我不想对每条记录都去数据库。
首先,是否值得从 ostream 中衍生出来?从 ostream 派生我能得到什么?如果我的类只是实现几个方法(包括一些自定义数据类型),该怎么办?我可以从 ostream 获得哪些功能?operator<<
假设我想要的是从 ostream 派生的类,我需要一些指导来理解 ostream 和 streambuf 类之间的关系。我需要实现哪一个?查看一些示例,似乎我根本不需要从 ostream 派生,只需给 ostream 构造函数一个自定义的 streambuf。这是真的吗?这是规范的方法吗?
我需要在自定义 streambuf 中实现哪些虚拟函数?我看过一些示例(包括这个网站:这里和这里,还有更多),一些覆盖了该方法,而另一些则覆盖了该方法。我应该覆盖哪一个?此外,查看 stringbuf 和 filebuf 源(Visual Studio 或 GCC),这两个缓冲区类都实现了 streambuf 的许多方法。sync
overflow
如果需要从 streambuf 派生的自定义类,那么从 stringbuf(或任何其他类)派生而不是直接从 streambuf 派生有什么好处吗?
至于“线条”。我希望至少当我使用“endl”操纵器的类的用户成为新行(即数据库中的记录)时。也许 - 取决于努力 - 每个“\n”字符也应该被视为新记录。我的自定义 ostream 和/或 streambuf 会收到哪些通知?
答:
最简单的方法是仅继承和重写两种方法:std::streambuf
std::streamsize xsputn(const char_type* s, std::streamsize n)
– 例如,将给定的缓冲区与提供的大小附加到您的内部缓冲区;std::string
int_type overflow(int_type c)
– 将单曲附加到您的内部缓冲区。char
你的 streambuf 可以从你想要的任何东西(例如数据库连接)构建。将某些内容附加到内部缓冲区后,您可以尝试将其拆分为几行并将某些内容推送到 DB 中(或者只是缓冲 SQL 语句以稍后执行)。
要使用它:只需将您的附加到任何 using 构造函数即可。streambuf
std::ostream
简单!我已经做了这样的事情来将字符串输出到 syslog - 一切都适用于用户定义的类的任何自定义。operator<<
评论
my2c - 我认为你以错误的方式解决这个问题。流听起来可能是个好主意,但你也需要一种方法来指示行的末尾(然后如果有人忘记了怎么办?我会建议一些类似于 java PreparedStatements 和批处理如何工作的东西,例如提供一组接受类型和列索引的方法,然后是一个“批处理”方法,它明确表明您确实正在批处理该行,然后执行以推入批处理。
任何基于流的操作都将依赖于类型(通常)来指示要填充的列 - 但是如果您有两个整数呢?IMO,作为用户,感觉不像是将记录插入数据库的自然方式......
若要向 iostreams 机制添加字符输入/输出的新源或目标,应创建一个新类。流缓冲区类的任务是与将存储字符的“外部设备”进行通信并提供缓冲设施。streambuf
使用 iostreams 与数据库通信的问题在于,数据库表与字符序列的概念不匹配。有点像在方孔中推一个圆钉。A 仅对字符进行操作。这是唯一呈现给它的东西。这意味着必须解析呈现给它的字符流以查找字段和记录分隔符。
如果你决定走这条路,我预测你最终会在你的 中编写一个 CSV 到 SQL 转换器,只是为了让它工作。streambuf
streambuf
streambuf
只需向类添加一些重载可能会更好。你可以在这里查看Qt框架的想法。它们还可以用于将项目添加到集合等。operator<<
operator<<
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()
是一个返回结构的方法。然后,在我的 ostream 的实现中,我有一个 ,它存储进程 ID,直到它被写入。效果很好!ProcessID
operator<<(ProcessID const& pid)
评论
MyStream
std::ostream
this->str("")
sync
overflow
flush()
overflow()
评论
streambuf
ostream
std::basic_ostream
streambuf