如何跨平台抛出std::ofstream文件打开失败的描述性异常?
核心结论
C++标准库本身没有直接提供开箱即用的、自动抛出带具体系统错误信息的文件流异常机制,但可以通过标准工具或封装逻辑实现简洁的跨平台解决方案。
现有方案分析
ofstream::exceptions的局限
使用std::ios::failbit触发的ios_base::failure异常仅包含通用错误提示(如ios_base::failbit set: iostream stream error),无法获取“只读目录”“权限拒绝”这类系统级具体原因,因为标准库未要求该异常关联系统错误码。std::system_error的有效性与冗余
手动检查文件打开状态并抛出std::system_error的方式能得到详细错误信息(如Error opening some_file.txt: Read-only file system),但直接写#ifdef分支会让代码显得冗长。
简洁实现方案
方案1:C++17及以上(标准跨平台最优解)
C++17引入的<filesystem>库提供原生跨平台文件操作支持,其抛出的std::filesystem::filesystem_error异常自带详细错误原因、涉及路径等信息,无需手动处理系统差异。
示例代码:
#include <fstream> #include <filesystem> namespace fs = std::filesystem; void SomeMethod(const std::string& fileName) { fs::path filePath(fileName); // 尝试创建文件(不存在则创建,失败自动抛异常) fs::create_file(filePath); // 打开文件 std::ofstream outFile(filePath); if (!outFile.is_open()) { throw fs::filesystem_error( "Failed to open file for writing", filePath, std::error_code(errno, std::generic_category()) ); } // 开始写入文件... }
抛出的异常会自动适配系统给出具体描述,比如Windows下文件被占用时会提示Failed to open file for writing: The process cannot access the file because it is being used by another process。
方案2:C11/C14(封装简化代码)
将错误检查逻辑封装成工具函数或自定义流类,避免重复编写系统差异分支:
方式A:封装工具函数
#include <fstream> #include <system_error> #include <string> #ifdef _WIN32 #include <windows.h> #endif void throw_file_open_error(const std::string& fileName) { #ifdef _WIN32 const auto errorCode = GetLastError(); #else const auto errorCode = errno; #endif throw std::system_error( errorCode, std::system_category(), "Error opening " + fileName ); } void SomeMethod(const std::string& fileName) { std::ofstream outFile(fileName); if (!outFile.is_open()) { throw_file_open_error(fileName); } // 开始写入文件... }
方式B:自定义抛出异常的流类
#include <fstream> #include <system_error> #include <string> #ifdef _WIN32 #include <windows.h> #endif class throwing_ofstream : public std::ofstream { public: explicit throwing_ofstream(const std::string& fileName) : std::ofstream(fileName) { check_open(fileName); } void open(const std::string& fileName) override { std::ofstream::open(fileName); check_open(fileName); } private: void check_open(const std::string& fileName) { if (!is_open()) { #ifdef _WIN32 const auto errorCode = GetLastError(); #else const auto errorCode = errno; #endif throw std::system_error( errorCode, std::system_category(), "Error opening " + fileName ); } } }; void SomeMethod(const std::string& fileName) { // 打开失败自动抛异常,无需手动检查 throwing_ofstream outFile(fileName); // 开始写入文件... }
内容的提问来源于stack exchange,提问作者M. Victor

