是否可以超载 std::chrono::duration_cast?
is it possible to overload std::chrono::duration_cast?
我有一个设备可以将时间报告为时钟滴答中的整数秒和小数秒。对于这个特定的设备,时钟运行 256MHz。我已经定义了我在持续时间和 time_points:
中使用的自定义分辨率
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
int
代表 system_clock::duration
。
根据看来,这似乎是合法和道德的,但味道不太好。这个限制太容易产生bug了。
如果我有一种方法可以定义转换为我的非常高分辨率类型的实现,以防止溢出,那将是理想的。知道使用自定义辅助函数而不是强制转换只是自找麻烦,因为有人会忘记一些时间,而错误可能会在生产中被忽视。
我同意你点击溢出。但这不是因为 system_clock::rep
是 int
。实际上,您的 system_clock::rep
是 int64_t
,是的,它确实会溢出。
你的 system_clock::period
是 nano
。而将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);
Here 是这些函数的示例实现。只需复制一个,为 your_cast
选择一个合适的名称,然后以最适合您的方式实施它。
我有一个设备可以将时间报告为时钟滴答中的整数秒和小数秒。对于这个特定的设备,时钟运行 256MHz。我已经定义了我在持续时间和 time_points:
中使用的自定义分辨率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
int
代表 system_clock::duration
。
根据
如果我有一种方法可以定义转换为我的非常高分辨率类型的实现,以防止溢出,那将是理想的。知道使用自定义辅助函数而不是强制转换只是自找麻烦,因为有人会忘记一些时间,而错误可能会在生产中被忽视。
我同意你点击溢出。但这不是因为 system_clock::rep
是 int
。实际上,您的 system_clock::rep
是 int64_t
,是的,它确实会溢出。
你的 system_clock::period
是 nano
。而将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);
Here 是这些函数的示例实现。只需复制一个,为 your_cast
选择一个合适的名称,然后以最适合您的方式实施它。