boost:chrono

  • 跨平台、可移植一直是很多C++程序员追求的目标。但由于C++是一种中级语言,很多时候都要和操作系统打交道,因而功能代码常常和平台结合很紧密,难以做到真正的跨平台,再向其他平台移植代码的时候修改操作系统相关的代码通常是必不可少的工作
  • boost库提供了数个操作系统相关的库,部分的屏蔽了操作系统的底层细节,能够提高程序的可移植性。比如说system库,它封装了操作系统底层的错误代码,为上层提供了一个可移植的统一的处理接口; chrono、cpu_time库基于操作系统的API提供高精度的时间功能

概述

chrono完全实现了C++标准里定义的时间处理库,并且额外增加了一些功能。

  • chrono库里的很多时间概念于date_time库类似,但它更侧重于表达”计算机世界“里的时间,所以是一个很重要的基础库,被cpu_time、thread等库使用
  • chrono库需要编译,并且依赖system库,在jamfile里指定lib的语句是:lib boost_chrone: boost_system:boost_chrono;
  • chrono库提供宏BOOST_CHRONO_HEADER_ONLY,可以把chrone库完全头文件化,不需要编译或者链接(但还需要链接system库)
  • chrono库位于名字空间boost::chrono,需要包含头文件,为了使用扩展功能( chrono库另有一个头文件,包含了很多扩展功能),需要定义宏BOOST_CHRONO_EXTENSIONS, 即:
//#define BOOST_ERROR_CODE_HEADER_ONLY   // 无需编译即可使用system库
#define BOOST_CHRONO_HEADER_ONLY         // 无需编译即可使用chrono库
#define BOOST_CHRONO_EXTENSIONS			 // 使用扩展功能
#include 				 // 头文件
using namespace boost;
using namespace boost::chrono;

时间长度

chrono库定义了时间长度的表示duration(注:chrono不关心历史日期,没有date_time库里的date的概念),在概念上于date_time库的time_duration相同,都表示一定长度的时间,但duration侧重于编译期的时间单位表示,更接近ratio,所以与time_duration的接口非常不同。

类摘要

  • duration有两个模板参数,第一个参数Rep是一个算数类型(或者可以当作算术类型运算)比如int、double,表示时间单位的数量;第二个参数Period表示时间单位,以秒为基准,它必须是ratio类型,而且Period::num必须是正数
template <class Rep, class Period>  
class duration
{
    public:
        typedef Rep rep;                         //内部类型定义
        typedef Period period;
    private:
        rep rep_;                                //时间单位的计数
    public:
        BOOST_CONSTEXPR duration();              // 构造函数
        explicit duration(const Rep2& r);        
        //~duration() {} //= default;
//        BOOST_CONSTEXPR        duration(const duration& rhs) : rep_(rhs.rep_) {} // = default;
        duration& operator=(const duration& rhs);

        // conversions
        duration(const duration<Rep2, Period2>& d );
       
        BOOST_CONSTEXPR rep count() const ;

        // arithmetic

        BOOST_CONSTEXPR duration  operator+() const;
        BOOST_CONSTEXPR duration  operator-() const ;
        
        duration& operator++()      {++rep_; return *this;}
        duration  operator++(int)   {return duration(rep_++);}
        duration& operator--()      {--rep_; return *this;}
        duration  operator--(int)   {return duration(rep_--);}
        duration& operator+=(const duration& d);
        duration& operator-=(const duration& d);
        duration& operator*=(const rep& rhs);
        duration& operator/=(const rep& rhs) ;
        duration& operator%=(const rep& rhs);
        duration& operator%=(const duration& rhs);
        
