How can I get the standard file streams to return a useful error message?

给你一囗甜甜゛ 提交于 2019-12-10 18:49:44

问题


#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_codes

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!