安全地重载流运算符>>

16

有大量关于重载 operator<< 以模仿 toString() 风格的方法将一个复杂对象转换为字符串的信息可用。 我也有兴趣实现反向操作,即使用 operator>> 将字符串反序列化为对象。

通过检查 STL 源代码,我已经收集到:

istream &operator>>(istream &, Object &);

哪种函数签名是正确的,可以反序列化一个类型为Object的对象。不幸的是,我一直不知道如何正确实现这个函数——特别是如何处理错误:

  1. 如何在流中指示无效数据?抛出异常?
  2. 如果流中存在格式不正确的数据,应该处于什么状态?
  3. 在返回操作符链引用之前,是否应重置任何标志位?
3个回答

19
  1. 如何指示流中的无效数据?抛出一个异常吗?

您应该设置fail位。如果流的用户想要抛出异常,他可以配置流(使用istream::exceptions),然后流将相应地抛出异常。然后我会这样做。

stream.setstate(ios_base::failbit);
  1. 如果流中存在格式错误的数据,它应该处于什么状态?

对于不符合所需读取格式的格式错误数据,通常应该设置fail位。对于内部流特定错误,使用bad位(例如,如果未连接到流的缓冲区)。

  1. 在返回操作符连锁的引用之前,是否应该重置任何标志位?

我没有听说过这种情况。


要检查流是否处于良好状态,可以使用istream::sentry类。创建一个对象,将流和true传递给它(告诉它不要立即跳过空格)。如果设置了eoffailbad位,则sentry将计算为false

istream::sentry s(stream, true);
if(!s) return stream;
// now, go on extracting data...

1
在尝试执行任何操作之前,请确保您检查了“失败”位。 如果已经设置,只需返回流即可。 - KTC
1
谢谢您的建议,特别是使用“失败”位而不是异常。除了设置“失败”位之外,我是否需要对流的内容做出任何保证?例如,如果我设置了“失败”位,流是否应该保持不变? - Michael Koval
2
不从流中读取任何字符来处理格式错误的输入几乎是不可能的。你需要任意回溯。通常的行为是消耗有效的前缀。 - AProgrammer
1
在operator>>中,就像对于简单类型所做的那样(起初尽管浮点数是唯一的情况),返回失败并消费有效前缀。在用户代码中,您可以通过从stringstream中读取完整行然后解析该行来为用户提供更好的诊断。但是,在进行设计用于错误报告的完整词法分析器/解析器时,有时需要这样做。 - AProgrammer
1
我只会使用有效的前缀,然后保持不变。如果需要尝试解析而不实际消耗任何内容,则不再使用iostreams。它们对于此类操作过于不灵活。我将使用C流或iostreams的低级“读取”接口,并创建自己的预读缓冲区。 - Johannes Schaub - litb
显示剩余2条评论

2
一些额外的说明:
  • 实现operator>>时,您可能应该考虑使用bufstream而不是其他重载的operator>>;

  • 操作期间发生的异常应转换为failbit或badbit(取决于所使用的类,streambuf的成员可能会抛出异常);

  • 设置状态可能会引发异常;如果您在捕获异常后设置状态,则应传播原始异常而不是由setstate引发的异常;

  • 宽度是一个需要注意的字段。如果您正在考虑它,您应该将其重置为0。如果您正在使用其他operator>>来执行基本工作,则必须从接收到的宽度计算您传递的宽度;

  • 考虑考虑区域设置。

Lange和Kreft(Standard C++ IOStreams和Locales)更详细地介绍了这一点。他们提供了一个错误处理的模板代码,大约需要一页。

1
为什么您建议使用bustream而不是其他重载(例如istream)?我一定会查看Lange和Kreft的工作 - 它听起来就像我正在寻找的东西。 - Michael Koval
嗨,我看到Michael和AProgrammer都不太活跃,所以如果有其他人看到这个问题:为什么bustream比overloading更好? - Tomer

0
关于标志位,我不确定是否有任何标准存在,但最好将其重置。
Boost库提供了很棒的RAII封装,可用于这种情况:IO State Savers

我认为这并不是设计用于提取器的,而是由用户代码使用。 - AProgrammer
我看不出有什么区别。它们也可以在输入流上工作,因此如果您想将标志设置回原来的状态,可以这样做,比手动设置更好。影响流程方式奇怪的提取器基本上是损坏的。 - Eugene

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接