是否可以重载std::chrono::duration_cast?



我有一个设备,它以整数秒和小数秒的时钟节拍报告时间。对于这个特殊的设备,时钟的工作频率为256MHz。我在duration和time_points中定义了一个自定义分辨率:

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

(除非是相关的,否则我宁愿不讨论int和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溢出system_clock::durationintRep。

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

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

我同意你碰到溢出。但这并不是因为system_clock::repint。事实上,你的system_clock::repint64_t,是的,它在这个操作中溢出。

你的system_clock::periodnano。将nano转换为std::ratio<1, 256'000'000>的转换因子为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);

下面是这些函数的示例实现。只需复制一个,为your_cast选择一个合适的名称,并实现它,但最适合你。

最新更新