【问题标题】:is it possible to overload std::chrono::duration_cast?是否可以重载 std::chrono::duration_cast?
【发布时间】:2021-08-06 21:52:51
【问题描述】:

我有一个设备,它以整数秒和小数秒为单位报告时间。对于这个特定的设备,时钟运行在 256MHz。我已经定义了我在持续时间和时间点中使用的自定义分辨率:

using Res = std::chrono::duration<uint64_t, std::ratio<1, 256'000'000>>;

(除非它是相关的,我宁愿不争论 uint 与 int)。

此分辨率足以表示自 1970 年以来的时间,溢出时间约为 2763。

给定,

#include <iostream>
#include <chrono>

int main() {
    using Res = std::chrono::duration<uint64_t, std::ratio<1, 256'000'000>>;
    using namespace std::chrono;
    using TimePoint = time_point<system_clock, Res>;

    auto s_now = system_clock::now();

    auto simple = time_point_cast<Res>(s_now);

    auto s_sec = duration_cast<seconds>(s_now.time_since_epoch());
    auto hard = TimePoint(Res(s_sec));
    hard += duration_cast<Res>(s_now.time_since_epoch() - s_sec);

    std::cout << "s_now  " << s_now.time_since_epoch().count() << "\t" << s_sec.count() << "\n"
              << "simple " << simple.time_since_epoch().count() << "\t" << duration_cast<seconds>(simple.time_since_epoch()).count() << "\n"
              << "hard   " << hard.time_since_epoch().count()  << "\t" << duration_cast<seconds>(hard.time_since_epoch()).count() << "\n"
;
    return 0;
}

那么simple 不等于hard。输出可能是这样的:

s_now  1628286679505051812  1628286679
simple 121693484773940438   475365174
hard   416841389953293263   1628286679

发生的情况是 time_point_cast overflows int 代表 system_clock::duration

基于https://stackoverflow.com/a/51226923/9220132,这似乎是合法和道德的,但它的味道并不好。这种限制太容易产生错误来源。

如果有一种方法可以让我定义转换为我的高分辨率类型的实现,以便防止溢出,那将是理想的。知道使用自定义辅助函数而不是强制转换只是自找麻烦,因为有人会忘记一些时间,并且该错误可能会在生产中被忽视。

【问题讨论】:

  • 我不确定我是否理解你的问题,但是在std 中添加一些东西是非常非法的,也就是 UB,除非明确允许 stackoverflow.com/questions/37541022/…
  • 明白了。但是,在某些情况下,为用户定义的类型提供模板特化是合法的。我希望答案就在那个方向。
  • 是的,std::hash 允许,但必须允许。可以尝试在 cppreference.com 上找到它。否则必须阅读标准,即免费草案,您可以通过该网站的常见问题解答找到。我会期待但它的就寝时间对我来说。
  • @Superlokkus 只要涉及到用户定义的类型,在std 中进行特化是完全可以的(std::hash&lt;user_defined_type&gt; 是一个常用的例子)——我不确定这是否适用于虽然这种情况,但它可能。
  • 但是,std::chrono::duration&lt;uint64_t, std::ratio&lt;1, 256'000'000&gt;&gt; 不是用户定义的类型,与(比如说)std::vector&lt;int&gt; 一样。

标签: c++ c++11 chrono


【解决方案1】:

我同意你点击溢出。但这不是因为system_clock::repint。确实,您的 system_clock::repint64_t,是的,它确实溢出了这个操作。

您的system_clock::periodnano。而将nano 转换为std::ratio&lt;1, 256'000'000&gt; 的转换因子是32/125。并且 32 * 1628286679505051812 溢出公共 rep : uint64_t

有几种方法可以避免溢出并获得正确答案。你展示其中之一。另一种方法是使用 128 位算法,这在您的平台上可用:

using D = duration<__int128_t>;
auto s_now = system_clock::now() + D{0};  // s_now::rep is now 128 bits

这会将输出更改为:

s_now  1628286679505051812  1628286679
simple 416841389953293263   1628286679
hard   416841389953293263   1628286679

最后,回答你的问题:不,不要覆盖duration_cast。而是创建your_cast 并用它做任何你想做的事情。 C++17 已经通过引入 duration_cast/time_point_cast 的三个新“风格”来实现这一点,它们只是改变了默认的舍入模式:

template<class ToDuration, class Rep, class Period>
  constexpr ToDuration floor(const duration<Rep, Period>& d);
template<class ToDuration, class Rep, class Period>
  constexpr ToDuration ceil(const duration<Rep, Period>& d);
template<class ToDuration, class Rep, class Period>
  constexpr ToDuration round(const duration<Rep, Period>& d);

template<class ToDuration, class Clock, class Duration>
  constexpr time_point<Clock, ToDuration> floor(const time_point<Clock, Duration>& tp);
template<class ToDuration, class Clock, class Duration>
  constexpr time_point<Clock, ToDuration> ceil(const time_point<Clock, Duration>& tp);
template<class ToDuration, class Clock, class Duration>
  constexpr time_point<Clock, ToDuration> round(const time_point<Clock, Duration>& tp);

Here 是这些函数的示例实现。只需复制一个,为your_cast 选择一个合适的名称,然后按照最适合您的方式实现它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-08
    • 2018-12-15
    相关资源
    最近更新 更多