🌐 Language
[Refactor] 移除 ios_defs::failure 异常类
背景
当前 io_base.h 中定义了一个 ios_defs::failure 异常类(io_base.h:47-65):
class failure : public std::runtime_error
{
public:
explicit failure(const std::string& msg, const std::error_code& ec = std::io_errc::stream)
: std::runtime_error(msg)
, m_ec(ec) {}
explicit failure(const char* msg, const std::error_code& ec = std::io_errc::stream)
: std::runtime_error(msg)
, m_ec(ec) {}
virtual const std::error_code& code() const noexcept
{
return m_ec;
}
private:
std::error_code m_ec;
};
该异常类在整个代码库中仅在一处被抛出——io_state_and_exp::clear() 的末尾(io_base.h:112-113):
if (need_throw_exp)
throw ios_defs::failure("failure bit has been set");
问题
ios_defs::failure 的设计存在以下问题:
-
丢失原始错误信息:IOv2 的异常体系已经在 handle_exception() 中将原始异常按类型分类存储(m_exp_dev_fail、m_exp_cvt_fail、m_exp_str_fail、m_exp_other_fail)。clear() 中的逻辑也会优先尝试重新抛出原始异常(io_base.h:85-104)。ios_defs::failure 仅在存储的原始异常指针为空时作为兜底被抛出,此时它携带的只是一个泛化的 "failure bit has been set" 消息,丢失了所有错误上下文。
-
与 IOv2 异常体系不一致:IOv2 自身已有明确分层的异常类型(device_error、cvt_error、stream_error、eof_error),分别对应不同的 fail bit。ios_defs::failure 不属于这个体系,它是从 std::runtime_error 继承的,调用方无法通过捕获 IOv2 的异常类型来处理它。
-
仅为兼容 std::ios_base::failure 而存在:这个类是对 C++ 标准库 std::ios_base::failure 的模仿。但 IOv2 已经建立了自己独立的、更细粒度的异常和状态体系,不需要保留这个标准库兼容层。
-
std::error_code 未被使用:failure 类中携带的 m_ec(std::error_code)在唯一的抛出点使用的是默认值 std::io_errc::stream,没有任何地方传递具体的错误码,也没有任何捕获点使用 code() 方法。
解决方案
移除 ios_defs::failure 异常类,改为在 clear() 中直接抛出与 fail bit 对应的 IOv2 异常类型:
重构前:
if (need_throw_exp)
throw ios_defs::failure("failure bit has been set");
重构后(示例):
if (state_in_exp & ios_defs::devfailbit)
throw device_error("device failure bit has been set");
else if (state_in_exp & ios_defs::cvtfailbit)
throw cvt_error("converter failure bit has been set");
else if (state_in_exp & ios_defs::strfailbit)
throw stream_error("stream failure bit has been set");
else if (state_in_exp & ios_defs::otherfailbit)
throw stream_error("other failure bit has been set");
这样,即使原始异常指针为空,调用方仍可以通过捕获 IOv2 的标准异常类型来正确处理错误,且不会丢失错误的分类信息。
任务列表
[Refactor] Remove the ios_defs::failure Exception Class
Background
io_base.h currently defines an ios_defs::failure exception class (io_base.h:47-65):
class failure : public std::runtime_error
{
public:
explicit failure(const std::string& msg, const std::error_code& ec = std::io_errc::stream)
: std::runtime_error(msg)
, m_ec(ec) {}
explicit failure(const char* msg, const std::error_code& ec = std::io_errc::stream)
: std::runtime_error(msg)
, m_ec(ec) {}
virtual const std::error_code& code() const noexcept
{
return m_ec;
}
private:
std::error_code m_ec;
};
This exception class is only thrown in one place in the entire codebase — at the end of io_state_and_exp::clear() (io_base.h:112-113):
if (need_throw_exp)
throw ios_defs::failure("failure bit has been set");
Problem
ios_defs::failure has the following issues:
-
Loses original error context: IOv2's exception system already categorizes and stores original exceptions by type in handle_exception() (m_exp_dev_fail, m_exp_cvt_fail, m_exp_str_fail, m_exp_other_fail). The logic in clear() prioritizes rethrowing the original exception (io_base.h:85-104). ios_defs::failure is only thrown as a fallback when the stored exception pointer is null, carrying only a generic "failure bit has been set" message with all error context lost.
-
Inconsistent with IOv2's exception hierarchy: IOv2 already has clearly layered exception types (device_error, cvt_error, stream_error, eof_error), each corresponding to a specific fail bit. ios_defs::failure does not belong to this hierarchy — it inherits from std::runtime_error, and callers cannot handle it by catching IOv2's exception types.
-
Exists only for std::ios_base::failure compatibility: This class mimics C++ standard library's std::ios_base::failure. But IOv2 has already established its own independent, more fine-grained exception and state system, making this compatibility layer unnecessary.
-
std::error_code is unused: The m_ec member in failure is always set to the default std::io_errc::stream at the only throw site. No call site passes a specific error code, and no catch site uses the code() method.
Proposed Solution
Remove the ios_defs::failure exception class and instead throw the appropriate IOv2 exception type corresponding to the fail bit in clear():
Before:
if (need_throw_exp)
throw ios_defs::failure("failure bit has been set");
After (example):
if (state_in_exp & ios_defs::devfailbit)
throw device_error("device failure bit has been set");
else if (state_in_exp & ios_defs::cvtfailbit)
throw cvt_error("converter failure bit has been set");
else if (state_in_exp & ios_defs::strfailbit)
throw stream_error("stream failure bit has been set");
else if (state_in_exp & ios_defs::otherfailbit)
throw stream_error("other failure bit has been set");
This way, even when the original exception pointer is null, callers can still correctly handle errors by catching IOv2's standard exception types, without losing the error classification.
Action Items
🌐 Language
[Refactor] 移除
ios_defs::failure异常类背景
当前
io_base.h中定义了一个ios_defs::failure异常类(io_base.h:47-65):该异常类在整个代码库中仅在一处被抛出——
io_state_and_exp::clear()的末尾(io_base.h:112-113):问题
ios_defs::failure的设计存在以下问题:丢失原始错误信息:IOv2 的异常体系已经在
handle_exception()中将原始异常按类型分类存储(m_exp_dev_fail、m_exp_cvt_fail、m_exp_str_fail、m_exp_other_fail)。clear()中的逻辑也会优先尝试重新抛出原始异常(io_base.h:85-104)。ios_defs::failure仅在存储的原始异常指针为空时作为兜底被抛出,此时它携带的只是一个泛化的"failure bit has been set"消息,丢失了所有错误上下文。与 IOv2 异常体系不一致:IOv2 自身已有明确分层的异常类型(
device_error、cvt_error、stream_error、eof_error),分别对应不同的 fail bit。ios_defs::failure不属于这个体系,它是从std::runtime_error继承的,调用方无法通过捕获 IOv2 的异常类型来处理它。仅为兼容
std::ios_base::failure而存在:这个类是对 C++ 标准库std::ios_base::failure的模仿。但 IOv2 已经建立了自己独立的、更细粒度的异常和状态体系,不需要保留这个标准库兼容层。std::error_code未被使用:failure类中携带的m_ec(std::error_code)在唯一的抛出点使用的是默认值std::io_errc::stream,没有任何地方传递具体的错误码,也没有任何捕获点使用code()方法。解决方案
移除
ios_defs::failure异常类,改为在clear()中直接抛出与 fail bit 对应的 IOv2 异常类型:重构前:
重构后(示例):
这样,即使原始异常指针为空,调用方仍可以通过捕获 IOv2 的标准异常类型来正确处理错误,且不会丢失错误的分类信息。
任务列表
io_state_and_exp::clear()中的兜底抛出逻辑,将throw ios_defs::failure(...)替换为根据 fail bit 类型抛出对应的 IOv2 异常。ios_defs::failure类定义(io_base.h:47-65)。#include <ios>的依赖(如果移除failure后不再需要std::io_errc)。IOv2Test/io/io_base/test_io_base_failure.cpp中与ios_defs::failure相关的测试用例。IOv2Test/io/io_state_and_exp/test_io_state_and_exp.cpp中捕获ios_defs::failure的测试代码。[Refactor] Remove the
ios_defs::failureException ClassBackground
io_base.hcurrently defines anios_defs::failureexception class (io_base.h:47-65):This exception class is only thrown in one place in the entire codebase — at the end of
io_state_and_exp::clear()(io_base.h:112-113):Problem
ios_defs::failurehas the following issues:Loses original error context: IOv2's exception system already categorizes and stores original exceptions by type in
handle_exception()(m_exp_dev_fail,m_exp_cvt_fail,m_exp_str_fail,m_exp_other_fail). The logic inclear()prioritizes rethrowing the original exception (io_base.h:85-104).ios_defs::failureis only thrown as a fallback when the stored exception pointer is null, carrying only a generic"failure bit has been set"message with all error context lost.Inconsistent with IOv2's exception hierarchy: IOv2 already has clearly layered exception types (
device_error,cvt_error,stream_error,eof_error), each corresponding to a specific fail bit.ios_defs::failuredoes not belong to this hierarchy — it inherits fromstd::runtime_error, and callers cannot handle it by catching IOv2's exception types.Exists only for
std::ios_base::failurecompatibility: This class mimics C++ standard library'sstd::ios_base::failure. But IOv2 has already established its own independent, more fine-grained exception and state system, making this compatibility layer unnecessary.std::error_codeis unused: Them_ecmember infailureis always set to the defaultstd::io_errc::streamat the only throw site. No call site passes a specific error code, and no catch site uses thecode()method.Proposed Solution
Remove the
ios_defs::failureexception class and instead throw the appropriate IOv2 exception type corresponding to the fail bit inclear():Before:
After (example):
This way, even when the original exception pointer is null, callers can still correctly handle errors by catching IOv2's standard exception types, without losing the error classification.
Action Items
io_state_and_exp::clear(), replacingthrow ios_defs::failure(...)with fail-bit-specific IOv2 exceptions.ios_defs::failureclass definition (io_base.h:47-65).#include <ios>dependency ifstd::io_errcis no longer needed after removingfailure.ios_defs::failureinIOv2Test/io/io_base/test_io_base_failure.cpp.IOv2Test/io/io_state_and_exp/test_io_state_and_exp.cppwhereios_defs::failureis caught.