Reputation: 68708
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
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?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();
}
}
Upvotes: 3
Views: 3544
Reputation: 48605
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
Upvotes: 3
Reputation: 595339
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 from system_error
in C++11. If you look in the updated ios_base.h
, std::ios_base::failure
derives from system_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.
Upvotes: 3