盐城市网站建设_网站建设公司_前端工程师_seo优化
2025/12/30 10:28:31 网站建设 项目流程

背景

公司代码规范新加一条:“核心库禁用异常。”
于是我把原本四处 throw 的日志库翻出来,决定用 std::error_code 做一次“无异常化”翻新。过程顺手记下,权当备忘。

选型思路

异常一旦禁用,能选的只剩三样:

  1. 返回 bool——信息太少;
  2. 输出 int errno——命名空间污染;
  3. std::error_code——值语义、可扩展、与标准库同频。

结论:直接上积木。

旧接口

// 旧日子:抛就完事
void rotate_log(const std::string& path);
// 调用方
try {rotate_log("app.log");
} catch (const LogException& e) {std::cerr << e.what() << '\n';
}

新接口

// 新日子:把错误码塞回来
std::error_code rotate_log(const std::string& path) noexcept;
// 调用方
if (auto ec = rotate_log("app.log"); ec) {std::cerr << ec.message() << '\n';
}

错误值设计

先列可能出错的情节:

  • 文件根本不存在 → not_found
  • 权限不足 → permission_denied
  • 磁盘写满 → no_space
  • 成功 → ok(必须是 0)

enum class 一次性写死:

enum class LogErr {ok = 0,not_found,permission_denied,no_space
};

错误类别

class LogCategory : public std::error_category {
public:const char* name() const noexcept override { return "log"; }std::string message(int ev) const override {switch (static_cast<LogErr>(ev)) {case LogErr::ok:               return "success";case LogErr::not_found:        return "log file not found";case LogErr::permission_denied:return "permission denied";case LogErr::no_space:         return "disk full";default:                       return "unknown log error";}}
};inline const std::error_category& log_category() {static LogCategory c;return c;
}

让枚举自动变身

namespace std {
template<> struct is_error_code_enum<LogErr> : true_type {};
}inline std::error_code make_error_code(LogErr e) {return {static_cast<int>(e), log_category()};
}

实现函数

std::error_code rotate_log(const std::string& path) noexcept {if (!fs::exists(path)) return LogErr::not_found;fs::space_info si = fs::space(path);if (si.available < 1_MiB) return LogErr::no_space;std::error_code ec;fs::rename(path, path + "." + timestamp(), ec);return ec ? LogErr::permission_denied : LogErr::ok;
}

注意:内部依旧用 fs:: 的无异常重载,把系统级错误转成自家枚举,保持对外统一语言。

调用侧

for (auto& path : log_files) {if (auto ec = rotate_log(path); ec) {report_to_monitoring("rotate failed", ec);continue;  // 单文件失败不阻断批次}
}

没有 try/catch,代码路径平坦;监控端收到的 ec.value() 可直接映射到告警级别。

单元测试

TEST(rotate_log, not_found) {auto ec = rotate_log("missing.log");EXPECT_EQ(ec, LogErr::not_found);EXPECT_TRUE(ec);EXPECT_EQ(ec.message(), "log file not found");
}

用枚举值直接比较,测试用例一眼能读。

经验小结

  1. 先列“失败场景”再列“枚举值”,保证不遗漏、不重复。
  2. 一定把 ok 钉死在 0,否则 if (ec) 就全乱。
  3. 内部若调用其他 error_code 接口,就地转换,别让系统错误泄漏到上层。
  4. 消息串只给人看,逻辑判断永远用枚举或布尔。

结语

把异常关进抽屉后,std::error_code 成了最顺手的那块积木:轻量、可拷贝、能跨线程、还能与标准库拼成同一套“语言”。一次重构,日志库体积没涨,性能没跌,代码审查却少了一堆 “catch (...) 兜底” 的争吵——也算意外收获。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询