        static BOOST_CONSTEXPR duration zero();
        static BOOST_CHRONO_LIB_CONSTEXPR duration min ();
        static BOOST_CHRONO_LIB_CONSTEXPR duration max ();
    };    
  • chrono库定义了常用的时间单位,比如hours、minutes、seconds、milliseconds等,但与date_time库time_duration使用派生子类的方法不同,chrono库通过在模板参数里使用不同的ratio来定义时间单位,因而每一个时间单位都是不同的类型:
    typedef duration<boost::int_least64_t, nano> nanoseconds;    // at least 64 bits needed
    typedef duration<boost::int_least64_t, micro> microseconds;  // at least 55 bits needed
    typedef duration<boost::int_least64_t, milli> milliseconds;  // at least 45 bits needed
    typedef duration<boost::int_least64_t> seconds;              // at least 35 bits needed
    typedef duration<boost::int_least32_t, ratio< 60> > minutes; // at least 29 bits needed
    typedef duration<boost::int_least32_t, ratio<3600> > hours;  // at least 23 bits needed

  • 除了这些预定义的时间单位,我们也可以自己使用typedef来简化duration类型,实现任意的时间分辨率,比如:
typedef duration<long,   ratio<30>> half_min;  //半分钟
typedef duration<int,    ratio<60*15>> quater;  //一刻钟
typedef duration<double, ratio<3600*24>> day;   //一天

//typedef duration my_hour;          //编译错误,不能直接使用整数
//typedef duration> my_ms; //编译错误,不能用负数

使用

duration就像是一个捆绑了算术类型的ratio,或者是一个带有时间单位的数字类型

基本运算

  • duration内部只有一个成员变量req_,用来存储时间的计数,支持各种算术运算----因为本质上它是一个算术类型。而时间单位则用ratio在编译期表示,各种时间单位的转换都是通过ratio在编译期模板元计算完成的
  • duration的构造函数指定它的时间计数,表示一段req_ * ratio秒的时间长度,成员函数count()可以获得内部的保存的时间计数rep_:
    seconds      s(10);             // 10s,相当于10 * ratio<1>
    minutes      m(5);				// 5min, 相当于 5 * ratio<60>
    hours        h(1);              // 1h, 相当于 1 * ratio<3600>
    milliseconds ms(100);           // 100ms, 相当于 100 * ratio<1, 1000>

    assert(s.count() == 10);         //使用成员函数
    assert(ms.count() == 100);
  • duration可以像int、double一样执行各种算数运算和比较运算,也能直接流输出:
    s *= 3;
    s += seconds(30);
    s = s - seconds(20);
    assert(s < seconds(50));
    std::cout << s << std::endl;

在这里插入图片描述

混用不同类型运算

  • 不同duration类型间混合运算要当心,小的时间单位可以直接于大的时间单位运算,因为大的时间单位可以自动转换为小的时间单位,比如1hour=60min。但反过来不一定可以,如果大的时间单位的模板参数是整数而无法表示小时间单位,那么就会发生编译错误,提醒你不能混合运算
    seconds s(10);
    minutes m(5);

    s += m;				 //混合不同的时间单位运算
    cout << s << endl;   //输出”310 seconds“

    //m+= s;             //编译失败,整数类型不能表示小数概念
  • 在duration的模板参数中使用double可以解决这个问题:
        seconds s(10);                               //10s
        typedef duration<double, ratio<60>> my_min;  //定义新的时间单位
        my_min m(5);                                 // 5分钟
        m += s;                                      //混合不同的时间单位运算 
        cout << m << endl;                           //输出”5.1667 minutes“

类型转换

  • chrono库提供一个模板函数duration_cast()来转换不同的时间单位,它只是执行简单的舍入取整
    using namespace std;
    seconds s(30);                               // 30s
    auto m = duration_cast<minutes>(s);           
    cout << m << endl;                           //"0 minutes"

    seconds s2(301);
    cout << duration_cast<minutes>(s2) << endl;  // "5 minutes"
  • chrono库里面还有三个C++标准之外的扩展函数floor()、ceil()、round()
    • floor():与duration_cast()相同,取下界,简单的截断处理
    • ceil():取上界
    • round():四舍五入操作
    using namespace std;
    seconds s(3600 + 50);                        //1h + 50s
    cout << floor<minutes>(s) << endl;             
    cout << ceil<minutes>(s) << endl;
    cout << round<minutes>(s) << endl;
    cout << round<hours>(s) << endl;    

