问题
One of the ancient anti-pattern is people checking error status and then returning fairly useless messages like "operation failed" instead of "operation failed because ...". I want C++ file I/O operations to fail with exception and get the error message on why it failed. Specifically I want ofstream object to raise exception when file creation fails and get bit more useful message such as "permission denied" or "No file or path".
This is trivial to do in languages such as C# or Java or Python but somehow there is no well documented way to do this C++. By default, iostream objects just fail silently. There is some global errorcode that gets but I would rather have exceptions. After lot of searching, I read that you can enable exceptions using following line of code:
my_file.exceptions(flog.exceptions() | std::ios::failbit | std::ifstream::badbit);
That works but now the exception that gets raised is std::ios_base::failure
and the ex.what() returns useless strings like "basic_ios::clear". As per the C++11 specs std::ios_base::failure
was supposed to be inherited from system_error which has .code().message() that will give the exception message. Let's keep aside this weirdness here and not finger point to person who decided what() should not be returning actual error message :). The problem is that even when compiling with C++11 and G++ 4.8.4, I find that std::ios_base::failure
is not actually inherited from system_error.
Questions
- Why
std::ios_base::failure
is not inherited from system_error in latest G++ 4.8.4 even when compiling with C++11 mode? Is GCC's implementation of C++11 incomplete in this area or do I need to do something more? - How do I get to my goal of raising exceptions when IO operations fail in C++ and getting error messages? Is there no way to do this even in latest C++11 or C++14? What are the alternatives?
Here's the sample code. You can compile and run it here.
#include <iostream>
#include <fstream>
#include <system_error>
int main() {
try {
std::ofstream flog;
flog.exceptions(flog.exceptions() | std::ios::failbit | std::ifstream::badbit);
flog.open("~/watever/xyz.tsv", std::ios::trunc);
}
catch (const std::ios_base::failure &ex) {
std::cout << "ios_base::failure: " << ex.what();
}
catch(const std::system_error& ex) {
std::cout << "system_error: " << ex.code().message();
}
}
回答1:
According to GCC's C++11 status documentation, "System error support" is fully supported.
And according to Bug 57953 - no C++11 compliant std::ios_base::failure found,
std::ios_base::failure
was changed in Revision 217559 to derive fromsystem_error
in C++11. If you look in the updatedios_base.h
,std::ios_base::failure
derives fromsystem_error
if_GLIBCXX_USE_CXX11_ABI
is defined. That define is mentioned in GCC's Using Dual ABI documentation.However, there is a regression regarding ABI issues with
std::ios_base::failure
that is still open, due to the fact that some pieces of the standard library do not define_GLIBCXX_USE_CXX11_ABI
:Bug 66145 - [5/6/7 Regression] std::ios_base::failure objects thrown from libstdc++.so use old ABI
the short answer is - you probably can't, at least not with GCC's current implementation anyway. Unless you can recompile everything in the library with
_GLIBCXX_USE_CXX11_ABI
defined.
回答2:
On POSIX
systems ios
failures set errno
so you can get meaningful error messages using that. I often do this:
std::string getenv_as_string(std::string const& var)
{
auto ptr = std::getenv(var.c_str());
return ptr ? ptr : "";
}
// ~ doesn't work from C++
const std::string HOME = getenv_as_string("HOME");
int main()
{
try
{
std::ofstream ifs;
ifs.open(HOME + "/watever/xyz.tsv", std::ios::trunc);
if(!ifs)
throw std::runtime_error(std::strerror(errno));
// Do stuff with ifs
}
catch(std::exception const& e)
{
std::cerr << e.what() << '\n';
}
}
Output:
No such file or directory
来源:https://stackoverflow.com/questions/38471518/how-to-get-io-error-messages-when-creating-a-file-in-c