Exception not caught opening a non-existing file using C++
我从这里开始运行MWE:
http://www.cplusplus.com/reference/ios/ios/exceptions/
在我的机器上,它没有捕获异常。 这是我的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #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,我得到:
terminate called after throwing an instance of 'std::ios_base::failure'
what(): basic_ios::clear
但是,在上面发布的链接上,提到该代码还应该捕获与打开文件有关的异常。 什么地方出了错?
-
看来这是一个不好的例子。一方面,应该像您一样通过const引用捕获异常,但不要这样做。
-
在Linux(不同发行版)上的g ++ 6.2.0上,该程序显示"运气不好!"。我也知道该程序应捕获failure异常并显示" Bad lucky!"。 (假设IDoNotExist.txt确实不存在)。因此,您的C ++编译器和/或运行时出现故障。造成这种情况的最可能原因是安装错误。尝试卸载并重新安装名称为g++或c++的每个软件包。
-
尽管cplusplus.com具有正面的一面,但也有负面的一面(关于该网站的观点各不相同,如果您想了解更多,请进行搜索)。我倾向于使用该参考站点,它似乎是最新的且准确的。例如,其有关I / O流异常的示例使用正确的类进行捕获。
-
@Someprogrammerdude该示例代码也失败。它也不能通过const引用捕获。
-
@NathanOliver的确如此,但至少它可以作为参考。
-
@zwol我在Arch上重新安装了所有基本和基本开发人员软件包,但是行为没有改变。
-
@MrZ它可能是g ++ 6.2.0 bug或类似的东西:我也使用gcc在Arch上,并且我得到了完全相同的行为。
它看起来像libstdc ++中的一个已知错误。
问题在于,随着对C ++ 11 ABI的更改,许多类在libstdc++6.so中被复制,一个版本与旧的ABI,另一版本与新的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)。
-
这是正确的答案。 完全准确地说,问题不是system_error仅限于C ++ 11(可以解决),而是std::system_error的大小和布局与std::exception不同,这意味着std::ios::failure会根据大小更改大小 它是否来自system_error。 多数民众赞成在ABI中断,因此我们定义了两种不同的ios::failure类型,并且哪种可见取决于_GLIBCXX_USE_CXX11_ABI宏。 如果libstdc ++。so抛出旧类型,而您尝试捕获新类型(反之亦然),则它将失败。 但我在GCC 8中使用了魔术,因此可以正常工作
-
应该捕获const异常:catch (const std::exception &e)
N.B. std::ifstream::failure是在ifstream的std::ios_base基类中定义的类型,因此此答案的其余部分将其称为std::ios_base::failure或仅仅是std::ios::failure。
这里的问题是,由于GCC 5在libstdc ++中有std::ios_base::failure的两个不同定义(有关更多详细信息,请参见Dual ABI文档)。这是必需的,因为C ++ 11将ios::failure的定义从:
1
| class ios_base::failure : public exception { |
至:
1
| class ios_base::failure : public system_error { |
这是ABI的更改,因为与exception相比system_error具有更多的数据成员,因此它更改了ios::failure的大小和布局。
因此,从GCC 5.1开始,当您的代码名称std::ios_base::failure(或std::ifstream::failure或它的任何其他名称)时,您得到的定义取决于_GLIBCXX_USE_CXX11_ABI宏的值。而且,当libstdc++.so库内部发生iostream错误时,抛出哪种类型取决于生成libstdc++.so时宏的值。如果尝试捕获一种类型,而库抛出另一种类型,则捕获将不起作用。这就是您所看到的。在您的代码中,std::ifstream::failure命名了新类型,但是库抛出了旧类型,这是一个不同的类,因此catch处理程序不匹配。
使用GCC 5.x和6.x,libstdc++.so中的代码会抛出旧类型,因此要捕获它,您需要使用-D_GLIBCXX_USE_CXX11_ABI=0编译代码或将处理程序更改为catch (const std::exception&)(因为新旧版本均如此)类型源自std::exception)。
使用GCC 7.x,库中的代码将引发新类型(由PR 66145更改),因此您需要使用-D_GLIBCXX_USE_CXX11_ABI=1编译代码或捕获std::exception。
对于GCC 8.x,库现在引发了一个异常类型,该异常类型可以由处理程序捕获为旧类型或新类型(由PR 85222更改),因此您无需更改代码。它会工作吗?