在这里插入图片描述

时钟

  • 时钟Clock是chrono里的另一个概念,它确定了一个时间的起点(since)和时间单位(duration)。使用时钟我们就可以处理计算机世界里的时间。

  • chrono库实现了C++标准里定义的三个时钟:

时钟 说明
system_clock 计算机实际时间(system_clock的计时起点是1970-1-1)
steady_clock 稳定的时钟,不会因为系统时间调整而变化
high_resolution_clock 高分辨率的时钟,但通常是前两者的typedef
  • chrono库还定义了四个度量程序运行时间的时钟
时钟 说明
process_real_cpu_clock 进程执行的实际时间
process_user_cpu_clock 用户CPU时间
process_system_cup_clock 系统CPU时间
thread_clock 线程执行的实际时间

各时钟的接口基本相同,比如:

 class BOOST_CHRONO_DECL system_clock
  {
  public:
      typedef BOOST_SYSTEM_CLOCK_DURATION          duration;    //时间单位
      typedef duration::rep                        rep;         //计数单位
      typedef duration::period                     period;      //ratio
      typedef chrono::time_point<system_clock>     time_point;  //时间点
      BOOST_STATIC_CONSTEXPR bool is_steady =             false; //是否是稳定时钟

      static BOOST_CHRONO_INLINE time_point  now() BOOST_NOEXCEPT;   //获取当前时间
#if !defined BOOST_CHRONO_DONT_PROVIDE_HYBRID_ERROR_HANDLING
      static BOOST_CHRONO_INLINE time_point  now(system::error_code & ec);
#endif

	//转换到C里面的time_t结构
      static BOOST_CHRONO_INLINE std::time_t to_time_t(const time_point& t) BOOST_NOEXCEPT;
      static BOOST_CHRONO_INLINE time_point  from_time_t(std::time_t t) BOOST_NOEXCEPT;
  };
  • 时钟用内部的四个typedef确定了可以产生的时间类型,is_steady用来区分时钟是否会随系统时间调整而改变
  • 时钟类最重要的操作是now(),用来获得当前的时间点time_point对象
  • system_clock额外提供与time_t结构的互换操作,而steady_clock 等时钟不支持

使用辅助类clock_string的两个静态成员函数name()、since()可以获取时钟的描述信息:

template<class CharT>
  struct clock_string<system_clock, CharT>
  {
    static std::basic_string<CharT> name();   //时钟的名字
    static std::basic_string<CharT> since();  //时钟的起点
  };
  • 使用方法:
template<typename T>
using clock_desc = clock_string<T, char>;

void case3()
{
    using namespace std;
    cout << clock_desc<system_clock>::name() << endl;
    cout << clock_desc<system_clock>::since() << endl;

    cout << clock_desc<steady_clock>::name() << endl;
    cout << clock_desc<steady_clock>::since() << endl;

    cout << clock_desc<process_real_cpu_clock>::name() << endl;
    cout << clock_desc<process_real_cpu_clock>::since() << endl;
}

int main()
{
    case3();
}

boost:chrono_第1张图片

时间点

时间点time_point与时钟紧密关联,它必须由一个时钟产生,标记了自时钟起点以来所经过的时间。

template <class Clock, class Duration>
class time_point
{
    public:
        typedef Clock                     clock;                 //内部类型定义
        typedef Duration                  duration;
        typedef typename duration::rep    rep;
        typedef typename duration::period period;
        typedef Duration                  difference_type;

    private:
        duration d_;                                               //时间长度

    public:
        BOOST_CONSTEXPR time_point()//构造函数
        BOOST_CONSTEXPR explicit time_point(const duration& d);

