行行宜行行

热爱风光、热爱新技术、踏出每一步,过好每一天!

基于 duration 的 space类

myspace 技术文档

一、概述

myspace::space 是一个模板类,用于类型安全地表示和操作存储空间,支持不同单位(字节、千字节、兆字节等)的存储量计算与转换。其设计灵感类似于 std::chrono::duration,但专注于空间单位而非时间单位,核心目标是避免不同存储单位直接运算导致的逻辑错误(如将字节与千字节直接相加),同时提供灵活的单位转换机制。

二、核心原理

myspace::space 的核心设计基于模板参数化的单位与数值类型,通过编译期计算实现单位转换和类型安全检查,主要原理包括:

  1. 模板参数定义

    • Rep_:存储数值的类型(如 int64_tsize_tdouble 等),需支持算术运算。
    • Period_:单位周期,基于 my::ratio(类似 std::ratio),表示 1 个单位等于多少字节。例如:ratio<1> 表示字节(1 单位 = 1 字节),ratio<1024> 表示千字节(1 单位 = 1024 字节)。
  2. 单位转换机制
    不同单位的 space 之间通过转换比例实现交互,转换比例由 ratio_divide(计算两个 ratio 的商)确定。例如:从千字节(kiloratio<1024>)转换到字节(ratio<1>)的比例为 1024/1

  3. 类型安全设计
    禁止不同单位的 space 直接运算(需先转换为统一单位),编译期即可检测单位不匹配的错误,避免"字节+千字节"等逻辑错误。

三、核心原理详解

1. 模板参数解析

template<typename Rep_, typename Period_ = ratio<1>> class space

  • Rep_:数值存储类型(如 int64_t 用于整数存储,double 用于浮点存储)。
  • Period_:单位周期(默认 ratio<1>,即字节),需满足 myratio::ratio 接口(包含 num 分子和 den 分母)。

2. 单位转换核心:space_cast

space_cast 用于在不同单位的 space 之间进行转换,参考自duration,其核心逻辑如下:

template<typename Tospace_, typename Rep_, typename Period_>
constexpr Tospace_ space_cast(const myspace::space<Rep_, Period_> &d) {
    using FromPeriod = Period_;       /// 原单位
    using ToPeriod = typename Tospace_::period;  /// 目标单位
    using CF = ratio_divide<FromPeriod, ToPeriod>;  /// 转换比例(原单位/目标单位)

    using CR = typename Tospace_::rep;  /// 目标数值类型
    if constexpr (std::is_floating_point_v<CR>) {
        /// 浮点类型:精确转换(分子/分母)
        return Tospace_(static_cast<CR>(d.count()) * static_cast<CR>(CF::num) / static_cast<CR>(CF::den));
    } else {
        /// 整数类型:直接乘除(注意可能的精度损失)
        return Tospace_(static_cast<CR>(d.count()) * CF::num / CF::den);
    }
}
  • 转换比例 CFratio_divide 计算,例如:从 kilo(1024字节)转换到 ratio<1>(字节)时,CF = 1024/1
  • 针对浮点/整数类型分别处理:浮点类型保留高精度,整数类型直接计算(需确保转换无溢出)。

3. 运算的类型统一机制

当对两个不同单位的 space 进行运算(如加减、比较)时,需先统一为高精度单位(即周期更小的单位,如字节比千字节精度高),核心工具包括:

  • higher_precision_space:使用条件编译确保选择两个单位中精度更高的类型(周期更小的 Period_)。

    template<typename Rep1_, typename Period1_, typename Rep2_, typename Period2_>
    struct higher_precision_space {
        using type = std::conditional_t<
            ratio_less<Period1_, Period2_>::value,  /// 若 Period1_ < Period2_(精度更高)
            space<Rep1_, Period1_>, 
            space<Rep2_, Period2_>
        >;
    };
    
  • unify_space_type:统一两个 space 的类型(高精度单位 + 公共数值类型)。

    template<typename Lhs, typename Rhs>
    struct unify_space_type {
        using Period = typename higher_precision_space<...>::type::period;  /// 高精度单位
        using Rep = std::common_type_t<typename Lhs::rep, typename Rhs::rep>;  /// 公共数值类型
        using type = space<Rep, Period>;  /// 统一后的类型
    };
    

四、实现步骤

步骤1:定义 space 类框架

首先定义 space 类的基本结构,包含模板参数、类型别名及核心成员:

template<typename Rep_, typename Period_ = ratio<1>>
class space {
public:
    using rep = Rep_;       /// 数值类型
    using period = Period_; /// 单位周期
private:
    Rep_ rep_;              /// 存储实际数值
};

步骤2:实现构造函数

支持默认构造、拷贝/移动构造,以及从其他单位/数值类型的转换构造:

constexpr space() : rep_() {}  /// 默认构造(值为0)
constexpr space(const space &) noexcept = default;  /// 拷贝构造
constexpr space(space &&) noexcept = default;       /// 移动构造

/// 从数值直接构造(仅允许显式转换,使用explicit避免隐式类型错误)
template<typename Rep2_>
explicit constexpr space(const Rep2_ &r_) : rep_(r_) {}

/// 从其他单位的 space 转换构造(自动调用 space_cast)
template<typename Rep2_, typename Period2_>
constexpr space(const space<Rep2_, Period2_> &d_) 
    : rep_(space_cast<space>(d_).count()) {}

