gpt4 book ai didi

c++ - 如何将任意大的持续时间转换为纳秒精度的持续时间而不会溢出?

转载 作者:行者123 更新时间:2023-12-01 14:47:13 24 4
gpt4 key购买 nike

假设我有自己的时钟,与 system_clock 的纪元相同:

using namespace std;

struct myclock
{
using rep = int64_t;
using period = nano;
using duration = chrono::duration<rep, period>;
using time_point = chrono::time_point<myclock>;

static constexpr bool is_steady = chrono::system_clock::is_steady;

static time_point now() noexcept;
};
我需要转换任何 tp (类型 system_clock::time_point)到 myclock::time_point像这样:
  • (如果需要)截断 tp丢弃“过去纳秒”的精度
  • 如果 tp.time_since_epoch()myclock::time_point s 有效范围 -- 返回 myclock::time_point() + tp.time_since_epoch()
  • 否则,抛出异常

  • 但是,不知道 system_clock s periodrep我遇到了 integer overflows :
        constexpr auto x = chrono::duration<int64_t, milli>(numeric_limits<int64_t>::max());
    constexpr auto y = chrono::duration<int8_t, nano>(1);

    constexpr auto z1 = x / nanoseconds(1) * nanoseconds(1); // naive way to discard post-nanosecond part
    constexpr auto z2 = y / nanoseconds(1) * nanoseconds(1);
    static_assert( x > y );
    如何以这种方式编写此逻辑,使其对任何 tp 都能可靠地工作和任意 system_clock::period/rep ?
    附言我检查了 MSVC 对 duration_cast 的实现/ time_point_cast ,但似乎它们有相同的问题(或需要相同的时钟类型)。

    最佳答案

    我强烈建议你把这个问题分成两部分:

  • 转换任何精度 time_point<myclock, D>往/返 time_point<system_clock, D> , 同时保持精度 D .
  • 编写一个免费函数(比如 checked_convert )以将一种精度转换为另一种精度(在相同的 time_point 时钟系列中)并抛出溢出。
  • time_point的第一次转换s 之间的时钟:
    添加静态成员函数 to_sysfrom_sysmyclock像这样:
    struct myclock
    {
    using rep = std::int64_t;
    using period = std::nano;
    using duration = std::chrono::duration<rep, period>;
    using time_point = std::chrono::time_point<myclock>;

    static constexpr bool is_steady = std::chrono::system_clock::is_steady;

    static time_point now() noexcept;

    template<typename Duration>
    static
    std::chrono::time_point<std::chrono::system_clock, Duration>
    to_sys(const std::chrono::time_point<myclock, Duration>& tp)
    {
    using Td = std::chrono::time_point<std::chrono::system_clock, Duration>;
    return Td{tp.time_since_epoch()};
    }

    template<typename Duration>
    static
    std::chrono::time_point<myclock, Duration>
    from_sys(const std::chrono::time_point<std::chrono::system_clock, Duration>& tp)
    {
    using Td = std::chrono::time_point<myclock, Duration>;
    return Td{tp.time_since_epoch()};
    }
    };
    现在您可以转换为 system_clock像这样:
    myclock::time_point tp;
    auto tp_sys = myclock::to_sys(tp);
    或者用 myclock::from_sys 走另一条路.
    这样做很酷的一点是,当你在 future 某个时间迁移到 C++20 时,你可以将语法更改为:
    auto tp_sys = std::chrono::clock_cast<std::chrono::system_clock>(tp);
    tp = std::chrono::clock_cast<myclock>(tp_sys);
    更酷的是,无需进一步更改您的代码,您还可以 clock_cast往/返:
  • utc_clock
  • tai_clock
  • gps_clock
  • file_clock
  • clock_cast系统将使用您的 to_sys/ from_sys反弹 system_clock往/返任何其他 std -定义的时钟,或什至选择加入 to_sys 的另一个用户定义的时钟/ from_sys系统。
    第二:检查转换
    template <class Duration, class Clock, class DurationSource>
    std::chrono::time_point<Clock, Duration>
    checked_convert(std::chrono::time_point<Clock, DurationSource> tp)
    {
    using namespace std::chrono;
    using Tp = time_point<Clock, Duration>;
    using TpD = time_point<Clock, duration<long double, typename Duration::period>>;
    TpD m = Tp::min();
    TpD M = Tp::max();
    if (tp < m || tp > M)
    throw std::runtime_error("overflow");
    return time_point_cast<Duration>(tp);
    }
    这里的想法是临时转换为基于浮点的 time_points 以进行溢出检查。您也可以使用 128 位整数 rep .任何具有高得离谱的最小值/最大值的东西。做检查。抛出溢出。如果安全,则转换为所需的积分 rep .
    sys_time<microseconds> tp1 = sys_days{1600y/1/1};
    auto tp2 = checked_convert<nanoseconds>(tp1); // throws "overflow"
    std::cout << tp2 << '\n';
    (我已经使用 C++20 语法构建了上面基于 system_clock 的 time_points)
    有人可能会问:为什么不是 checked_convert由标准提供?
    答:因为它并不完美。您的精度 long double可能(或可能不会)小于积分的精度 rep标的 time_point .更好的选择是 128 位整数 rep这肯定会有足够的精度。但是有些平台没有 128 位整数类型。甚至一些平台(低级嵌入式)甚至可能没有浮点类型。所以目前这个问题没有很好的标准解决方案。客户端可以使用多种技术,包括 one in this good answer .
    更新
    这是一个 duration checked_convert 的版本:
    template <class Duration, class Rep, class Period>
    Duration
    checked_convert(std::chrono::duration<Rep, Period> d)
    {
    using namespace std::chrono;
    using D = duration<long double, typename Duration::period>;
    D m = Duration::min();
    D M = Duration::max();
    if (d < m || d > M)
    throw std::runtime_error("overflow");
    return duration_cast<Duration>(d);
    }
    像这样使用时,它会引发异常:
    constexpr auto x = duration<int64_t, milli>(numeric_limits<int64_t>::max());
    constexpr auto y = duration<int8_t, nano>(1);
    auto z = checked_convert<decltype(y)>(x);

    关于c++ - 如何将任意大的持续时间转换为纳秒精度的持续时间而不会溢出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63124296/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com