fix: fix comment for new added files.

- translate zh-CN comment into en-US.
- change some comment into Doxygen style.
- add lost Doxygen comment.
- enrich the testbench for ceil_div.
- add lost metaprogramming functions for some files in macro namespace.
This commit is contained in:
2025-08-11 21:57:42 +08:00
parent 17540072d3
commit 20a9ef4166
8 changed files with 419 additions and 187 deletions

View File

@ -7,41 +7,50 @@
/**
* @brief The namespace providing functions which safely cast numeric value from one type to another.
* @details
* 在编写Rust的时候深刻地认识到在范围不同的类型之间进行转换是一件非常重要且容易出错的事情。
* 对于拓宽转换,我们可以安全地,不加考虑地直接进行转换。
* 对于收窄转换我们则需要引入Result机制来判断是否出错。
* 这些功能在Rust中对所有基础类型都有实现且通过From trait来进行统一管理非常完美。
* 但在C++中,我们需要手动重现它们。
* When writing Rust code, I deeply realized that casting between types with different ranges is very important,
* but it is greatly easy to make mistake which finally cause fatal error.
* For widening conversions, we can safely perform them directly without consideration.
* For narrowing conversions, we need to introduce a Result mechanism to determine if errors occur.
* These features are implemented for all primitive types in Rust,
* and managed uniformly through the \c From and \c TryFrom trait, which is perfect.
* But in C++, we need to manually replicate them.
*
* 在这个命名空间中我们将转换函数分为了两类一种是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的最小大小32位只有小于其最小大小的数据才能安全的转换例如u16
* 但在C++中我们不能那么做,也无从做起,因为我们不能知道每一个变长基本数据类型的最小大小。
* 所以我们使用另一种方案来解决这个问题。
* In this namespace, we divide conversion functions into two categories:
* \li \c to for definitely safe conversions,
* \li \c try_to for potentially risky conversions.
* There is a metaprogramming concept <TT>CAN_SAFE_TO\<T, U\></TT> to determine if a conversion is safe,
* which applied for these functions as the constraint.
*
* However, directly using \c CAN_SAFE_TO to determine whether the convertion is safe is incorrect.
* In Rust, whether these conversions are safe is manually determined by different traits ( \c From and \c TryFrom).
* But in C++, we brutally use the size of data types in compile-time to determine safe conversion,
* which causes variable-length data types produces different \c CAN_SAFE_TO results on different platforms,
* which affecting our code's portability.
* For example, we can use 'to' directly for conversion between \c uint64_t and \c size_t: on 64-bit platforms,
* but on 32-bit platforms it causes compile-time errors, resulting in portability issues.
*
* Rust's solution is to define the minimum size of \c usize (32-bit),
* allowing safe conversion only for data smaller than this (e.g. \c u16 ).
* But in C++ we can't do this because we can't know the minimum size of every variable-length primitive data type.
* So we use another solution.
*
* 我们的解决方案是为to函数强制应用CAN_SAFE_TO<T, U>规则而不对try_to函数强制应用。
* 在try_to函数内部我们再使用CAN_SAFE_TO<T, U>,判断是否可以安全转换,如果可以,就直接转换,否则做一系列判定。
* 这样以来程序员就需要手动判定两个数据类型之间是否肯定可以安全转换然后再使用to函数。
* 但至少编译器会在你误使用to函数的时候及时地给你抛出错误这时候只要改用try_to就好了。
* 同时如果全篇使用try_to也不会因为做了无用检查而影响性能因为无用检查已经通过if constexpr删除了。
* Our solution is to enforce \c CAN_SAFE_TO rules for \c to functions but not for \c try_to functions.
* Inside \c try_to, we use \c CAN_SAFE_TO to determine if safe conversion is possible.
* If yes, convert directly, otherwise perform essential checks before convertion.
* So under this solution, programmers need manually determine if conversion between two types is definitely safe before using \c to .
* But at least, the compiler will throw errors when \c to is inviable, and the only thing you should do is switching to \c try_to.
* Also, using \c try_to everywhere won't impact performance as unnecessary checks are removed via <TT>if constexpr</TT> statement in function.
*
* 由于没有需求,目前不支持以下转换:
* \li 浮点数与浮点数之间的转换。
* \li 浮点数与整数之间的转换。
* Currently unsupported conversions due to lack of demand:
* \li Floating-point to floating-point conversions
* \li Floating-point to integer conversions
*/
namespace yycc::num::safe_cast {
/// @brief All possible error raised in this module.
enum class CastError {
Overflow, ///< 转换时发生向上溢出错误。
Underflow, ///< 转换时发生向下溢出错误。
Overflow, ///< Overflow error occurred during conversion.
Underflow, ///< Underflow error occurred during conversion.
};
/// @brief The result type in this module.
@ -50,17 +59,17 @@ namespace yycc::num::safe_cast {
/**
* @private
* @brief 检查一个整数类型是否可以安全地转换为另一个整数类型
* @return
* @brief Check if an integer type can be safely converted to another integer type
* @return True if it can be safely converted, false otherwise.
*/
template<typename TDst, typename TSrc>
requires std::integral<TDst> && std::integral<TSrc>
constexpr bool can_safe_to() {
// 检查 TSrc TDst 是否有符号
// Fetch the sign info of TSrc and TDst.
constexpr bool is_src_signed = std::is_signed_v<TSrc>;
constexpr bool is_dst_signed = std::is_signed_v<TDst>;
// 获取 TSrc TDst 的范围
// Get the range of TSrc and 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();
@ -68,21 +77,22 @@ namespace yycc::num::safe_cast {
if constexpr (is_src_signed) {
if constexpr (is_dst_signed) {
// 有符号向有符号转换,两端都需要检查。
// 如果完全处于范围内,则肯定可以安全转换。
// Signed to signed conversion, both upper and lower bound need to be checked.
// If completely within the range, it is definitely safe.
return dst_min <= src_min && dst_max >= src_max;
} else {
// 有符号向无符号转换,总是不安全的。
// 因为会存在负数情况
// Signed to unsigned conversion, always unsafe.
// Because negative numbers exist.
return false;
}
} else {
if constexpr (is_dst_signed) {
// 无符号向有符号转换,则只检查上端。
// 如果上端够小内,则肯定可以安全转换。
// Unsigned to signed conversion, only check the upper bound.
// If the upper bound is small enough, it is definitely safe.
return dst_max >= src_max;
} else {
// 无符号向无符号转换则只检查上端因为下端均为0。
// Unsigned to unsigned conversion, only check the upper bound,
// because the lower bound is 0.
return dst_max >= src_max;
}
}
@ -90,17 +100,17 @@ namespace yycc::num::safe_cast {
/**
* @private
* @brief can_safe_to()的变量版本
* @details 为后文约束提供的便利变量
* @brief Variable version of can_safe_to()
* @details Convenience variable for subsequent constraints
*/
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<> trait只不过颠倒了一下顺序从from变为to
* @return 转换后的结果。
* @brief Convert an integer type to another integer type.
* @details Similar to Rust's \c From trait, but with reversed direction (from "from" to "to").
* @return The converted result.
*/
template<typename TDst, typename TSrc>
requires std::integral<TDst> && std::integral<TSrc> && CAN_SAFE_TO<TDst, TSrc>
@ -109,42 +119,42 @@ namespace yycc::num::safe_cast {
}
/**
* @brief 尝试将一个整数类型转换为另一个整数类型。
* @details 类似于Rust中的TryFrom<> trait只不过颠倒了一下顺序从from变为to
* @return 一个包含转换结果的Result类型。
* @brief Attempt to convert an integer type to another integer type.
* @details Similar to Rust's \c TryFrom trait, but with reversed direction (from "from" to "to").
* @return A result containing the conversion result or convertion error.
*/
template<typename TDst, typename TSrc>
requires std::integral<TDst> && std::integral<TSrc>
Result<TDst> try_to(const TSrc& lhs) {
// 检查是否可以直接转换
// Check whether we can convert directly.
if constexpr (CAN_SAFE_TO<TDst, TSrc>) {
return static_cast<TDst>(lhs);
} else {
// 检查 TSrc TDst 是否有符号
// Fetch the sign info of TSrc and TDst.
constexpr bool is_src_signed = std::is_signed_v<TSrc>;
constexpr bool is_dst_signed = std::is_signed_v<TDst>;
// 获取 TSrc TDst 的范围
// Fetch the range of TSrc and 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();
// 检查是否可以安全转换
// Check whether we can convert safely.
if constexpr (is_src_signed == is_dst_signed) {
// 如果两者都是有符号或无符号,直接比较范围
// If both are signed or unsigned, compare ranges directly.
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 signs are different, we need to check the value range.
if constexpr (is_src_signed) {
// 如果 TSrc 是有符号TDst 是无符号,需要确保 lhs 不小于 0
// If TSrc is signed, TDst is unsigned, we need to ensure lhs is not negative.
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 TSrc is unsigned, TDst is signed, we need to ensure lhs is not greater than dst_max.
if (lhs > dst_max) return std::unexpected(CastError::Overflow);
return static_cast<TDst>(lhs);
}