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

@ -10,12 +10,11 @@
*/ */
namespace yycc::flag_enum { namespace yycc::flag_enum {
// YYC MARK:
//
// Reference: // Reference:
// Enum operator overload: https://stackoverflow.com/a/71107019 // Enum operator overload: https://stackoverflow.com/a/71107019
// Constexpr operator overload: https://stackoverflow.com/a/17746099 // Constexpr operator overload: https://stackoverflow.com/a/17746099
//
// YYC MARK:
// Currently, the solution of "Constexpr operator overload" is not used. // Currently, the solution of "Constexpr operator overload" is not used.
// We use explicit way, "Enum operator overload". // We use explicit way, "Enum operator overload".

View File

@ -4,3 +4,25 @@
#if (defined(YYCC_PTRSIZE_32) + defined(YYCC_PTRSIZE_64)) != 1 #if (defined(YYCC_PTRSIZE_32) + defined(YYCC_PTRSIZE_64)) != 1
#error "Current environment used pointer size is not supported!" #error "Current environment used pointer size is not supported!"
#endif #endif
namespace yycc::macro::ptr_size {
/// @brief The pointer size kind.
enum class PtrSizeKind {
Bits32, ///< 32-bit environment
Bits64 ///< 64-bit environment
};
/**
* @brief Fetch the pointer size
* @return The kind of pointer size.
*/
inline constexpr PtrSizeKind get_ptr_size() {
#if defined(YYCC_PTRSIZE_32)
return PtrSizeKind::Bits32;
#else
return PtrSizeKind::Bits64;
#endif
}
} // namespace yycc::macro::ptr_size

View File

@ -12,3 +12,28 @@
#else #else
#error "Current STL is not supported!" #error "Current STL is not supported!"
#endif #endif
namespace yycc::macro::stl {
/// @brief The STL implementation kind.
enum class StlKind {
MSSTL, ///< Microsoft STL
GNUSTL, ///< GNU STL
CLANGSTL ///< Clang STL
};
/**
* @brief Fetch the STL implementation
* @return The kind of STL implementation.
*/
inline constexpr StlKind get_stl() {
#if defined(YYCC_STL_MSSTL)
return StlKind::MSSTL;
#elif defined(YYCC_STL_GNUSTL)
return StlKind::GNUSTL;
#else
return StlKind::CLANGSTL;
#endif
}
} // namespace yycc::macro::stl

View File

@ -3,32 +3,35 @@
#include <concepts> #include <concepts>
/** /**
* @brief The namespace providing function relative robust numeric operations. * @brief The namespace providing functions for robust numeric operations.
* @details * @details
* 在使用Rust编写一些程序后深刻地认识到Rust中针对基本类型的运算符的丰富程度。 * After writing some programs in Rust, I deeply appreciated the richness of operators
* 提供了诸如整数向上取整除法等便利操作。 * for primitive types in Rust, which provides convenient operations like ceiling integral division.
* 因此我在这个命名空间里复刻Rust中的这些便利功能。 * Therefore, I replicate these convenient features from Rust in this namespace.
* *
* 由于没有需求,目前暂未实现以下功能: * Currently unimplemented features due to lack of demand:
* \li 仅支持无符号整数向上取整除法。 * \li Only supports unsigned integer ceiling division
* \li 显式指定运算溢出行为
*/ */
namespace yycc::num::op { namespace yycc::num::op {
/** /**
* @brief 无符号整数的向上整除 * @brief Unsigned integer ceiling division
* @details * @details
* 执行两个无符号整数之间的整除,并将结果向上取整。 * Performs division between two unsigned integers and rounds up the result.
* 该函数可能在很多加密函数中进行使用以分配合适空间。 * @exception std::logic_error If the divisor is zero
* @tparam T 除法操作建立在的无符号整数类型 * @tparam T The unsigned integer type for division operation
* @param[in] lhs 左操作数 * @param[in] lhs Left operand
* @param[in] rhs 右操作数 * @param[in] rhs Right operand
* @return 向上取整的整除结果 * @return Ceiling division result
*/ */
template<typename T> template<typename T>
requires std::unsigned_integral<T> requires std::unsigned_integral<T>
T div_ceil(T lhs, T rhs) { T div_ceil(T lhs, T rhs) {
// Check divisor first
if (rhs == 0) throw std::logic_error("div with 0"); if (rhs == 0) throw std::logic_error("div with 0");
// YYC MARK:
// We use this algorithm, instead of traditional `(lhs + rhs - 1) / rhs`,
// which may have unsafe overflow case.
return (lhs % rhs == 0) ? (lhs / rhs) : (lhs / rhs) + 1u; return (lhs % rhs == 0) ? (lhs / rhs) : (lhs / rhs) + 1u;
} }

View File

@ -7,41 +7,50 @@
/** /**
* @brief The namespace providing functions which safely cast numeric value from one type to another. * @brief The namespace providing functions which safely cast numeric value from one type to another.
* @details * @details
* 在编写Rust的时候深刻地认识到在范围不同的类型之间进行转换是一件非常重要且容易出错的事情。 * 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.
* 对于收窄转换我们则需要引入Result机制来判断是否出错。 * For widening conversions, we can safely perform them directly without consideration.
* 这些功能在Rust中对所有基础类型都有实现且通过From trait来进行统一管理非常完美。 * For narrowing conversions, we need to introduce a Result mechanism to determine if errors occur.
* 但在C++中,我们需要手动重现它们。 * 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适用于肯定能安全转换的类型。 * In this namespace, we divide conversion functions into two categories:
* 另一种是try_to用于可能存在风险的转换。 * \li \c to for definitely safe conversions,
* 但直接套用CAN_SAFE_TO<T, U>来决定是否可以安全转换是不正确的。 * \li \c try_to for potentially risky conversions.
* Rust中这些转换安全与否是通过trait的不同From和TryFrom来手动编写的。 * 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.
* 这就导致那些变长基本数据类型在不同编译平台上致使CAN_SAFE_TO<T, U>输出不同结果, *
* 从而影响我们的代码的可移植性。 * However, directly using \c CAN_SAFE_TO to determine whether the convertion is safe is incorrect.
* 例如在uint64_t和size_t之间的变换在64位平台上我们可以使用to函数直接转换 * In Rust, whether these conversions are safe is manually determined by different traits ( \c From and \c TryFrom).
* 然而在32位平台上就会出现编译报错造成了代码的可移植性问题。 * But in C++, we brutally use the size of data types in compile-time to determine safe conversion,
* Rust中的解决方案是Rust规定了Usize的最小大小32位只有小于其最小大小的数据才能安全的转换例如u16 * which causes variable-length data types produces different \c CAN_SAFE_TO results on different platforms,
* 但在C++中我们不能那么做,也无从做起,因为我们不能知道每一个变长基本数据类型的最小大小。 * 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函数强制应用。 * Our solution is to enforce \c CAN_SAFE_TO rules for \c to functions but not for \c try_to functions.
* 在try_to函数内部我们再使用CAN_SAFE_TO<T, U>,判断是否可以安全转换,如果可以,就直接转换,否则做一系列判定。 * Inside \c try_to, we use \c CAN_SAFE_TO to determine if safe conversion is possible.
* 这样以来程序员就需要手动判定两个数据类型之间是否肯定可以安全转换然后再使用to函数。 * If yes, convert directly, otherwise perform essential checks before convertion.
* 但至少编译器会在你误使用to函数的时候及时地给你抛出错误这时候只要改用try_to就好了。 * So under this solution, programmers need manually determine if conversion between two types is definitely safe before using \c to .
* 同时如果全篇使用try_to也不会因为做了无用检查而影响性能因为无用检查已经通过if constexpr删除了。 * 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.
* *
* 由于没有需求,目前不支持以下转换: * Currently unsupported conversions due to lack of demand:
* \li 浮点数与浮点数之间的转换。 * \li Floating-point to floating-point conversions
* \li 浮点数与整数之间的转换。 * \li Floating-point to integer conversions
*/ */
namespace yycc::num::safe_cast { namespace yycc::num::safe_cast {
/// @brief All possible error raised in this module. /// @brief All possible error raised in this module.
enum class CastError { enum class CastError {
Overflow, ///< 转换时发生向上溢出错误。 Overflow, ///< Overflow error occurred during conversion.
Underflow, ///< 转换时发生向下溢出错误。 Underflow, ///< Underflow error occurred during conversion.
}; };
/// @brief The result type in this module. /// @brief The result type in this module.
@ -50,17 +59,17 @@ namespace yycc::num::safe_cast {
/** /**
* @private * @private
* @brief 检查一个整数类型是否可以安全地转换为另一个整数类型 * @brief Check if an integer type can be safely converted to another integer type
* @return * @return True if it can be safely converted, false otherwise.
*/ */
template<typename TDst, typename TSrc> template<typename TDst, typename TSrc>
requires std::integral<TDst> && std::integral<TSrc> requires std::integral<TDst> && std::integral<TSrc>
constexpr bool can_safe_to() { 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_src_signed = std::is_signed_v<TSrc>;
constexpr bool is_dst_signed = std::is_signed_v<TDst>; 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_min = std::numeric_limits<TSrc>::min();
constexpr TSrc src_max = std::numeric_limits<TSrc>::max(); constexpr TSrc src_max = std::numeric_limits<TSrc>::max();
constexpr TDst dst_min = std::numeric_limits<TDst>::min(); 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_src_signed) {
if constexpr (is_dst_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; return dst_min <= src_min && dst_max >= src_max;
} else { } else {
// 有符号向无符号转换,总是不安全的。 // Signed to unsigned conversion, always unsafe.
// 因为会存在负数情况 // Because negative numbers exist.
return false; return false;
} }
} else { } else {
if constexpr (is_dst_signed) { 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; return dst_max >= src_max;
} else { } else {
// 无符号向无符号转换则只检查上端因为下端均为0。 // Unsigned to unsigned conversion, only check the upper bound,
// because the lower bound is 0.
return dst_max >= src_max; return dst_max >= src_max;
} }
} }
@ -90,17 +100,17 @@ namespace yycc::num::safe_cast {
/** /**
* @private * @private
* @brief can_safe_to()的变量版本 * @brief Variable version of can_safe_to()
* @details 为后文约束提供的便利变量 * @details Convenience variable for subsequent constraints
*/ */
template<typename TDst, typename TSrc> template<typename TDst, typename TSrc>
requires std::integral<TDst> && std::integral<TSrc> requires std::integral<TDst> && std::integral<TSrc>
inline constexpr bool CAN_SAFE_TO = can_safe_to<TDst, TSrc>(); inline constexpr bool CAN_SAFE_TO = can_safe_to<TDst, TSrc>();
/** /**
* @brief 将一个整数类型转换为另一个整数类型。 * @brief Convert an integer type to another integer type.
* @details 类似于Rust中的From<> trait只不过颠倒了一下顺序从from变为to * @details Similar to Rust's \c From trait, but with reversed direction (from "from" to "to").
* @return 转换后的结果。 * @return The converted result.
*/ */
template<typename TDst, typename TSrc> template<typename TDst, typename TSrc>
requires std::integral<TDst> && std::integral<TSrc> && CAN_SAFE_TO<TDst, TSrc> requires std::integral<TDst> && std::integral<TSrc> && CAN_SAFE_TO<TDst, TSrc>
@ -109,42 +119,42 @@ namespace yycc::num::safe_cast {
} }
/** /**
* @brief 尝试将一个整数类型转换为另一个整数类型。 * @brief Attempt to convert an integer type to another integer type.
* @details 类似于Rust中的TryFrom<> trait只不过颠倒了一下顺序从from变为to * @details Similar to Rust's \c TryFrom trait, but with reversed direction (from "from" to "to").
* @return 一个包含转换结果的Result类型。 * @return A result containing the conversion result or convertion error.
*/ */
template<typename TDst, typename TSrc> template<typename TDst, typename TSrc>
requires std::integral<TDst> && std::integral<TSrc> requires std::integral<TDst> && std::integral<TSrc>
Result<TDst> try_to(const TSrc& lhs) { Result<TDst> try_to(const TSrc& lhs) {
// 检查是否可以直接转换 // Check whether we can convert directly.
if constexpr (CAN_SAFE_TO<TDst, TSrc>) { if constexpr (CAN_SAFE_TO<TDst, TSrc>) {
return static_cast<TDst>(lhs); return static_cast<TDst>(lhs);
} else { } else {
// 检查 TSrc TDst 是否有符号 // Fetch the sign info of TSrc and TDst.
constexpr bool is_src_signed = std::is_signed_v<TSrc>; constexpr bool is_src_signed = std::is_signed_v<TSrc>;
constexpr bool is_dst_signed = std::is_signed_v<TDst>; 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_min = std::numeric_limits<TSrc>::min();
constexpr TSrc src_max = std::numeric_limits<TSrc>::max(); constexpr TSrc src_max = std::numeric_limits<TSrc>::max();
constexpr TDst dst_min = std::numeric_limits<TDst>::min(); constexpr TDst dst_min = std::numeric_limits<TDst>::min();
constexpr TDst dst_max = std::numeric_limits<TDst>::max(); constexpr TDst dst_max = std::numeric_limits<TDst>::max();
// 检查是否可以安全转换 // Check whether we can convert safely.
if constexpr (is_src_signed == is_dst_signed) { 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_min) return std::unexpected(CastError::Underflow);
if (lhs > dst_max) return std::unexpected(CastError::Overflow); if (lhs > dst_max) return std::unexpected(CastError::Overflow);
return static_cast<TDst>(lhs); return static_cast<TDst>(lhs);
} else { } else {
// 两者符号不一致 // If signs are different, we need to check the value range.
if constexpr (is_src_signed) { 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 < 0) return std::unexpected(CastError::Underflow);
if (lhs > dst_max) return std::unexpected(CastError::Overflow); if (lhs > dst_max) return std::unexpected(CastError::Overflow);
return static_cast<TDst>(lhs); return static_cast<TDst>(lhs);
} else { } 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); if (lhs > dst_max) return std::unexpected(CastError::Overflow);
return static_cast<TDst>(lhs); return static_cast<TDst>(lhs);
} }

View File

@ -24,54 +24,68 @@
/** /**
* @brief The namespace providing Rust-like safe arithmetic operations. * @brief The namespace providing Rust-like safe arithmetic operations.
* @details
* After writing some programs in Rust, I've deeply realized the richness of operators for primitive types in Rust.
* You can explicitly specify the behavior of arithmetic overflow
* (choose one of wrapping, checked, overflowing, and saturating).
* Therefore, I'm replicating these convenient features from Rust in this namespace.
*
* Additionally, I provide a bunch of extra operations, called ordinary operation.
* These functions are just an alias to wrapping operator, indicating wrapping is the normal case.
* These normal operators will not have any undefined behavior which C++ will make.
* It basically like \c Add, \c Sub, \c Mul, and \c Div traits in Rust,
* providing safe operations for primitive types.
*/ */
namespace yycc::num::safe_op { namespace yycc::num::safe_op {
// YYC MARK: /*
// 在使用Rust编写一些程序后深刻地认识到Rust中针对基本类型的运算符的丰富程度。 Implementation notes:
// 可以显式地指定运算溢出的行为wrappingcheckedoverflowingsaturating四选一
// 因此我在这个命名空间里复刻Rust中的这些便利功能。
// YYC MARK: - Wrapping operation:
// 实现说明: - Unsigned integer use default overflow behavior.
// * Wrapping 运算: - Signed integer will be casted into unsigned integer before operation to simulate wrapping overflow.
// - 无符号整数直接使用默认溢出行为。 - Checked operation:
// - 有符号整数通过无符号类型转换模拟溢出回绕。 - Use compiler built-in function to detect overflow, return std::optional<T>.
// * Checked 运算: - Return std::nullopt when division by zero or overflow.
// - 使用编译器内置函数检测溢出返回std::optional<T>。 - Overflowing operation:
// - 除零或溢出时返回std::nullopt。 - Return std::pair<T, bool> where bool indicates whether overflow occurs.
// * Overflowing 运算: - Explicitly handle 2 division undefined behavior:
// - 返回包含结果和溢出标志的结构体OverflowResult<T>。 - Division by zero.
// - 显式处理除零和有符号最小值除以-1的情况。 - Signed minimum value divided by -1.
// * Saturating 运算: - Saturating operation:
// - 溢出时返回类型最大值或最小值。 - Return maximum or minimum value when overflow or underflow occurs.
// - 根据操作数符号判断饱和方向。 - Determine saturation direction (overflow or underflow) based on operand sign.
*/
#pragma region Undefined Behaviors #pragma region Undefined Behaviors
// YYC MARK: /*
// 需要注意以下未定义行为: YYC MARK:
// * 有符号整数运算结果超出该类型表示范围时(如 INT_MAX + 1行为未定义。 Following undefined behaviors should be noticed:
// * 对 INT_MIN / -1 的除法可能溢出导致UB。 - Signed integer overflow and underflow (e.g. INT_MAX + 1).
// * 无符号整数溢出时行为是明确定义的(按模 2^N 回绕),但需注意逻辑错误。 - Perform `INT_MIN / -1` division.
// * 除数为零时行为未定义。 - The right operand in division is zero.
*/
/** /**
* @private * @private
* @brief 两数相加,同时考虑有符号整数溢出的未定义行为 * @brief Adds two numbers while considering the undefined behavior of signed integer overflow.
* @param a 加法的左操作数 * @tparam T Integer type
* @param b 加法的右操作数 * @param[in] a The left operand of the addition
* @return 考虑了有符号整数未定义行为的加法结果。 * @param[in] b The right operand of the addition
* @return The result of the addition that takes into account the undefined behavior of signed integers.
*/ */
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T ub_signed_int_add(T a, T b) { T ub_signed_int_add(T a, T b) {
if constexpr (std::is_unsigned_v<T>) { if constexpr (std::is_unsigned_v<T>) {
// 无符号数的加减乘都是自然回绕的,可以直接使用运算符。 // Add, Sub, Mul and Div for unsigned integer is natural wrapping.
// So we can use operator simply and directly.
return a + b; return a + b;
} else { } else {
// 有符号数的溢出在C++中是未定义的。 // In C++, it is undefined behavior that signed integer overflow.
// 所以需要使用位操作强制转换为无符号进行计算,然后再转回来。 // So we need cast them into unsigned integer forcely before operation,
// do operation for them, and cast the result back to simulate wrapping overflow.
using UT = std::make_unsigned_t<T>; using UT = std::make_unsigned_t<T>;
return static_cast<T>(static_cast<UT>(a) + static_cast<UT>(b)); return static_cast<T>(static_cast<UT>(a) + static_cast<UT>(b));
} }
@ -79,10 +93,11 @@ namespace yycc::num::safe_op {
/** /**
* @private * @private
* @brief 两数相减,同时考虑有符号整数溢出的未定义行为 * @brief Subtracts two numbers while considering the undefined behavior of signed integer overflow.
* @param a 减法的左操作数 * @tparam T Integer type
* @param b 减法的右操作数 * @param[in] a The left operand of the subtraction
* @return 考虑了有符号整数未定义行为的减法结果。 * @param[in] b The right operand of the subtraction
* @return The result of the subtraction that takes into account the undefined behavior of signed integers.
*/ */
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
@ -97,10 +112,11 @@ namespace yycc::num::safe_op {
/** /**
* @private * @private
* @brief 两数相乘,同时考虑有符号整数溢出的未定义行为 * @brief Multiplies two numbers while considering the undefined behavior of signed integer overflow.
* @param a 乘法的左操作数 * @tparam T Integer type
* @param b 乘法的右操作数 * @param[in] a The left operand of the multiplication
* @return 考虑了有符号整数未定义行为的乘法结果。 * @param[in] b The right operand of the multiplication
* @return The result of the multiplication that takes into account the undefined behavior of signed integers.
*/ */
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
@ -115,17 +131,19 @@ namespace yycc::num::safe_op {
/** /**
* @private * @private
* @brief 检查有符号最小值除以-1时的未定义行为 * @brief Checks for undefined behavior when dividing the minimum signed integer value by -1.
* @param a 除法的左操作数 * @tparam T Integer type
* @param b 除法的右操作数 * @param[in] a The left operand of the division
* @return 如果会发生未定义行为则为true否则为false * @param[in] b The right operand of the division
* @return Returns true if undefined behavior will occur, false otherwise.
*/ */
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
bool ub_signed_int_min_div_minus_one(T a, T b) { bool ub_signed_int_min_div_minus_one(T a, T b) {
if constexpr (std::is_signed_v<T>) { if constexpr (std::is_signed_v<T>) {
// 对有符号数来说, INT_MIN / -1 的除法可能溢出 // For signed value, `INT_MIN / -1` may cause overflow,
// (如 INT_MIN == -INT_MIN - 1 时),导致未定义行为。 // which finally cause the undefined behavior,
// due to the truth that `INT_MIN == -INT_MIN - 1`.
if (b == -1 && a == std::numeric_limits<T>::min()) { if (b == -1 && a == std::numeric_limits<T>::min()) {
return true; return true;
} else { } else {
@ -138,11 +156,11 @@ namespace yycc::num::safe_op {
/** /**
* @private * @private
* @brief 检查除以0的未定义行为 * @brief Checks for the undefined behavior of division by zero.
* @details 如果发生除以0则引发panic * @tparam T Integer type
* @param a 除法的左操作数 * @param[in] a The left operand of the division
* @param b 除法的右操作数 * @param[in] b The right operand of the division
* @return 如果会发生未定义行为则为true否则为false * @return Returns true if undefined behavior will occur, false otherwise.
*/ */
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
@ -155,18 +173,19 @@ namespace yycc::num::safe_op {
#pragma region Hardware Operation Overflow #pragma region Hardware Operation Overflow
// YYC MARK: // YYC MARK:
// 定义一个方便在Windows函数中计算的宏 // If we are using Windows function family,
// we define a convenient macro assisting overflow calculation.
#if defined(YYCC_HARDWARE_OVERFLOW_WIN32_FNS) #if defined(YYCC_HARDWARE_OVERFLOW_WIN32_FNS)
#define WIN_EASY_OPER(fn, ty, a, b, c) FAILED(fn(static_cast<ty>(a), static_cast<ty>(b), reinterpret_cast<ty*>(c))) #define WIN_EASY_OPER(fn, ty, a, b, c) FAILED(fn(static_cast<ty>(a), static_cast<ty>(b), reinterpret_cast<ty*>(c)))
#endif #endif
/** /**
* @private * @private
* @brief 基于硬件指令的带溢出检测的加法 * @brief Addition with overflow detection based on hardware instructions.
* @param[in] a 加法的左操作数 * @param[in] a The left operand of the addition.
* @param[in] b 加法的右操作数 * @param[in] b The right operand of the addition.
* @param[out] c 存放结果的引用 * @param[out] c The pointer to the variable storing result.
* @return 如果发生溢出则为true否则为false。 * @return Returns true if an overflow occurs, false otherwise.
*/ */
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
@ -203,8 +222,9 @@ namespace yycc::num::safe_op {
static_assert(std::false_type::value, "not supported integral type."); static_assert(std::false_type::value, "not supported integral type.");
} }
} }
// 由于Windows函数限制如果发生溢出函数不会计算结果。 // Due to the limitation of Windows function family,
// 所以我们要手动填写回绕了的结果 // if overflow or underflow occurs, there is no calculation result.
// So we need fill the wrapping value manually.
if (overflow) *c = ub_signed_int_add<T>(a, b); if (overflow) *c = ub_signed_int_add<T>(a, b);
return overflow; return overflow;
#endif #endif
@ -212,11 +232,11 @@ namespace yycc::num::safe_op {
/** /**
* @private * @private
* @brief 基于硬件指令的带溢出检测的减法 * @brief Subtraction with overflow detection based on hardware instructions.
* @param[in] a 减法的左操作数 * @param[in] a The left operand of the subtraction.
* @param[in] b 减法的右操作数 * @param[in] b The right operand of the subtraction.
* @param[out] c 存放结果的引用 * @param[out] c The pointer to the variable storing the result.
* @return 如果发生溢出则为true否则为false。 * @return Returns true if an overflow occurs, false otherwise.
*/ */
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
@ -253,7 +273,7 @@ namespace yycc::num::safe_op {
static_assert(std::false_type::value, "not supported integral type."); static_assert(std::false_type::value, "not supported integral type.");
} }
} }
// 同理,手算溢出值 // Similarly, manually calculate wrapping value.
if (overflow) *c = ub_signed_int_sub<T>(a, b); if (overflow) *c = ub_signed_int_sub<T>(a, b);
return overflow; return overflow;
#endif #endif
@ -261,11 +281,11 @@ namespace yycc::num::safe_op {
/** /**
* @private * @private
* @brief 基于硬件指令的带溢出检测的乘法 * @brief Multiplication with overflow detection based on hardware instructions.
* @param[in] a 乘法的左操作数 * @param[in] a The left operand of the multiplication.
* @param[in] b 乘法的右操作数 * @param[in] b The right operand of the multiplication.
* @param[out] c 存放结果的引用 * @param[out] c The reference to the variable storing the result.
* @return 如果发生溢出则为true否则为false。 * @return Returns true if an overflow occurs, false otherwise.
*/ */
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
@ -302,13 +322,14 @@ namespace yycc::num::safe_op {
static_assert(std::false_type::value, "not supported integral type."); static_assert(std::false_type::value, "not supported integral type.");
} }
} }
// 同理,手算溢出值 // Similarly, manually calculate wrapping value.
if (overflow) *c = ub_signed_int_mul<T>(a, b); if (overflow) *c = ub_signed_int_mul<T>(a, b);
return overflow; return overflow;
#endif #endif
} }
// YYC MARK: 删除定义的宏,防止污染 // YYC MARK:
// Delete the defined macro to prevent polluting the content later.
#if defined(YYCC_HARDWARE_OVERFLOW_WIN32_FNS) #if defined(YYCC_HARDWARE_OVERFLOW_WIN32_FNS)
#undef WIN_EASY_OPER #undef WIN_EASY_OPER
#endif #endif
@ -318,36 +339,64 @@ namespace yycc::num::safe_op {
#pragma region Wrapping operations #pragma region Wrapping operations
// YYC MARK: // YYC MARK:
// 使用 wrapping_* 方法在所有模式下进行包裹。 // wrapping_* function family will wrap the result in any scenario.
/**
* @brief Performs a wrapping addition operation on two integers.
* @tparam T Integer type.
* @param[in] a The left operand of the addition.
* @param[in] b The right operand of the addition.
* @return The wrapping result of the addition operation.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T wrapping_add(T a, T b) { T wrapping_add(T a, T b) {
return ub_signed_int_add(a, b); return ub_signed_int_add(a, b);
} }
/**
* @brief Performs a wrapping subtraction operation on two integers.
* @tparam T Integer type.
* @param[in] a The left operand of the subtraction.
* @param[in] b The right operand of the subtraction.
* @return The wrapping result of the subtraction operation.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T wrapping_sub(T a, T b) { T wrapping_sub(T a, T b) {
return ub_signed_int_sub(a, b); return ub_signed_int_sub(a, b);
} }
/**
* @brief Performs a wrapping multiplication operation on two integers.
* @tparam T Integer type.
* @param[in] a The left operand of the multiplication.
* @param[in] b The right operand of the multiplication.
* @return The wrapping result of the multiplication operation.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T wrapping_mul(T a, T b) { T wrapping_mul(T a, T b) {
return ub_signed_int_mul(a, b); return ub_signed_int_mul(a, b);
} }
/**
* @brief Performs a wrapping division operation on two integers.
* @tparam T Integer type.
* @param[in] a The left operand of the division.
* @param[in] b The right operand of the division.
* @return The wrapping result of the division operation.
* @exception std::logic_error If division by zero occurs.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T wrapping_div(T a, T b) { T wrapping_div(T a, T b) {
// 除以0是未定义行为 // Division by zero is undefined behavior.
if (ub_div_zero(a, b)) throw std::logic_error("div with 0"); if (ub_div_zero(a, b)) throw std::logic_error("div with 0");
// 对有符号数来说, INT_MIN / -1 的除法可能溢出 // `INT_MIN / -1` overflow undefined behavior.
// (如 INT_MIN == -INT_MIN - 1 时),导致未定义行为。
if (ub_signed_int_min_div_minus_one(a, b)) if (ub_signed_int_min_div_minus_one(a, b))
// 此时a就是有符号整数最小值直接返回他即可。 // "a" self is the minimum value of signed integer, return it directly.
// 不需要再通过std::numeric_limits去访问最小值。 // There is no need to re-fetch it by std::numeric_limits.
return a; return a;
return a / b; return a / b;
} }
@ -356,8 +405,17 @@ namespace yycc::num::safe_op {
#pragma region Checked operations #pragma region Checked operations
// YYC MARK: 使用 checked_* 方法时发生溢出,则返回 None 值 // YYC MARK:
// If overflow occurs when using checked_* function family,
// these functions will return std::nullopt, otherwise the computed result.
/**
* @brief Performs a checked addition operation on two integers.
* @tparam T Integer type.
* @param[in] a The left operand of the addition.
* @param[in] b The right operand of the addition.
* @return An std::optional containing the result if no overflow occurs, otherwise std::nullopt.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
std::optional<T> checked_add(T a, T b) { std::optional<T> checked_add(T a, T b) {
@ -366,6 +424,13 @@ namespace yycc::num::safe_op {
return result; return result;
} }
/**
* @brief Performs a checked subtraction operation on two integers.
* @tparam T Integer type.
* @param[in] a The left operand of the subtraction.
* @param[in] b The right operand of the subtraction.
* @return An std::optional containing the result if no overflow occurs, otherwise std::nullopt.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
std::optional<T> checked_sub(T a, T b) { std::optional<T> checked_sub(T a, T b) {
@ -374,6 +439,13 @@ namespace yycc::num::safe_op {
return result; return result;
} }
/**
* @brief Performs a checked multiplication operation on two integers.
* @tparam T Integer type.
* @param[in] a The left operand of the multiplication.
* @param[in] b The right operand of the multiplication.
* @return An std::optional containing the result if no overflow occurs, otherwise std::nullopt.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
std::optional<T> checked_mul(T a, T b) { std::optional<T> checked_mul(T a, T b) {
@ -382,12 +454,22 @@ namespace yycc::num::safe_op {
return result; return result;
} }
/**
* @brief Performs a checked division operation on two integers.
* @tparam T Integer type.
* @param[in] a The left operand of the division.
* @param[in] b The right operand of the division.
* @return
* An std::optional containing the result,
* if no undefined behavior (division by zero or overflow) occurs,
* otherwise std::nullopt.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
std::optional<T> checked_div(T a, T b) { std::optional<T> checked_div(T a, T b) {
// 除以0返回空值 // Division by zero is undefined behavior.
if (ub_div_zero(a, b)) return std::nullopt; if (ub_div_zero(a, b)) return std::nullopt;
// 溢出返回空值 // `INT_MIN / -1` overflow undefined behavior.
if (ub_signed_int_min_div_minus_one(a, b)) return std::nullopt; if (ub_signed_int_min_div_minus_one(a, b)) return std::nullopt;
return a / b; return a / b;
} }
@ -396,13 +478,15 @@ namespace yycc::num::safe_op {
#pragma region Overflowing operations #pragma region Overflowing operations
// YYC MARK: 使用 overflowing_* 方法返回该值和一个指示是否存在溢出的布尔值 // YYC MARK:
// overflowing_* function family return a tuple with 2 items as the result.
// First is the wrapping value and second is a boolean indicates whether overflow occurs.
/** /**
* @brief Overflow系列运算函数返回的结果 * @brief The result returned by the overflow function family.
* @details * @details
* 第一项为运算结果。 * The first item is the operation result.
* 第二项为指示是否发生了溢出true为溢出否则为false * The second item indicates whether an overflow has occurred. true means overflow, otherwise false.
*/ */
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
@ -435,11 +519,11 @@ namespace yycc::num::safe_op {
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
OverflowingPair<T> overflowing_div(T a, T b) { OverflowingPair<T> overflowing_div(T a, T b) {
// 除以0需要panic // Division by zero is undefined behavior.
if (ub_div_zero(a, b)) throw std::logic_error("div with 0"); if (ub_div_zero(a, b)) throw std::logic_error("div with 0");
// 溢出只可能发生在有符号最小值除以-1上 // `INT_MIN / -1` overflow undefined behavior.
if (ub_signed_int_min_div_minus_one(a, b)) { if (ub_signed_int_min_div_minus_one(a, b)) {
// a就是最小值无需再次获取 // "a" self is minimum value, no need to get it again.
return std::make_pair(a, true); return std::make_pair(a, true);
} else { } else {
return std::make_pair(a / b, false); return std::make_pair(a / b, false);
@ -450,25 +534,41 @@ namespace yycc::num::safe_op {
#pragma region Saturating operations #pragma region Saturating operations
// YYC MARK: 使用 saturating_* 方法使值达到最小值或最大值 /*
// 无符号的溢出判定较为简单,有符号的则稍显复杂,具体遵守如下规则 YYC MARK:
// 认识到对于有符号abs(MIN) = abs(MAX) + 1 The direction of saturation is pretty simple for unsigned integer.
// * 加法: However, it is slightly complex for signed integer.
// - 区间运算[a, b] + [c, d] = [a+c, b+d] In detail, it follow following rules:
// - 正+正 -> [0, MAX] + [0, MAX] -> [0, 2 * MAX],可能上溢 -> 饱和到max
// - 负+负 -> [MIN, -1] + [MIN, -1] -> [2 * MIN, -2],可能下溢 -> 饱和到min
// - 正+负 -> [0, MAX] + [MIN, -1] -> [MIN, MAX - 1],不可能溢出
// * 减法:
// - 区间运算[a, b] - [c, d] = [a-d, b-c]
// - 正-负 -> [0, MAX] - [MIN, -1] -> [1, MAX - MIN],可能上溢 -> 饱和到max
// - 负-正 -> [MIN, -1] - [0, MAX] -> [MIN - MAX, -1],可能下溢 -> 饱和到min
// - 正-正 -> [0, MAX] - [0, MAX] -> [-MAX, MAX],不可能溢出
// - 负-负 -> [MIN, -1] - [MIN, -1] -> [MIN + 1, -(MIN + 1)],不可能溢出
// * 乘法:
// - 正*正 -> 可能上溢 -> 饱和到max
// - 正*负 -> 可能下溢 -> 饱和到min
// - 负*负 -> 可能上溢 -> 饱和到max
Acknowledge the truth for signed integer: abs(MIN) = abs(MAX) + 1
- ADD operation:
- Range operation rule: [a, b] + [c, d] = [a+c, b+d]
- Pos+Pos -> [0, MAX] + [0, MAX] -> [0, 2 * MAX]. May overflow -> Saturating to MAX.
- Neg+Neg -> [MIN, -1] + [MIN, -1] -> [2 * MIN, -2]. May underflow -> Saturating to MIN.
- Pos+Neg -> [0, MAX] + [MIN, -1] -> [MIN, MAX - 1]. Impossible overflow or underflow.
- SUB Operation:
- Range operation rule: [a, b] - [c, d] = [a-d, b-c]
- Pos-Neg -> [0, MAX] - [MIN, -1] -> [1, MAX - MIN]. Maybe overflow -> Saturating to MAX.
- Neg-Pos -> [MIN, -1] - [0, MAX] -> [MIN - MAX, -1]. Maybe underflow -> Saturating to MIN.
- Pos-Pos -> [0, MAX] - [0, MAX] -> [-MAX, MAX]. Impossible overflow or underflow.
- Neg-Neg -> [MIN, -1] - [MIN, -1] -> [MIN + 1, -(MIN + 1)]. Impossible overflow or underflow.
- MUL Operation:
- Pos*Pos -> Maybe overflow -> Saturating to MAX.
- Pos*Neg -> Maybe underflow -> Saturating to MIN.
- Neg*Neg -> Maybe overflow -> Saturating to MAX.
*/
/**
* @brief Performs a saturating addition operation on two integers.
* @details
* If an overflow occurs during the addition,
* the result will be saturated to the maximum/minimum value,
* according to the the direction of overflow (overflow or underflow).
* @tparam T Integer type.
* @param[in] a The left operand of the addition.
* @param[in] b The right operand of the addition.
* @return The result of the saturating addition operation.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T saturating_add(T a, T b) { T saturating_add(T a, T b) {
@ -478,13 +578,25 @@ namespace yycc::num::safe_op {
if constexpr (std::is_unsigned_v<T>) { if constexpr (std::is_unsigned_v<T>) {
return Limits::max(); return Limits::max();
} else { } else {
// 溢出只可能发生在同号情况,因此只判定其中一个操作数的符号即可。 // Overflow only occurs when 2 operand have same sign.
// So we simply check the sign of one of them.
return (a > 0) ? Limits::max() : Limits::min(); return (a > 0) ? Limits::max() : Limits::min();
} }
} }
return result; return result;
} }
/**
* @brief Performs a saturating subtraction operation on two integers.
* @details
* If an overflow occurs during the subtraction,
* the result will be saturated to the maximum/minimum value,
* according to the the direction of overflow (overflow or underflow).
* @tparam T Integer type.
* @param[in] a The left operand of the subtraction.
* @param[in] b The right operand of the subtraction.
* @return The result of the saturating subtraction operation.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T saturating_sub(T a, T b) { T saturating_sub(T a, T b) {
@ -494,14 +606,26 @@ namespace yycc::num::safe_op {
if constexpr (std::is_unsigned_v<T>) { if constexpr (std::is_unsigned_v<T>) {
return 0; return 0;
} else { } else {
// 溢出只可能发生在异号情况,因此只判定两个操作数的大小即可。 // Overflow only occurs when 2 operand have different sign.
// a < b则a为负否则a为正 // So we simply compare these 2 operand.
// If a < b, then "a" is negative, otherwise positive.
return (a < b) ? Limits::min() : Limits::max(); return (a < b) ? Limits::min() : Limits::max();
} }
} }
return result; return result;
} }
/**
* @brief Performs a saturating multiplication operation on two integers.
* @details
* If an overflow occurs during the multiplication,
* the result will be saturated to the maximum/minimum value,
* according to the the direction of overflow (overflow or underflow).
* @tparam T Integer type.
* @param[in] a The left operand of the multiplication.
* @param[in] b The right operand of the multiplication.
* @return The result of the saturating multiplication operation.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T saturating_mul(T a, T b) { T saturating_mul(T a, T b) {
@ -511,19 +635,32 @@ namespace yycc::num::safe_op {
if constexpr (std::is_unsigned_v<T>) { if constexpr (std::is_unsigned_v<T>) {
return Limits::max(); return Limits::max();
} else { } else {
// 做异号判定如果XOR为true则为异号 // Check whether 2 operands have different sign.
// If the result of XOR is true, these 2 operands should have different sign.
return ((a ^ b) < 0) ? Limits::min() : Limits::max(); return ((a ^ b) < 0) ? Limits::min() : Limits::max();
} }
} }
return result; return result;
} }
/**
* @brief Performs a saturating division operation on two integers.
* @details
* If an overflow occurs during the division,
* the result will be saturated to the maximum/minimum value,
* according to the the direction of overflow (overflow or underflow).
* @tparam T Integer type.
* @param[in] a The left operand of the division.
* @param[in] b The right operand of the division.
* @return The result of the saturating division operation.
* @exception std::logic_error If division by zero occurs.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T saturating_div(T a, T b) { T saturating_div(T a, T b) {
// 除以0需要panic // Division by zero is undefined behavior.
if (ub_div_zero(a, b)) throw std::logic_error("div with zero"); if (ub_div_zero(a, b)) throw std::logic_error("div with zero");
// 如果发生最小值除以0的情况那么溢出到最大值 // `INT_MIN / -1` overflow undefined behavior.
if (ub_signed_int_min_div_minus_one(a, b)) { if (ub_signed_int_min_div_minus_one(a, b)) {
return std::numeric_limits<T>::max(); return std::numeric_limits<T>::max();
} else { } else {
@ -536,27 +673,57 @@ namespace yycc::num::safe_op {
#pragma region Ordinary operations #pragma region Ordinary operations
// YYC MARK: // YYC MARK:
// Rust的方式进行四则运算,不存在未定义行为。 // Rust-way add, sub, mul and div operators.
// 默认的四则运算与wrapping_*系列函数行为一致。 // There is no any undefined behavior which may occurs in these functions.
// These normal operator is just an alias to wrapping_* function family.
/**
* @brief Performs an addition operation on two integers.
* @tparam T Integer type.
* @param[in] a The left operand of the addition.
* @param[in] b The right operand of the addition.
* @return The result of the addition operation.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T add(T a, T b) { T add(T a, T b) {
return wrapping_add(a, b); return wrapping_add(a, b);
} }
/**
* @brief Performs a subtraction operation on two integers.
* @tparam T Integer type
* @param[in] a The left operand of the subtraction.
* @param[in] b The right operand of the subtraction.
* @return The result of the subtraction operation.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T sub(T a, T b) { T sub(T a, T b) {
return wrapping_sub(a, b); return wrapping_sub(a, b);
} }
/**
* @brief Performs a multiplication operation on two integers.
* @tparam T Integer type.
* @param[in] a The left operand of the multiplication.
* @param[in] b The right operand of the multiplication.
* @return The result of the multiplication operation.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T mul(T a, T b) { T mul(T a, T b) {
return wrapping_mul(a, b); return wrapping_mul(a, b);
} }
/**
* @brief Performs a division operation on two integers.
* @tparam T Integer type.
* @param[in] a The left operand of the division.
* @param[in] b The right operand of the division.
* @return The result of the division operation.
* @exception std::logic_error If division by zero or value overflow occurs.
*/
template<typename T> template<typename T>
requires std::integral<T> requires std::integral<T>
T div(T a, T b) { T div(T a, T b) {
@ -567,6 +734,7 @@ namespace yycc::num::safe_op {
} // namespace yycc::num::safe_op } // namespace yycc::num::safe_op
// YYC MARK: 删除宏定义,防止污染后面的内容 // YYC MARK:
// Delete the macro definition to prevent polluting the content later.
#undef YYCC_HARDWARE_OVERFLOW_GCC_FNS #undef YYCC_HARDWARE_OVERFLOW_GCC_FNS
#undef YYCC_HARDWARE_OVERFLOW_WIN32_FNS #undef YYCC_HARDWARE_OVERFLOW_WIN32_FNS

View File

@ -1,5 +1,4 @@
#include "op.hpp" #include "op.hpp"
#include <algorithm> #include <algorithm>
namespace yycc::string::op { namespace yycc::string::op {

View File

@ -2,12 +2,18 @@
#include <yycc.hpp> #include <yycc.hpp>
#include <yycc/num/op.hpp> #include <yycc/num/op.hpp>
#include <yycc/rust/prelude.hpp>
#define OP ::yycc::num::op #define OP ::yycc::num::op
namespace yycctest::num::op { namespace yycctest::num::op {
TEST(NumOp, DivCeil) { TEST(NumOp, DivCeil) {
EXPECT_EQ(OP::div_ceil<uint32_t>(7, 4), UINT32_C(2)); // Normal case
EXPECT_EQ(OP::div_ceil<u32>(8, 4), UINT32_C(2));
EXPECT_EQ(OP::div_ceil<u32>(7, 4), UINT32_C(2));
// Limit case
EXPECT_EQ(OP::div_ceil<u8>(255, 2), UINT8_C(128));
} }
} }