I was running a MWE from here: http://www.cplusplus.com/reference/ios/ios/exceptions/ On my machine it does not catch the exception. Here is my code
#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;
}
}
Using gcc 6.2.1
on Arch-Linux I get:
terminate called after throwing an instance of 'std::ios_base::failure'
what(): basic_ios::clear
However, on the link posted above it is mentioned that the code should also catch the exception related to opening the file. What went wrong?
It looks like a known bug in libstdc++.
The problem is that with the change to the C++11 ABI, many classes were duplicated in
libstdc++6.so
, one version with the old ABI, other with the new one.Exception classes were not duplicated so this problem didn't exist at the time. But then, in some newer revision of the language, it was decided that
std::ios_base::failure
should derive fromstd::system_error
instead ofstd::exception
... butsystem_error
is a C++11 only class so it must use the new ABI flag or it will complain. Now you have two differentstd::ios_base::failure
classes and a mess in your hands!The easy solution is to compile your program with
-D_GLIBCXX_USE_CXX11_ABI=0
and resign to the old ABI until the bug is solved. Or alternatively, writecatch (std::exception &e)
.N.B.
std::ifstream::failure
is a type defined in thestd::ios_base
base class ofifstream
, so the rest of this answer refers to it asstd::ios_base::failure
or juststd::ios::failure
.The problem here is that since GCC 5 there are two different definitions of
std::ios_base::failure
in libstdc++ (see the Dual ABI docs for more details). That's needed because C++11 changed the definition ofios::failure
from:to:
This is an ABI change, because
system_error
has additional data members compared toexception
, and so it changes the size and layout ofios::failure
.So since GCC 5.1, when your code names
std::ios_base::failure
(orstd::ifstream::failure
or any other name for it) which definition you get depends on the value of the_GLIBCXX_USE_CXX11_ABI
macro. And when an iostream error happens inside thelibstdc++.so
library, which type gets thrown depends on the value of the macro whenlibstdc++.so
was built. If you try to catch one type and the library throws the other type, the catch won't work. This is what you're seeing. In your codestd::ifstream::failure
names the new type, but the library is throwing the old type, which is a different class, so the catch handler isn't matched.With GCC 5.x and 6.x the code inside
libstdc++.so
throws the old type, so to catch it you need to either compile you code with-D_GLIBCXX_USE_CXX11_ABI=0
or change your handler tocatch (const std::exception&)
(because both the old and new types derive fromstd::exception
).With GCC 7.x the code inside the library throws the new type (changed by PR 66145), so you need to compile your code with
-D_GLIBCXX_USE_CXX11_ABI=1
or catchstd::exception
.For GCC 8.x the library now throws an exception type that can be caught by a handler for the old type or the new type (changed by PR 85222), so you don't need to change your code. It will Just Work™.