debugcppMinor
Better option than "errno" for file IO error handling
Viewed 0 times
handlingfileerrorthanbetteroptionerrnofor
Problem
I have the following method for opening a file:
The objective here is to attempt to open a file and throw
Everything works fine and I get a nice error message like this if the exception is thrown:
Failed to open Tank file "unexistent_file": 'No such file or directory'
But what I don't like in that block is having to use the
A way around it would be to let the stream throw an exception, then catch it, get the error message from the
Produced the error message:
Failed to open Tank file "unexistent_file": 'ios_base::clear: unspecified iostream_category error'.
Is there a better way to implement this? I was hoping that the new C++11
void TankFile::OpenForReading(const std::string & filename)
{
assert(!filename.empty());
errno = 0;
file.exceptions(0); // Don't throw
file.open(filename, (std::fstream::in | std::fstream::binary));
if (!file.is_open() || !file.good())
{
const char * errorStr = strerror(errno);
throw TankFileError(Format("Failed to open Tank file \"%s\": '%s'", filename.c_str(), errorStr));
}
}The objective here is to attempt to open a file and throw
TankFileError with a proper error description on failure. The caller will be expecting this exception type.Everything works fine and I get a nice error message like this if the exception is thrown:
Failed to open Tank file "unexistent_file": 'No such file or directory'
But what I don't like in that block is having to use the
errno global and strerror().A way around it would be to let the stream throw an exception, then catch it, get the error message from the
what() member and re-throw with TankFileError, but I find this solution also a bit hackish, plus, in the tests I did, the resulting error message from std::fstream::failure was pretty cryptic:void TankFile::OpenForReading(const std::string & filename)
{
assert(!filename.empty());
try
{
file.exceptions(std::fstream::failbit);
file.open(filename, (std::fstream::in | std::fstream::binary));
}
catch (std::fstream::failure & err)
{
throw TankFileError(Format("Failed to open Tank file \"%s\": '%s'", filename.c_str(), err.what()));
}
}Produced the error message:
Failed to open Tank file "unexistent_file": 'ios_base::clear: unspecified iostream_category error'.
Is there a better way to implement this? I was hoping that the new C++11
system_error library would provide a way to query this kind of error messages, but from what I've seen, you still have to pass errno around to get an error string.Solution
I currently don't see any seamless alternative to using errno and ::strerror;.
using the the new e.code() semantics yields (with clang 3.4 and libc++ 1101) only the first line of
Live
So even if one's lucky enough to have a std:: implementation that cares about iso §27.5.3.1.1 and actually derives ios_base::failure from system_error, the msg generated is too poor to be presented to users.
The only thing to be discussed is how the libc messages are best incorporated into wrapper classes.
#include
#include
#include
#include
#include // strerror
#include
#if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 1000)
#define HAS_IOS_BASE_FAILURE_DERIVED_FROM_SYSTEM_ERROR 1
#else
#define HAS_IOS_BASE_FAILURE_DERIVED_FROM_SYSTEM_ERROR 0
#endif
using std::cerr;
using std::cout;
using std::endl;
int main(/*int argc, char** argv*/) {
int rv = EXIT_SUCCESS;
errno = 0;
try {
std::ifstream ifs;
ifs.exceptions(std::ios::badbit | std::ios::failbit);
ifs.open("DOESN'T EXIST");
} catch (const std::ios_base::failure& e) {
#if (HAS_IOS_BASE_FAILURE_DERIVED_FROM_SYSTEM_ERROR)
//
// e.code() is only available if the lib actually follows iso §27.5.3.1.1
// and derives ios_base::failure from system_error
// like e.g. libc++
// http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/ios?revision=193085&view=markup
// (line 415)
//
// and not keeps on deriving it directly from runtime_error
// like libstdc++
// https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/ios_base.h#L209
//
cout << "libc++ error #" << e.code().value()
<< ' ' << e.code().message()
<< ',' << endl << " ";
#endif
cout << "libc error #" << (rv = errno)
<< ": " << ::strerror(errno)
<< endl;
cout << "handled" << endl;
}
return rv;
}using the the new e.code() semantics yields (with clang 3.4 and libc++ 1101) only the first line of
libc++ error #1 unspecified iostream_category error,
libc error #2: No such file or directory
handledLive
So even if one's lucky enough to have a std:: implementation that cares about iso §27.5.3.1.1 and actually derives ios_base::failure from system_error, the msg generated is too poor to be presented to users.
The only thing to be discussed is how the libc messages are best incorporated into wrapper classes.
Code Snippets
#include <stdexcept>
#include <system_error>
#include <string>
#include <iostream>
#include <cstring> // strerror
#include <fstream>
#if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 1000)
#define HAS_IOS_BASE_FAILURE_DERIVED_FROM_SYSTEM_ERROR 1
#else
#define HAS_IOS_BASE_FAILURE_DERIVED_FROM_SYSTEM_ERROR 0
#endif
using std::cerr;
using std::cout;
using std::endl;
int main(/*int argc, char** argv*/) {
int rv = EXIT_SUCCESS;
errno = 0;
try {
std::ifstream ifs;
ifs.exceptions(std::ios::badbit | std::ios::failbit);
ifs.open("DOESN'T EXIST");
} catch (const std::ios_base::failure& e) {
#if (HAS_IOS_BASE_FAILURE_DERIVED_FROM_SYSTEM_ERROR)
//
// e.code() is only available if the lib actually follows iso §27.5.3.1.1
// and derives ios_base::failure from system_error
// like e.g. libc++
// http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/ios?revision=193085&view=markup
// (line 415)
//
// and not keeps on deriving it directly from runtime_error
// like libstdc++
// https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/ios_base.h#L209
//
cout << "libc++ error #" << e.code().value()
<< ' ' << e.code().message()
<< ',' << endl << " ";
#endif
cout << "libc error #" << (rv = errno)
<< ": " << ::strerror(errno)
<< endl;
cout << "handled" << endl;
}
return rv;
}libc++ error #1 unspecified iostream_category error,
libc error #2: No such file or directory
handledContext
StackExchange Code Review Q#57829, answer score: 6
Revisions (0)
No revisions yet.