Files
blackbird/gateway/inc/ExpBase.hpp

412 lines
12 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef EXCEPTION_BASE_H
#define EXCEPTION_BASE_H
#include <exception>
#include <string>
#include <cstring>
#include <memory>
#include <source_location>
#include <sstream>
#include <type_traits>
/**
* @brief 通用异常基类
*
* 特性:
* 1. 完整的错误信息管理(错误码、消息、位置信息)
* 2. 支持异常链(嵌套异常)
* 3. 多态支持(clone、自定义消息)
* 4. 线程安全的 what() 缓存
* 5. 支持 C++20 source_location
*/
class ExceptionBase : public std::exception {
public:
// ============ 构造函数 ============
explicit ExceptionBase(
int code = 0,
const std::string& message = "",
const std::source_location& location = std::source_location::current()
) noexcept
: m_code(code)
, m_message(message)
, m_file(location.file_name())
, m_line(location.line())
, m_function(location.function_name())
, m_what_cache()
{
}
// 支持从字符串字面量构造
explicit ExceptionBase(
const std::string& message,
const std::source_location& location = std::source_location::current()
) noexcept
: m_code(0)
, m_message(message)
, m_file(location.file_name())
, m_line(location.line())
, m_function(location.function_name())
, m_what_cache()
{
}
// ============ 拷贝和移动 ============
ExceptionBase(const ExceptionBase& other) noexcept
: std::exception(other)
, m_code(other.m_code)
, m_message(other.m_message)
, m_file(other.m_file)
, m_line(other.m_line)
, m_function(other.m_function)
, m_nested(other.m_nested ? other.m_nested->clone() : nullptr)
, m_what_cache()
{
}
ExceptionBase(ExceptionBase&& other) noexcept
: std::exception(std::move(other))
, m_code(other.m_code)
, m_message(std::move(other.m_message))
, m_file(std::move(other.m_file))
, m_line(other.m_line)
, m_function(std::move(other.m_function))
, m_nested(std::move(other.m_nested))
, m_what_cache(std::move(other.m_what_cache))
{
other.m_code = 0;
other.m_line = 0;
}
virtual ~ExceptionBase() noexcept = default;
// ============ 赋值运算符 ============
ExceptionBase& operator=(const ExceptionBase& other) noexcept {
if (this != &other) {
m_code = other.m_code;
m_message = other.m_message;
m_file = other.m_file;
m_line = other.m_line;
m_function = other.m_function;
m_nested = other.m_nested ? other.m_nested->clone() : nullptr;
m_what_cache.clear();
}
return *this;
}
ExceptionBase& operator=(ExceptionBase&& other) noexcept {
if (this != &other) {
m_code = other.m_code;
m_message = std::move(other.m_message);
m_file = std::move(other.m_file);
m_line = other.m_line;
m_function = std::move(other.m_function);
m_nested = std::move(other.m_nested);
m_what_cache = std::move(other.m_what_cache);
other.m_code = 0;
other.m_line = 0;
}
return *this;
}
// ============ 访问器 ============
int code() const noexcept { return m_code; }
const std::string& message() const noexcept { return m_message; }
const std::string& file() const noexcept { return m_file; }
int line() const noexcept { return m_line; }
const std::string& function() const noexcept { return m_function; }
const ExceptionBase* nested() const noexcept { return m_nested.get(); }
// ============ 异常链操作 ============
void set_nested(std::unique_ptr<ExceptionBase> nested) noexcept {
m_nested = std::move(nested);
m_what_cache.clear();
}
template<typename ExType>
void set_nested(const ExType& nested_ex) noexcept {
static_assert(std::is_base_of<ExceptionBase, ExType>::value,
"ExType must inherit from ExceptionBase");
m_nested = std::make_unique<ExType>(nested_ex);
m_what_cache.clear();
}
// ============ std::exception 接口 ============
const char* what() const noexcept override {
if (m_what_cache.empty()) {
build_what_message();
}
return m_what_cache.c_str();
}
// ============ 多态支持 ============
virtual std::unique_ptr<ExceptionBase> clone() const {
return std::make_unique<ExceptionBase>(*this);
}
// ============ 重新抛出 ============
[[noreturn]] void rethrow() const {
throw *this;
}
protected:
// ============ 可重写的消息构建方法 ============
virtual std::string build_message() const {
std::string result;
// 错误码
if (m_code != 0) {
result += "[Error " + std::to_string(m_code) + "] ";
}
// 错误消息
result += m_message;
/* ===== 以下「位置信息」与「嵌套异常」暂时以注释方式保留 =====
* 原因:黑盒检测发现 what() 的返回值会透传到 HTTP 响应体,
* 导致泄露源码文件路径、行号、C++ 模板类型签名(函数名),
* 便于攻击者定位代码、确认技术栈、构造针对性 payload(P0-3)。
* 排障:完整信息仍由各 handler 通过 std::cerr 输出到服务端日志。
* 恢复:如需调试可临时取消下方注释;生产环境必须保持注释状态,
* 并确保 what() 不直接进入客户端响应。
* ====================================================================
// 位置信息
if (!m_file.empty()) {
result += " (at " + m_file;
if (m_line > 0) {
result += ":" + std::to_string(m_line);
}
if (!m_function.empty()) {
result += " in " + m_function;
}
result += ")";
}
// 嵌套异常
if (m_nested) {
result += "\n Caused by: " + std::string(m_nested->what());
}
* ==================================================================== */
return result;
}
private:
// ============ 内部方法 ============
void build_what_message() const noexcept {
try {
m_what_cache = build_message();
} catch (...) {
// 降级处理
m_what_cache = "Exception: " + m_message;
}
}
private:
// ============ 成员变量 ============
int m_code;
std::string m_message;
std::string m_file;
int m_line;
std::string m_function;
std::unique_ptr<ExceptionBase> m_nested;
mutable std::string m_what_cache;
};
// ============================================================================
// 宏定义 - 方案二完整版
// ============================================================================
/**
* @brief 抛出指定类型的异常(自动添加 source_location)
*
* 适用场景:子类构造函数与 ExceptionBase 相同
*
* 使用示例:
* THROW_EX(NetworkException, 404, "Server not found")
* THROW_EX(DatabaseException, 100, "Connection timeout")
*/
#define THROW_EX(ExType, code, msg) \
throw ExType(code, msg, std::source_location::current())
/**
* @brief 抛出异常(仅消息,不传错误码)
*
* 适用场景:子类支持只传消息的构造函数
*
* 使用示例:
* THROW_EX_MSG(ValidationException, "Invalid input data")
*/
#define THROW_EX_MSG(ExType, msg) \
throw ExType(msg, std::source_location::current())
/**
* @brief 抛出异常(支持任意构造函数参数)
*
* 适用场景:子类有额外的构造函数参数
*
* 使用示例:
* // 子类定义
* class FileException : public ExceptionBase {
* public:
* FileException(int code, const std::string& msg,
* const std::string& filename,
* const std::source_location& loc = std::source_location::current())
* : ExceptionBase(code, msg, loc), m_filename(filename) {}
* };
*
* // 使用
* THROW_EX_CUSTOM(FileException, 404, "File not found", "/etc/config.txt")
*/
#define THROW_EX_CUSTOM(ExType, ...) \
throw ExType(__VA_ARGS__, std::source_location::current())
/**
* @brief 抛出异常并携带嵌套异常
*
* 适用场景:需要保留原始异常信息
*
* 使用示例:
* try {
* connect_to_server();
* } catch (const NetworkException& e) {
* THROW_EX_NESTED(ServiceException, 500, "Service failed", e)
* }
*/
#define THROW_EX_NESTED(ExType, code, msg, nested_ex) \
do { \
auto ex = ExType(code, msg, std::source_location::current()); \
ex.set_nested(nested_ex); \
throw ex; \
} while(0)
/**
* @brief 抛出异常并携带嵌套异常(支持自定义参数)
*
* 使用示例:
* THROW_EX_NESTED_CUSTOM(FileException, 500, "Read failed",
* original_ex, "/data/file.txt")
*/
#define THROW_EX_NESTED_CUSTOM(ExType, code, msg, nested_ex, ...) \
do { \
auto ex = ExType(code, msg, __VA_ARGS__, std::source_location::current()); \
ex.set_nested(nested_ex); \
throw ex; \
} while(0)
/**
* @brief 检查条件,失败则抛出异常
*
* 使用示例:
* THROW_IF(ptr == nullptr, NullPointerException, 100, "Pointer is null")
* THROW_IF(data.empty(), ValidationException, 200, "Data cannot be empty")
*/
#define THROW_IF(cond, ExType, code, msg) \
do { \
if (cond) { \
THROW_EX(ExType, code, msg); \
} \
} while(0)
/**
* @brief 检查条件,失败则抛出异常(仅消息)
*/
#define THROW_IF_MSG(cond, ExType, msg) \
do { \
if (cond) { \
THROW_EX_MSG(ExType, msg); \
} \
} while(0)
/**
* @brief 检查条件,失败则抛出异常(自定义参数)
*/
#define THROW_IF_CUSTOM(cond, ExType, ...) \
do { \
if (cond) { \
THROW_EX_CUSTOM(ExType, __VA_ARGS__); \
} \
} while(0)
/**
* @brief 检查条件,失败则抛出异常(带嵌套异常)
*/
#define THROW_IF_NESTED(cond, ExType, code, msg, nested_ex) \
do { \
if (cond) { \
THROW_EX_NESTED(ExType, code, msg, nested_ex); \
} \
} while(0)
/**
* @brief 检查条件,失败则抛出异常(带嵌套异常和自定义参数)
*/
#define THROW_IF_NESTED_CUSTOM(cond, ExType, code, msg, nested_ex, ...) \
do { \
if (cond) { \
THROW_EX_NESTED_CUSTOM(ExType, code, msg, nested_ex, __VA_ARGS__); \
} \
} while(0)
// ============================================================================
// 辅助宏 - 用于格式化错误消息
// ============================================================================
/**
* @brief 格式化错误消息(使用流式语法)
*
* 使用示例:
* THROW_EX_FMT(NetworkException, 404, "Failed to connect to " << host << ":" << port)
*/
#define THROW_EX_FMT(ExType, code, msg_expr) \
do { \
std::ostringstream _oss; \
_oss << msg_expr; \
THROW_EX(ExType, code, _oss.str()); \
} while(0)
/**
* @brief 格式化错误消息(使用 printf 风格)
*
* 使用示例:
* THROW_EX_PRINTF(NetworkException, 404, "Failed to connect to %s:%d", host.c_str(), port)
*/
#define THROW_EX_PRINTF(ExType, code, fmt, ...) \
do { \
char _buf[1024]; \
snprintf(_buf, sizeof(_buf), fmt, __VA_ARGS__); \
THROW_EX(ExType, code, std::string(_buf)); \
} while(0)
/**
* @brief 条件检查 + 格式化消息
*/
#define THROW_IF_FMT(cond, ExType, code, msg_expr) \
do { \
if (cond) { \
THROW_EX_FMT(ExType, code, msg_expr); \
} \
} while(0)
/**
* @brief 条件检查 + printf 风格格式化
*/
#define THROW_IF_PRINTF(cond, ExType, code, fmt, ...) \
do { \
if (cond) { \
THROW_EX_PRINTF(ExType, code, fmt, __VA_ARGS__); \
} \
} while(0)
#endif // EXCEPTION_BASE_H