        BOOST_CONSTEXPR duration time_since_epoch() const;        //返回时间长度


#ifdef BOOST_CHRONO_EXTENSIONS
        BOOST_CONSTEXPR time_point  operator+() const {return *this;}
        BOOST_CONSTEXPR time_point  operator-() const {return time_point(-d_);}
        time_point& operator++()      {++d_; return *this;}
        time_point  operator++(int)   {return time_point(d_++);}
        time_point& operator--()      {--d_; return *this;}
        time_point  operator--(int)   {return time_point(d_--);}

        time_point& operator+=(const rep& r) {d_ += duration(r); return *this;}
        time_point& operator-=(const rep& r) {d_ -= duration(r); return *this;}

#endif

        time_point& operator+=(const duration& d) {d_ += d; return *this;}
        time_point& operator-=(const duration& d) {d_ -= d; return *this;}

        // special values

        static BOOST_CHRONO_LIB_CONSTEXPR time_point min()   //最前可以表示的时间点
        static BOOST_CHRONO_LIB_CONSTEXPR time_point max();  //最后可以表示的时间点
    };

使用方法:

void case3()
{
    typedef duration<double, ratio<3600*24>> day;
    using namespace std;
    auto tp1 = system_clock::now();
    cout << tp1 << endl;

    auto d = tp1.time_since_epoch();
    cout << duration_cast<hours>(d) << endl;
    cout << duration_cast<day>(d) << endl;

    auto tp2 = tp1 +minutes(1);
    cout << tp2 << endl;
}

在这里插入图片描述

    using namespace std;
    typedef duration<double, ratio<3600*24>> day;
    auto tp = steady_clock::now();
    cout << tp << endl;

    auto d = tp.time_since_epoch();
    cout << round<minutes>(d) << endl;
    cout << round<hours>(d) << endl;

在这里插入图片描述

其他

自定义字面值

C++14新增了h/s/m/ms等字面值,用来便捷的声明duration变量。在C++11中我们同样可以定义自己的字面量:

hours operator"" _h(unsigned long long n)
{
    return hours(n);
}

seconds operator"" _s(unsigned long long n)
{
    return seconds(n);
}

milliseconds operator"" _ms(unsigned long long n)
{
    return milliseconds(n);
}
void case3()
{
    using namespace std;
    auto h = 5_h;
    auto s = 45_s;
    auto ms = 200_ms;

    cout << h << "; "<< s << "; "<< ms << endl;
}

在这里插入图片描述

时间点转换为实际时间

  • system_clock提供静态成员函数to_time_t(),可以把system_clock产生的time_point对象转换为time_t类型。以time_t为基础,我们既可以使用date_time库的from_time_t()转换到ptime对象,也可以用C的ctime()系统函数操作。
    auto tp = system_clock::now();
    auto t = system_clock::to_time_t(tp);

    cout << std::ctime(&t) << endl;
  • steady_clock等其他时钟因为不与确定的现实时间点关联,所以它们产生的time_point对象无法转换为实际时间

高精度计时器

  • 仿照timer,我们也可以实现高精度的计时器,它最高可以精确到ns级
  • 我们避免计时系统受系统时间外部调整的影响,我们需要使用steady_clock,在对象创建时基类时间点,之后就可以随时获取当前时间,两者相减得到时间
class steady_timer final {
private:
    typedef boost::chrono::steady_clock clock_type;    //定义时钟类型
    typedef clock_type ::time_point  time_point_type;  //定义时间点类型
    typedef boost::chrono::milliseconds  duration_type; //使用微秒精度

    time_point_type m_start = clock_type::now();          //构造时记录时间点
public:
    steady_timer() = default;
    ~steady_timer() = default;

public:
    void restart(){                                           //重启计时器
        m_start = clock_type ::now();
    }

    duration_type elapsed() const {
        return round<duration_type>(clock_type::now() - m_start);  //将流逝时间转换为微妙
    }
};

int main()
{
    using namespace std;
    steady_timer t;
    sleep(1);
    cout << t.elapsed() << endl;
}

你可能感兴趣的:(C++)