为什么C ++ STL iostream不是“exception友好”?

我习惯于Delphi VCL框架,其中TStreams抛出错误的例外(例如文件未find,磁盘已满)。 我正在移植一些代码来使用C ++ STL来代替,而且iostreams不会在默认情况下抛出exception,而是设置badbit / failbit标志 。

两个问题

a:为什么会这样 – 从第一天开始,对于一个有例外的语言来说,这似乎是一个奇怪的devise决定?

b:如何最好地避免这种情况? 我可以生产出像我所期望的那样的垫片类,但是这感觉就像重新发明了轮子。 也许有一个BOOST库,这是一个更安全的方式吗?

一个。 C ++不是从第一天开始的例外。 “C类”开始于1979年,1989年增加了例外情况。同时, streams库是早在1984年(后来成为iostreams在1989年(后来由GNU在1991年重新实现)),它不能使用exception处理开始。

参考:

  • Bjarne Stroustrup, C ++史:1979-1991
  • C ++库

可以使用.exceptions方法启用exception。

 // ios::exceptions #include <iostream> #include <fstream> #include <string> int main () { std::ifstream file; file.exceptions ( ifstream::failbit | ifstream::badbit ); try { file.open ("test.txt"); std::string buf; while (std::getline(file, buf)) std::cout << "Read> " << buf << "\n"; } catch (ifstream::failure e) { std::cout << "Exception opening/reading file\n"; } std::cout.flush(); file.close(); return 0; } 

正如肯尼所说,如果你愿意,你可以启用例外。 但是,通常I / O在发生错误时需要某种恢复编程风格,而使用exception很难支持这种风格 – 在input操作之后testingstream的状态要简单得多。 我从来没有见过任何在I / O上使用exception的C ++代码。

好的,这是“回答我自己的问题”的时间…

首先,感谢KennyTM的历史。 就像他说的那样,从一开始就没有 C ++的例外,所以iostream的“例外”处理之后就不再出现了。

其次,正如Neil B所指出的那样,在input格式转换错误方面有例外,这将是一个很大的麻烦。 这让我感到惊讶,因为我正在考虑iostreams作为一个简单的文件系统包装层,我根本没有考虑过这种情况。

第三,看起来BOOST确实给聚会带来了一些东西: Boost.IOStreams 。 如果我理解正确,这些处理stream的低级I / O和缓冲方面,留下常规的C ++ IOStreams库来处理转换问题。 Boost.IOStreams以我期望的方式使用exception 。 如果我理解正确,肯尼的例子也可能是这样的:

 #include <ostream> #include <boost/iostreams/device/file.hpp> #include <boost/iostreams/stream.hpp> int main () { boost::iostreams::stream_buffer <boost::iostreams::file_source> buf("test.txt"); std::istream file(&buf); try { std::string buf; while (std::getline(file, buf)) std::cout << "Read> " << buf << "\n"; } catch (std::ios_base::failure::failure e) { std::cout << "Exception opening/reading file\n"; } std::cout.flush(); file.close(); return 0; } 

觉得在这个版本中,像“file not found”这样的东西应该丢掉,但是istream错误会被badbit / failbit报告。

我的意见是,exception不应该用于所有错误,不应该保留用于error handling。 最简单的指导原则是exception处理exception情况,在处理条件的情况下,会导致代码乱七八糟,并且会有大量退出这个失败的testing。

在正常的I / O处理中,像eof这样的东西并不是特例 – 它们是可以预期的,而且通常使用非exception方法处理得更干净。 同样,在search一个容器时,找不到特定的关键字并不一定是意外或exception的,所以适用于例如std :: find返回“失败”的范围的结束范围值。

另一方面,虽然很多人会不同意,但我认为在recursionsearch中使用例外来取得成功是恰当的。 每次调用,失败是正常情况 – 使用成功例外可以大大清理recursion函数的结构。 虽然我不确定我是否会在真实世界中使用它,但是抛出一个exception来减less并返回只发现不匹配的结果,给出了一个相当干净且易于跟踪的回溯LRparsing器。

顺便说一句 – 我不喜欢依赖iostream来input,除了在一个简单的给我全文件内容(或一个固定大小的块)的方式。 我发现使用其他方法来validation和解释input更容易和更强大。

  1. 每当你抛出一个exception,你需要考虑exception安全。 所以没有例外,也不例外,没有例外 – 安全头痛。

  2. Ireamreams也支持exception。 但是抛出exception是可选的。 您可以通过设置exceptions (failbit | badbit | eofbit)来启用例外exceptions (failbit | badbit | eofbit)

  3. Iostreams让你既能享受exception行为,又能享受无所顾忌的行为。