步骤3:实现基础运算符

包括自增/自减、加减赋值等,基于当前单位的数值直接操作:

/// 一元运算符
constexpr space operator+() const { return *this; }
constexpr space operator-() const { return space(-count()); }

/// 自增/自减
space &operator++() { 
    ++rep_; 
    return *this;
}

space operator++(int) { 
    space temp(*this); 
    ++*this; 
    return temp; 
}
/// (自减类似)

/// 赋值运算(同单位直接操作)
space &operator+=(const space &d_) { 
    rep_ += d_.count(); 
    return *this; 
}

space &operator-=(const space &d_) { 
    rep_ -= d_.count(); 
    return *this; 
}
/// (乘除赋值类似)

步骤4:实现跨单位运算

针对不同单位的 space 运算,先通过 unify_space_type 统一类型,再执行操作:

/// 两个 space 相加(统一类型后计算)
template<typename Rep1_, typename Period1_, typename Rep2_, typename Period2_>
constexpr auto operator+(const space<Rep1_, Period1_> &lhs, const space<Rep2_, Period2_> &rhs) {
    using Common = typename unify_space_type<...>::type;  /// 统一类型
    return Common(space_cast<Common>(lhs).count() + space_cast<Common>(rhs).count());  /// 通过统一类型进行计算
}

/// 比较运算(统一类型后比较)
template<typename Rep1_, typename Period1_, typename Rep2_, typename Period2_>
constexpr bool operator==(const space<Rep1_, Period1_> &lhs, const space<Rep2_, Period2_> &rhs) {
    using Common = typename unify_space_type<...>::type;
    return space_cast<Common>(lhs).count() == space_cast<Common>(rhs).count();
}

步骤5:定义常用单位

基于 ratio 定义常用存储单位,简化使用:

/// 定义单位比例(1024进制)
constexpr int64_t KB = 1024LL;
constexpr int64_t MB = KB * 1024;
/// (GB、TB 类似)

using kilo = ratio<KB, 1>;   /// 1 千字节 = 1024 字节
using mega = ratio<MB, 1>;   /// 1 兆字节 = 1024*1024 字节
/// (giga、tera 类似)

/// 常用 space 类型
using bytes = space<size_t>;               /// 字节(默认单位 ratio<1>)
using kilobytes = space<int64_t, kilo>;    /// 千字节
using megabytes = space<int64_t, mega>;    /// 兆字节
/// (gigabytes、terabytes 类似)

五、使用示例

示例1:基本创建与转换

#include "space.h"

int main() {
    /// 创建不同单位的 space
    myspace::bytes b(1024);                /// 1024 字节
    myspace::kilobytes kb(2);              /// 2 千字节(2*1024字节)

    /// 转换:千字节 -> 字节
    auto b2 = myspace::space_cast<myspace::bytes>(kb);
    std::cout << b2.count() << " bytes\n";  /// 输出:2048 bytes

    /// 转换:字节 -> 千字节(整数类型自动取整)
    auto kb2 = myspace::space_cast<myspace::kilobytes>(b);
    std::cout << kb2.count() << " KB\n";    /// 输出:1 KB
}

示例2:跨单位运算

int main() {
    myspace::bytes b(512);         /// 512 字节
    myspace::kilobytes kb(1);      /// 1024 字节

    /// 相加:自动转换为字节(高精度单位)
    auto sum = b + kb;
    std::cout << sum.count() << " bytes\n";  /// 512 + 1024 = 1536 bytes

    /// 比较:1KB(1024字节)> 512字节
    if (kb > b) {
        std::cout << "1KB > 512B\n";
    }

    /// 乘法:2倍 1KB = 2048字节
    auto mul = kb * 2;
    std::cout << myspace::space_cast<myspace::bytes>(mul).count() << " bytes\n";  // 2048
}

六、注意事项

  1. 整数转换的精度损失:当整数类型 space 转换为低精度单位(如字节→千字节)时,会自动取整(如 1500字节 → 1KB),需根据场景选择浮点类型(如 space<double, kilo>)保留精度。
  2. 溢出风险:整数类型运算可能导致溢出(如大数值×大比例),建议对大存储量使用 int64_t 或浮点类型。
  3. 自定义单位:支持通过 ratio 定义自定义单位(如 ratio<1000> 表示1000字节的"千字节"),只需与 space 模板结合即可使用。
  4. 算术类型交互:支持 space 与算术类型(如 intdouble)直接运算,自动沿用原单位(如 bytes(100) + 50 结果为 bytes(150))。

七、总结

myspace::space 通过模板参数化单位和数值类型,实现了类型安全的存储空间操作,避免了单位混淆导致的错误。其核心优势在于:

  • 编译期单位检查,杜绝"字节+兆字节"等逻辑错误;
  • 灵活的单位转换,支持整数/浮点类型适配;
  • 与算术类型的无缝交互,兼顾易用性与安全性。

适用于需要精确处理存储单位的场景(如文件大小计算、内存管理等)。

最后,内容如有错误或者不对的地方,欢迎各位大佬指出,接收一切质疑和批评,持续加油改进。如有侵权,请联系我删除。

Tags:

发表回复

Your email address will not be published. Required fields are marked *.

*
*

近期评论

您尚未收到任何评论。

conviction

要想看到璀璨的日出时刻,就要有跋山涉水的决心和坚毅