没有捕到异常使用C ++打开一个不存在的文件

时间:2016-10-25 17:48:36

标签: c++ ifstream

我从这里开始运行MWE: http://www.cplusplus.com/reference/ios/ios/exceptions/ 在我的机器上它没有捕获异常。这是我的代码

#include <iostream>
#include <fstream>

int main()
{
    std::ifstream file;
    file.exceptions( std::ifstream::failbit | std::ifstream::badbit );
    try
    {
        file.open("IDoNotExist.txt");
    }
    catch(const std::ifstream::failure& e)
    {
        std::cout << "Bad luck!" << std::endl;
    }
}

在Arch-Linux上使用gcc 6.2.1我得到:

  

在抛出'std :: ios_base :: failure'的实例后终止调用

     

what():basic_ios :: clear

但是,在上面发布的链接中,提到代码还应该捕获与打开文件相关的异常。出了什么问题?

2 个答案:

答案 0 :(得分:4)

看起来像known bug in libstdc++

问题在于,随着对C ++ 11 ABI的更改,许多类在libstdc++6.so中重复,一个版本使用旧版ABI,另一个版本使用新版本。

异常类没有重复,所以当时不存在这个问题。但是,在该语言的一些较新版本中,我们决定std::ios_base::failure应该来自std::system_error而不是std::exception ......但system_error是C ++ 11只有类,所以它必须使用新的ABI标志或它会抱怨。现在你有两个不同的std::ios_base::failure课程并且手中一团糟!

简单的解决方案是使用-D_GLIBCXX_USE_CXX11_ABI=0编译您的程序并辞退到旧的ABI,直到错误得到解决。或者,写catch (std::exception &e)

答案 1 :(得分:2)

N.B。 std::ifstream::failurestd::ios_base ifstream基类中定义的类型,因此本答案的其余部分将其称为std::ios_base::failure或仅std::ios::failure

这里的问题是,自GCC 5以来,libstdc ++中有两个不同的std::ios_base::failure定义(更多详细信息,请参阅Dual ABI docs)。这是必要的,因为C ++ 11改变了ios::failure的定义:

class ios_base::failure : public exception {

为:

class ios_base::failure : public system_error {

这是ABI更改,因为system_errorexception相比有额外的数据成员,因此会更改ios::failure的大小和布局。

因此,自GCC 5.1起,当您的代码名称为std::ios_base::failure(或std::ifstream::failure或其他任何名称)时,您获得的定义取决于_GLIBCXX_USE_CXX11_ABI宏的值。当libstdc++.so库中发生iostream错误时,抛出哪种类型取决于构建libstdc++.so时宏的值。如果您尝试捕获一种类型并且库抛出另一种类型,则catch将不起作用。这就是你所看到的。在你的代码std::ifstream::failure中命名新类型,但是库抛出旧类型,这是一个不同的类,因此catch处理程序不匹配。

使用GCC 5.x和6.x,libstdc++.so中的代码会抛出旧类型,因此要捕获它,您需要使用-D_GLIBCXX_USE_CXX11_ABI=0编译代码或将处理程序更改为{{1 (因为新旧类型都来自catch (const std::exception&))。

使用GCC 7.x,库中的代码会抛出 new 类型(由PR 66145更改),因此您需要使用std::exception或catch {编译代码{1}}。

对于GCC 8.x,库现在抛出一个异常类型,可以由旧类型的处理程序捕获或新类型(由PR 85222更改),所以你不要我不需要改变你的代码。它将Just Work™。