文章目录
- 0. 概述
- 1. duration
- 1.1 分析
- std::chrono::duration_cast()
- 1.2 使用案例
- std::chrono::duration::count()
- 1.3 部分源码
- 2. time_point
- 2.1 分析
- std::chrono::time_point_cast()
- 2.2 使用举例
- std::chrono::time_point::time_since_epoch()
- 2.3 部分源码
0. 概述
本篇文章介绍 chrono 模板库,是参考 cplusplus.com 官网做的一篇详解。
chrono 库是可以实现各种时间格式的定义和转化,整体分成三部分。
- duration 类:
用作 测量时间跨度,比如:1分钟,2小时,或者10毫秒。
使用 duration 类模板的对象来表示时,可以将计数表示和周期精度耦合在一起(例如:10表示计数,毫秒表示周期精度) - time_point 类:
用作 表示某一个时间点,比如:日出的时间,某人的纪念日
使用 time_point 类模板的对象来表示时,需要指定 clock(三种,后面有讲) 和相对于纪元的持续时间来表示这一点
namespace chrono
{
// duration 类
template <class _Rep, class _Period> class duration;
// time_point 类
template <class _Clock, class _Duration = typename _Clock::duration> class time_point;
// ...
}
- clock 结构体
就像名称所示,时钟,可以将时间点与实际物理时间联系起来。
主要介绍三个时钟,它们提供了将当前时间表示为时间点的方法- 系统时钟 system_clock
- 稳定时钟 steady_clock
- 高精度时钟 high_resolution_clock
namespace chrono
{
struct system_clock { // wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTime
using rep = long long;
using period = ratio<1, 10'000'000>; // 100 nanoseconds
using duration = _CHRONO duration<rep, period>;
using time_point = _CHRONO time_point<system_clock>;
static constexpr bool is_steady = false;
// 获取当前时间
static time_point now();
// time_point 类型转化成 time_t
static __time64_t to_time_t(const time_point& _Time);
// time_t 类型转化成 time_point
static time_point from_time_t(__time64_t _Tm);
};
struct steady_clock { // wraps QueryPerformanceCounter
using rep = long long;
using period = nano;
using duration = nanoseconds;
using time_point = _CHRONO time_point<steady_clock>;
static constexpr bool is_steady = true;
static time_point now() noexcept { // get current time
const long long _Freq = _Query_perf_frequency(); // doesn't change after system boot
const long long _Ctr = _Query_perf_counter();
static_assert(period::num == 1, "This assumes period::num == 1.");
// 10 MHz is a very common QPC frequency on modern PCs. Optimizing for
// this specific frequency can double the performance of this function by
// avoiding the expensive frequency conversion path.
constexpr long long _TenMHz = 10'000'000;
if (_Freq == _TenMHz) {
static_assert(period::den % _TenMHz == 0, "It should never fail.");
constexpr long long _Multiplier = period::den / _TenMHz;
return time_point(duration(_Ctr * _Multiplier));
} else {
// Instead of just having "(_Ctr * period::den) / _Freq",
// the algorithm below prevents overflow when _Ctr is sufficiently large.
// It assumes that _Freq * period::den does not overflow, which is currently true for nano period.
// It is not realistic for _Ctr to accumulate to large values from zero with this assumption,
// but the initial value of _Ctr could be large.
const long long _Whole = (_Ctr / _Freq) * period::den;
const long long _Part = (_Ctr % _Freq) * period::den / _Freq;
return time_point(duration(_Whole + _Part));
}
}
};
_EXPORT_STD using high_resolution_clock = steady_clock;
} // namespace chrono
1. duration
1.1 分析
template <class _Rep, class _Period> class duration;
_Rep
表示一种数值类型,用来表示 _Period 的类型,比如:int, float, double…_Period
是ratio
类型,表示 用秒表示的时间单位 比如:second, milisecond…
常用的duration<Rep,Period>已经定义好了,在 std::chrono下:
// std::chrono
using nanoseconds = duration<long long, nano>;
using microseconds = duration<long long, micro>;
using milliseconds = duration<long long, milli>;
using seconds = duration<long long>;
using minutes = duration<int, ratio<60>>;
using hours = duration<int, ratio<3600>>;
using days = duration<int, ratio_multiply<ratio<24>, hours::period>>;
using weeks = duration<int, ratio_multiply<ratio<7>, days::period>>;
using years = duration<int, ratio_multiply<ratio<146097, 400>, days::period>>;
using months = duration<int, ratio_divide<years::period, ratio<12>>>;
std::chrono::duration_cast()
由于 duration 的种类繁多,chrono 库提供了duration_cast 类型转换函数模板,在模板参数中填写需要转成的类型如:<std::chrono::seconds / months / days…>,就可以得到想要的结果,std::chrono 下:
// std::chrono
template <class _To, class _Rep, class _Period, enable_if_t<_Is_duration_v<_To>, int> /* = 0 */>
_To duration_cast(const duration<_Rep, _Period>& _Dur)
1.2 使用案例
🌰典型的用法是表示一段时间:
// duration constructor
#include <iostream>
#include <chrono>
int main ()
{
typedef std::chrono::duration<int> seconds_type;
typedef std::chrono::duration<int,std::milli> milliseconds_type;
typedef std::chrono::duration<int,std::ratio<60*60>> hours_type;
hours_type h_oneday (24); // 24h
seconds_type s_oneday (60*60*24); // 86400s
milliseconds_type ms_oneday (s_oneday); // 86400000ms
seconds_type s_onehour (60*60); // 3600s
//hours_type h_onehour (s_onehour); // NOT VALID (type truncates), use:
hours_type h_onehour (std::chrono::duration_cast<hours_type>(s_onehour));
milliseconds_type ms_onehour (s_onehour); // 3600000ms (ok, no type truncation)
std::cout << ms_onehour.count() << "ms in 1h" << std::endl;
return 0;
}
--------------------------
输出结果:
3600000ms in 1h
- 需要注意的就是,大单位的 duration 可以作为参数构造小单位,反过来就不行了,需要使用 duration_cast 进行强转。
std::chrono::duration::count()
🌰 duration 还有一个成员函数 count()
返回 Rep 类型的 Period 数量:
// duration::count
#include <iostream>
#include <chrono> // std::chrono::seconds, std::chrono::milliseconds,std::chrono::duration_cast
using namespace std::chrono;
int main ()
{
milliseconds foo (1000); // 1 second,这里的 milliseconds 是库里的,定义见 1.1
foo*=60;
std::cout << "duration (in periods): ";
std::cout << foo.count() << " milliseconds.\n";
std::cout << "duration (in seconds): ";
std::cout << foo.count() * milliseconds::period::num / milliseconds::period::den;
std::cout << " seconds.\n";
return 0;
}
-------------------------------
输出结果:
duration (in periods): 60000 milliseconds.
duration (in seconds): 60 seconds.
1.3 部分源码
template <class _Rep, class _Period>
class duration { // represents a time duration
public:
using rep = _Rep;
using period = typename _Period::type;
template <class _Rep2, enable_if_t<is_convertible_v<const _Rep2&, _Rep> && (treat_as_floating_point_v<_Rep> !treat_as_floating_point_v<_Rep2>), int> = 0>
duration(const _Rep2& _Val) : _MyRep(static_cast<_Rep>(_Val)) {}
template <class _Rep2, class _Period2, enable_if_t<treat_as_floating_point_v<_Rep> || (_Ratio_divide_sfinae<_Period2, _Period>::den == 1 && !treat_as_floating_point_v<_Rep2>), int> = 0>
duration(const duration<_Rep2, _Period2>& _Dur) : _MyRep(_CHRONO duration_cast<duration>(_Dur).count()) {}
_Rep count() const
{
return _MyRep;
}
common_type_t<duration> operator+() const;
common_type_t<duration> operator-() const;
duration& operator++();
duration operator++(int);
// -- (略)
duration& operator+=(const duration& _Right);
duration& operator-=(const duration& _Right);
// *= /= %= (略)
// 返回为 0 的 duration 类型
static duration zero();
// 返回相应 _Rep 类型的最小值 的 duration
static duration(min)();
// 返回相应 _Rep 类型的最大值 的 duration
static duration(max)();
private:
_Rep _MyRep; // the stored rep
};
2. time_point
2.1 分析
template <class _Clock, class _Duration = typename _Clock::duration> class time_point;
类型 std::chrono::time_point 表示一个具体时间,鉴于我们使用时间的情景不同,这个 time point 具体到什么程度,由选用的单位决定。模板参数如下:
- 时钟类型(见 clock 部分)
- duration 的时间类型,决定了 time_point 的时间类型
std::chrono::time_point_cast()
由于各种 time_point 表示方式不同,chrono 也提供了相应的转换函数 time_point_cast()。
template <class _To, class _Clock, class _Duration, enable_if_t<_Is_duration_v<_To>, int> = 0>
time_point<_Clock, _To> time_point_cast(const time_point<_Clock, _Duration>& _Time);
{
// change the duration type of a time_point; truncate
return time_point<_Clock, _To>(_CHRONO duration_cast<_To>(_Time.time_since_epoch()));
}
2.2 使用举例
以 system_clock 这个时钟举例,里面涉及的起始时间,是计算机元年 1970年1月1日8点
(后文简称计元)。
🌰代码如下:
// time_point constructors
#include <iostream>
#include <chrono>
using namespace std::chrono;
int main ()
{
// 用时钟 system_clock::time_point 直接声明,其值默认为:纪元时间
system_clock::time_point tp_epoch;
std::time_t tt = system_clock::to_time_t(tp_epoch); // 转成 time_t 后可查
// 用 time_point 类(tp_seconds 的时间周期是 second)
time_point<system_clock, duration<int>> tp_seconds(duration<int>(1)); // duration 不传时间精度,就默认是 second
std::cout << tp_seconds.time_since_epoch().count() << std::endl;
// 用时钟 system_clock::time_point (time_point) 构造对象,时间周期默认是 1/10000000 s
system_clock::time_point tp(tp_seconds);
std::cout << "1 second since system_clock epoch = ";
std::cout << tp.time_since_epoch().count();
std::cout << " system_clock periods." << std::endl;
// 打印
std::cout << "time_point tp_epoch is: " << ctime(&tt);
tt = system_clock::to_time_t(tp);
std::cout << "time_point tp is: " << ctime(&tt);
return 0;
}
------------------------------
输出结果:
1
1 second since system_clock epoch = 10000000 system_clock periods.
time_point tp_epoch is : Thu Jan 1 08 : 00 : 00 1970
time_point tp is : Thu Jan 1 08 : 00 : 01 1970
std::chrono::time_point::time_since_epoch()
time_point 有一个函数 time_since_epoch()
用来获得1970年1月1日到 time_point 时间经过的duration。同样,如果 time_point 以天为单位,函数返回的 duration 就以天为单位。
#include <iostream>
#include <chrono>
using namespace std::chrono;
int main()
{
typedef duration<int, std::ratio<60 * 60 * 24>> days_type; // ratio以second为单位,这里的duration时间跨度为1day
time_point<system_clock, days_type> today = time_point_cast<days_type>(system_clock::now());
std::cout << today.time_since_epoch().count() << " days since epoch" << std::endl;
return 0;
}
--------------------
输出结果:
19679 days since epoch
2.3 部分源码
template <class _Clock, class _Duration = typename _Clock::duration>
class time_point { // represents a point in time
public:
using clock = _Clock;
using duration = _Duration;
using rep = typename _Duration::rep;
using period = typename _Duration::period;
time_point(const _Duration& _Other): _MyDur(_Other) {}
template <class _Duration2, enable_if_t<is_convertible_v<_Duration2, _Duration>, int> = 0>
constexpr time_point(const time_point<_Clock, _Duration2>& _Tp): _MyDur(_Tp.time_since_epoch()) {}
_Duration time_since_epoch() const
{
return _MyDur;
}
time_point& operator++() noexcept(is_arithmetic_v<rep>);
time_point operator++(int) noexcept(is_arithmetic_v<rep>);
time_point& operator--() noexcept(is_arithmetic_v<rep>);
time_point operator--(int) noexcept(is_arithmetic_v<rep>);
time_point& operator+=(const _Duration& _Dur);
time_point& operator-=(const _Duration& _Dur);
static time_point(min)();
static time_point(max)();
private:
_Duration _MyDur{duration::zero()}; // duration since the epoch
};