Files
YYCCommonplace/src/yycc/num/safe_cast.hpp

156 lines
7.6 KiB
C++
Raw Normal View History

#pragma once
#include <expected>
#include <concepts>
#include <limits>
#include <type_traits>
/**
* @brief The namespace providing functions which safely cast numeric value from one type to another.
* @details
* Rust的时候
*
* Result机制
* Rust中对所有基础类型都有实现From trait来进行统一管理
* C++
*
* to
* try_to
* CAN_SAFE_TO<T, U>
* Rust中trait的不同From和TryFrom
*
* 使CAN_SAFE_TO<T, U>
*
* uint64_t和size_t之间的变换64使to函数直接转换
* 32
* Rust中的解决方案是Rust规定了Usize的最小大小32u16
* C++
* 使
*
* to函数强制应用CAN_SAFE_TO<T, U>try_to函数强制应用
* try_to函数内部使CAN_SAFE_TO<T, U>
* 使to函数
* 使to函数的时候try_to就好了
* 使try_toif constexpr删除了
*
*
* \li
* \li
*/
namespace yycc::num::safe_cast {
/// @brief All possible error raised in this module.
enum class CastError {
Overflow, ///< 转换时发生向上溢出错误。
Underflow, ///< 转换时发生向下溢出错误。
};
/// @brief The result type in this module.
template<typename T>
using Result = std::expected<T, CastError>;
/**
* @private
* @brief
* @return
*/
template<typename TDst, typename TSrc>
requires std::integral<TDst> && std::integral<TSrc>
constexpr bool can_safe_to() {
// 检查 TSrc 和 TDst 是否有符号
constexpr bool is_src_signed = std::is_signed_v<TSrc>;
constexpr bool is_dst_signed = std::is_signed_v<TDst>;
// 获取 TSrc 和 TDst 的范围
constexpr TSrc src_min = std::numeric_limits<TSrc>::min();
constexpr TSrc src_max = std::numeric_limits<TSrc>::max();
constexpr TDst dst_min = std::numeric_limits<TDst>::min();
constexpr TDst dst_max = std::numeric_limits<TDst>::max();
if constexpr (is_src_signed) {
if constexpr (is_dst_signed) {
// 有符号向有符号转换,两端都需要检查。
// 如果完全处于范围内,则肯定可以安全转换。
return dst_min <= src_min && dst_max >= src_max;
} else {
// 有符号向无符号转换,总是不安全的。
// 因为会存在负数情况
return false;
}
} else {
if constexpr (is_dst_signed) {
// 无符号向有符号转换,则只检查上端。
// 如果上端够小内,则肯定可以安全转换。
return dst_max >= src_max;
} else {
// 无符号向无符号转换则只检查上端因为下端均为0。
return dst_max >= src_max;
}
}
}
/**
* @private
* @brief can_safe_to()
* @details 便
*/
template<typename TDst, typename TSrc>
requires std::integral<TDst> && std::integral<TSrc>
inline constexpr bool CAN_SAFE_TO = can_safe_to<TDst, TSrc>();
/**
* @brief
* @details Rust中的From<> traitfrom变为to
* @return
*/
template<typename TDst, typename TSrc>
requires std::integral<TDst> && std::integral<TSrc> && CAN_SAFE_TO<TDst, TSrc>
TDst to(const TSrc& lhs) {
return static_cast<TDst>(lhs);
}
/**
* @brief
* @details Rust中的TryFrom<> traitfrom变为to
* @return Result类型
*/
template<typename TDst, typename TSrc>
requires std::integral<TDst> && std::integral<TSrc>
Result<TDst> try_to(const TSrc& lhs) {
// 检查是否可以直接转换
if constexpr (CAN_SAFE_TO<TDst, TSrc>) {
return static_cast<TDst>(lhs);
} else {
// 检查 TSrc 和 TDst 是否有符号
constexpr bool is_src_signed = std::is_signed_v<TSrc>;
constexpr bool is_dst_signed = std::is_signed_v<TDst>;
// 获取 TSrc 和 TDst 的范围
constexpr TSrc src_min = std::numeric_limits<TSrc>::min();
constexpr TSrc src_max = std::numeric_limits<TSrc>::max();
constexpr TDst dst_min = std::numeric_limits<TDst>::min();
constexpr TDst dst_max = std::numeric_limits<TDst>::max();
// 检查是否可以安全转换
if constexpr (is_src_signed == is_dst_signed) {
// 如果两者都是有符号或无符号,直接比较范围
if (lhs < dst_min) return std::unexpected(CastError::Underflow);
if (lhs > dst_max) return std::unexpected(CastError::Overflow);
return static_cast<TDst>(lhs);
} else {
// 两者符号不一致
if constexpr (is_src_signed) {
// 如果 TSrc 是有符号TDst 是无符号,需要确保 lhs 不小于 0
if (lhs < 0) return std::unexpected(CastError::Underflow);
if (lhs > dst_max) return std::unexpected(CastError::Overflow);
return static_cast<TDst>(lhs);
} else {
// 如果 TSrc 是无符号TDst 是有符号,需要确保 lhs 不大于 TDst 的最大值
if (lhs > dst_max) return std::unexpected(CastError::Overflow);
return static_cast<TDst>(lhs);
}
}
}
}
} // namespace yycc::num::safe_cast