问题
#include <fstream>
#include <iostream>
#include <exception>
int main(int argc, char **argv)
{ try
{ std::ifstream sFile(argv[1]);
sFile.exceptions(std::ios::badbit | std::ios::failbit);
} catch (const std::exception &_r)
{ std::cerr << "exception: " << _r.what() << std::endl;
}
}
In case of the file passed in does not exist, this code prints out with g++ 4.5.2 (yes I know that this is a very old version, but I don't have enough clout to change this):
"exception: basic_ios::clear"
Using Visual C++ 12:
"exception: ios_base::failbit set: iostream stream error"
Considering that system error messages have already been introduced a very long time ago, I consider this as not acceptable.
回答1:
The C++11 solution
Since C++11, std::ios_base::failure inherits from std::system_error so we should be able to catch
that exception and get the error message right out of it.
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
#include <fstream> // std::ifstream
#include <iostream> // std::cerr, std::endl
#include <system_error> // std::system_error
int
main()
{
const auto filename = std::string {"/no/such/file.txt"};
try
{
auto istr = std::ifstream {filename};
istr.exceptions(std::ios::badbit | std::ios::failbit);
// ...
istr.close();
}
catch (const std::ios_base::failure& e)
{
std::cerr << "error: " << e.what() << std::endl;
//std::cerr << "error: " << e.code().message() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Unfortunately, this doesn't work with my GCC 5.3.0 yet. The catch
never gets activated and the program core-dumps instead. What is even worse is that this means we have to catch
std::exception
which will also match all other kinds of exceptions that might not be related to I/O errors. This really bothers me.
Using std::strerror(errno)
If everything else fails, you can use 'ye good ol' errno
and obtain a human-readable string via std::strerror
(which is not thread-safe, by the way). There also is no guarantee that errno
hasn't been cleared (or re-assigned) between the point where the error occurred and the point you handle the exception. This is because in C++, arbitrary code might have been executed during stack unwinding.
#include <cerrno> // errno
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
#include <cstring> // std::strerror
#include <exception> // std::exception
#include <fstream> // std::ifstream
#include <iostream> // std::cerr, std::endl
int
main()
{
const auto filename = std::string {"/no/such/file.txt"};
try
{
auto istr = std::ifstream {filename};
istr.exceptions(std::ios::badbit | std::ios::failbit);
// ...
istr.close();
}
catch (const std::exception&)
{
std::cerr << "error: " << std::strerror(errno) << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Using std::error_code
s
This solution is only marginally better than using std::strerror(errno)
but at least it looks more like C++11. The problems are the same except that it is thread-safe.
#include <cerrno> // errno
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
#include <exception> // std::exception
#include <fstream> // std::ifstream
#include <iostream> // std::cerr, std::endl
#include <system_error> // std::error_code, std::system_category
int
main()
{
const auto filename = std::string {"/no/such/file.txt"};
try
{
auto istr = std::ifstream {filename};
istr.exceptions(std::ios::badbit | std::ios::failbit);
// ...
istr.close();
}
catch (const std::exception&)
{
const auto ec = std::error_code {errno, std::system_category()};
std::cerr << "error: " << ec.message() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
回答2:
After a failed system call:
std::cout << strerror(errno) << std::endl;
Will produce a somewhat useful error message. You will will need to #include
string.h and errno.h.
C++ exceptions will not be useful here. These POSIX functions will generally produce a more meaningful diagnostic.
来源:https://stackoverflow.com/questions/35190832/how-can-i-get-the-standard-file-streams-to-return-a-useful-error-message