在C++中使用fstream时如何获得有意义的错误消息

14
什么是从std :: fstreams中以可移植的方式获取有意义的文件访问错误消息的最佳方法? badbitsfailbits的原始程度有点令人烦恼。我之前针对win32和POSIX编写过自己的异常层次结构,那比STL的方式要灵活得多。

我从启用了异常的fstream的downcasted catch (std :: exception)的what方法中得到“basic :: ios_clear”作为错误消息。这对我来说并没有太大意义,虽然我知道问题所在,但我希望我的程序能够更具信息性,这样当我几个月后开始部署时会更轻松。

Boost中是否存在任何内容可以跨平台和跨STL实现从fstream的实现中提取有意义的消息?


+1 对于这个问题 - 我也遇到了这个问题。拥有一个不能提供有意义的错误信息的基本界面似乎是荒谬的。至少 APR(Apache Portable Runtime)已经提供了这个功能。能够向用户传达操作系统与文件名相关的问题,例如“无效权限”或“磁盘错误”等,非常重要。否则,程序会正确地停止运行,但用户却不知道该采取什么措施来纠正问题。 - PP.
3个回答

8
无人阻止您同时检查errno/strerror(例如,在异常处理程序中)以获取更具体的失败原因。
更新-关于可移植性
顺便提一下,如果我没记错的话,Visual Studio的fstream实现调用_open/_read/_write/等CRT方法,这些方法设置errno。 Microsoft不保证CRT方法返回后GetLastError仍包含正确的值。对于cygwin、mingw等实现,它们设置errno,但不声明或保证GetLastError
所以,我坚持认为您需要、可以且应该只检查errno
现在,考虑到上述所有内容,如果您仍然想通过使用Boost::System而不是简单地调用strerror来使生活复杂化和过度工程化,那么我猜我的定义和您的定义优雅和简单并不相同。 :)

仅仅因为“没有人阻止我”并不意味着你的建议就是优雅的。此外,我已经说过:“我以前写过针对win32和POSIX的自己的异常层次结构”,最后我也提到了可移植性作为一个理想的特性。所以这个答案没有任何价值。还是谢谢你。 - Hassan Syed
1
@Hassan Syed:在Boost中,唯一接近这个的是Boost::System(http://www.boost.org/doc/libs/1_42_0/libs/system/doc/index.html),但这对iostreams没有帮助。 - Billy ONeal
经过仔细检查,boost::system确实是我问题的确切解决方案。如果您创建答案,我可以接受它:D。我一直避免使用它,因为它没有教程式的文档:D。 - Hassan Syed
@Hassan,我不明白你认为这个解决方案有什么不优雅之处,errno 不具备可移植性(实际上它是你唯一的可移植选择),而且除了你以外,其他人应该如何理解你所说的“我编写了自己的异常层次结构”。你可以对其进行包装和抽象,但归根结底,你仍然需要阅读 errno - vladr
有人“必须”读取errno,但这个人不一定是我——“计算机科学中的所有问题都可以通过另一级间接性来解决”,我不想再次提供这种间接性。希望我们都能认同,在软件工程中重复利用是好的。 - Hassan Syed
显示剩余2条评论

1
你需要什么信息?badbit 表示 I/O 错误。eofbit 表示文件结束。failbit 表示解析错误。
为了排除一种解决方案,我认为你不能覆盖本地类型输入函数,因为 ADL。你可以实现 operator>>(istream, input_safe_int),其中 input_safe_int 是从 int& 构造的。在内部放置一个 try 块等。

1

我有幸捕获到了std::ios_base::failure,然后使用errno重新抛出了std::system_error:

terminate called after throwing an instance of 'std::system_error'
  what():  broken/path: No such file or directory

#include <fstream>

int main() {
  const std::string filename{ "broken/path" };
  try {
    std::ifstream file{ filename };
    file.exceptions(std::ios::failbit); // std::ios_base_failure is thrown here
  } catch (std::ios_base::failure&) {
    throw std::system_error{ errno, std::generic_category(), filename };
  }
}

这适用于UNIX和Windows,因为“所有errno值都是…与UNIX兼容的”(source)。


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