Boost C++ 库

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb SutterAndrei Alexandrescu, C++ 编码标准

用户指南 - Boost C++ 函数库
PrevUpHomeNext
获取 Boost.Chrono

Boost.Chrono 位于最新 Boost 发布的 /boost/chrono 文件夹中。文档、测试和示例文件夹位于 boost/libs/chrono/

您也可以从 Boost trunk 目录 boost/chrono 和 libs/chrono 访问最新(不稳定?)的状态。只需访问 此处 并按照匿名 SVN 访问的说明进行操作。

在哪里安装 Boost.Chrono?

简单的方法是将文件解压缩(或从 SVN 检出)到您的 BOOST_ROOT 目录中。

构建 Boost.Chrono

Boost.Chrono 可以通过定义 BOOST_CHRONO_HEADER_ONLY 配置为仅头文件库。但是 Boost.Chrono 依赖于非仅头文件库 Boost.System,因此您需要链接 boost_system。

Boost.System 有一个未记录的功能(使用宏 BOOST_ERROR_CODE_HEADER_ONLY)来使其成为仅头文件库。

如果未定义 BOOST_CHRONO_HEADER_ONLY,则在使用前需要编译并构建该库,例如使用

bjam libs/chrono/build
要求

特别是,Boost.Chrono 依赖于

Boost.Config

用于配置目的,...

Boost.Exception

用于 throw_exception,...

Boost.Integer

用于 cstdint 兼容性,...

Boost.MPL

用于 MPL Assert 和 bool,logical ...

Boost.Operators

用于运算符,...

Boost.Ratio

用于 ratio, milli, micro, ...

Boost.System

用于 error_code, ...

Boost.TypeTraits

用于 is_base, is_convertible, common_type, ...

Boost.Utility/EnableIf

用于 enable_if, ...

构建使用 Boost.Chrono 的可执行文件

除了链接 Boost.Chrono 库之外,您还需要链接 Boost.System 库。如果 Boost.System 配置为定义 BOOST_ERROR_CODE_HEADER_ONLY,则无需链接它,因为依赖部分是仅头文件库。

异常安全

库中的所有函数都是异常中立的,只要底层参数提供异常安全,它们就能提供强大的异常安全保证。

线程安全

库中的所有函数都是线程不安全的,除非明确注明。

由于 Boost.Chrono 不使用可变的全局变量,因此线程安全分析仅限于对每个实例变量的访问。如果另一个线程正在读取或写入用户变量,则使用修改用户变量访问的函数不是线程安全的。

测试过的编译器

该实现最终将与大多数符合 C++03 的编译器一起工作。目前我通常在以下环境进行测试

Windows,使用

  • MSVC 10.0

MinGW,使用

  • GCC 4.5.0
  • GCC 4.5.0 -std=c++0x
  • GCC 4.5.2
  • GCC 4.5.2 -std=c++0x
  • GCC 4.6.0
  • GCC 4.6.0 -std=c++0x
  • GCC 4.8.0
  • GCC 4.8.0 -std=c++0x

Ubuntu,使用 * GCC 4.4.6 * GCC 4.4.6 -std=c++0x * GCC 4.5.4 * GCC 4.5.4 -std=c++0x * GCC 4.6.1 * GCC 4.6.1 -std=c++0x * Intel 12.1.3 * Intel 12.1.3 -std=c++0x

OsX,使用

  • GCC 4.1.2
  • GCC 4.6.2
  • GCC 4.6.2 -std=c++0x
  • GCC 4.7.0
  • GCC 4.7.0 -std=c++0x
  • GCC 4.7.1
  • GCC 4.7.1 -std=c++0x
  • GCC 4.7.2
  • GCC 4.7.2 -std=c++0x
  • GCC 4.8.0
  • GCC 4.8.0 -std=c++0x
  • GCC 4.8.1
  • GCC 4.8.1 -std=c++0x
  • clang 3.1
  • clang 3.1 -std=c++0x -stdlib=libc++
  • clang 3.2
  • clang 3.2 -std=c++11 -stdlib=libc++

提交的代码在更多编译器上进行了测试。有两个编译器(VACPP 和 Borland)不提供所需的功能。其他如 Intel 和 Sun 在 I/O 方面存在一些问题。虽然一切都编译和链接正确,但存在一些运行时问题,我尚未解决。请参阅回归测试了解详情。

[Note] 注意

请让我们知道这在其他平台/编译器上的工作情况。

[Note] 注意

请将任何问题、评论和错误报告发送至 boost <at> lists <dot> boost <dot> org。

[////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 如果你只想测量程序的执行时间,这是一个完整的程序

#include <boost/chrono.hpp>
#include <cmath>

int main()
{
    boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now();

    for ( long i = 0; i < 10000000; ++i )
    std::sqrt( 123.456L ); // burn some time

    boost::chrono::duration<double> sec = boost::chrono::system_clock::now() - start;
    std::cout << "took " << sec.count() << " seconds\n";
    return 0;
}

输出是

took 0.832 seconds

duration 是这个库的核心。用户在日常使用中看到的接口与 Jeff Garland 编写的 Boost.DateTime time duration 几乎相同,无论是在语法还是行为上。这是一个非常受欢迎的 Boost 库,已有 7 年历史。这个接口有着巨大的积极历史。

该库包含六个时间单位 duration

选择这些单位作为 Boost 库的子集,因为它们是在休眠、等待条件变量或等待获取互斥锁时最常用的单位。这些单位中的每一个都只是对有符号整数计数的薄包装。也就是说,当你构造 minutes(3) 时,所发生的一切只是一个 3 存储在 minutes 中。当你构造 microseconds(3) 时,所发生的一切只是一个 3 存储在 microseconds 中。

这些不同类型唯一不同的上下文是在相互转换时。此时,使用单位特定的编译时转换常量将源单位转换为目标单位。只允许从较粗粒度单位到较细粒度单位的转换(在 Boost 中)。这种限制确保所有转换始终是精确的。也就是说,microseconds 始终可以表示 minutes 拥有的任何值。

Boost.DateTime 中,这些单位通过继承结合在一起。Boost.Chrono 而是通过类模板 duration 将这些单位结合在一起。也就是说,在 Boost.Chrono 中,上述所有六个单位都只是不同 duration 实例的类型定义。这种从 Boost.DateTime 的改变具有深远的积极影响,同时完全不改变日常使用的语法。

最直接的积极影响是该库可以立即生成任何所需的单位,并具有任何所需的精度。在对不同精度的 duration 进行比较或算术运算时,如果希望比较和算术运算是精确的,这有时是必要的。

第二个好处是,通过发布类模板 duration 接口,用户代码可以非常轻松地创建具有任何所需精度的 durationratio 工具用于指定精度,因此只要精度可以通过相对于秒的有理常数来表示,这个框架就可以精确地表示它(三分之一秒不是问题,三分之一飞秒也不是问题)。所有这些实用性和灵活性都无需任何成本,只需利用无运行时开销的 ratio 工具。

在 Boost.DateTime 中,hoursnanoseconds 的表示形式不同。前者通常用 long 表示,而后者需要 long long。原因很简单,就是范围。你不需要很多小时来覆盖极大的时间范围。但对于纳秒来说并非如此。在可能的情况下,能够减少某些单位的 sizeof 开销,可以显著提高性能。

Boost.Chrono 延续并推广了这种理念。不仅可以指定 duration 的精度,还可以指定其表示形式。这可以是任何整数类型,甚至是浮点类型。或者它可以是模拟算术类型的用户定义类型。六个预定义单位都使用有符号整数类型作为其表示形式。它们都具有 ± 292 年的最小范围。nanoseconds 需要 64 位来覆盖该范围。hours 只需要 23 位来覆盖该范围。

一个 duration 具有表示和计时周期(精度)。

template <class Rep, class Period = ratio<1> > class duration;

表示只是任何算术类型,或此类类型的模拟。表示存储一个节拍计数。此计数是 duration 中存储的唯一数据成员。如果表示是浮点类型,它可以根据表示的精度存储节拍的 fracciones。节拍周期由 ratio 表示,并编码到 duration 的类型中,而不是存储。节拍周期仅在尝试在不同 duration 之间进行转换时对 duration 的行为产生影响。当仅在相同 duration 之间进行算术运算时,节拍周期完全被忽略。

示例

typedef boost::chrono::duration<long, boost::ratio<60> > minutes;
minutes m1(3);                 // m1 stores 3
minutes m2(2);                 // m2 stores 2
minutes m3 = m1 + m2;          // m3 stores 5

typedef boost::chrono::duration<long long, boost::micro> microseconds;
microseconds us1(3);           // us1 stores 3
microseconds us2(2);           // us2 stores 2
microseconds us3 = us1 + us2;  // us3 stores 5

microseconds us4 = m3 + us3;   // us4 stores 300000005

在上面的最后一行代码中,有一个从分钟到微秒的隐式转换,导致微秒数相对较大。

如果您需要访问 duration 中的节拍计数,有一个成员 count() 简单地返回存储的节拍计数。

long long tc = us4.count();    // tc is 300000005

这些 duration 具有非常简单、非常可预测和非常可观察的行为。毕竟,这实际上只是 Jeff 的 boost time duration 库经过时间考验的接口(通过模板而不是继承统一)。

minutes m4 = m3 + us3;

它不会编译!理由是不应允许隐式截断错误发生。如果这能够编译,那么 m4 将持有 5,与 m3 的值相同。us3 关联的值已被有效地忽略。这类似于将双精度数赋值给 int 的问题:小数部分被默默地丢弃了。

有一个 duration_cast 工具可以明确要求这种行为

minutes m4 = boost::chrono::duration_cast<minutes>(m3 + us3);  // m4.count() == 5

通常,可以随意执行 duration 算术。如果未使用 duration_cast 并且它能编译,则算术是精确的。如果想覆盖这种精确算术行为,可以使用 duration_cast 明确指定此意图。duration_cast 的效率与隐式转换相同,甚至会尽可能地精确。

您可以使用 duration_cast<>duration 转换为您想要的任何单位。如果无法进行精确转换,此功能将向下舍入(截断)。例如

boost::chrono::nanoseconds start;
boost::chrono::nanoseconds end;
typedef boost::chrono::milliseconds ms;
ms d = boost::chrono::duration_cast<ms>(end - start);

// d now holds the number of milliseconds from start to end.

std::cout << d.count() << "ms\n";

我们可以转换为 nanoseconds,或者某些基于整数的持续时间,nanoseconds 总是可以精确转换的,那么 duration_cast<> 是不必要的。

typedef boost::chrono::nanoseconds ns;
ns d = end - start;
std::cout << ns.count() << "ns\n";

如果您需要带浮点表示的秒数,您也可以省去 duration_cast<>

typedef boost::chrono::duration<double> sec;  // seconds, stored with a double
sec d = end - start;
std::cout << sec.count() << "s\n";

如果您不确定是否需要 duration_cast<>,请随意尝试不使用它。如果转换是精确的,或者如果目标具有浮点表示,它将编译:否则它将不会编译。

如果您需要使用 duration_cast<>,但希望在转换不精确时向上舍入而不是向下舍入,这里有一个方便的小辅助函数可以实现。编写它实际上是理解 Boost.Chrono 的一个很好的入门项目。

template <class ToDuration, class Rep, class Period>
ToDuration
round_up(boost::chrono::duration<Rep, Period> d)
{
    // first round down
    ToDuration result = boost::chrono::duration_cast<ToDuration>(d);
    if (result < d)  // comparisons are *always* exact
       ++result;     // increment by one tick period
    return result;
}

typedef boost::chrono::milliseconds ms;
ms d = round_up<ms>(end - start);
// d now holds the number of milliseconds from start to end, rounded up.
std::cout << d.count() << "ms\n";

Boost.Chrono 提供了一些简单的舍入实用函数,用于处理持续时间。

// round down
template <class To, class Rep, class Period>
To
floor(const duration<Rep, Period>& d)
{
    return duration_cast<To>(d);
}

// round to nearest, to even on tie
template <class To, class Rep, class Period>
To
round(const duration<Rep, Period>& d)
{
    To t0 = duration_cast<To>(d);
    To t1 = t0;
    ++t1;
    BOOST_AUTO(diff0, d - t0);
    BOOST_AUTO(diff1, t1 - d);
    if (diff0 == diff1)
    {
        if (t0.count() & 1)
            return t1;
        return t0;
    }
    else if (diff0 < diff1)
        return t0;
    return t1;
}
// round up
template <class To, class Rep, class Period>
To
ceil(const duration<Rep, Period>& d)
{
    To t = duration_cast<To>(d);
    if (t < d)
        ++t;
    return t;
}

Chrono 库的优点在于其转换的简易性和准确性。例如,要将 milliseconds(一秒的 1/1000)转换为一秒的 1/30,必须将毫秒乘以 0.03。众所周知,0.03 无法在计算机中精确表示。尽管如此,当发生这种情况时,round 将精确(无舍入误差)检测到平局并四舍五入。差值 diff0diff1 不是近似值,而是精确差值,即使 d 的单位是毫秒而 To 是一秒的 1/30diff0diff1 的单位是一秒的 1/3000,毫秒和一秒的 1/30 都可以精确转换(无截断误差)。

同样,ceil 中的比较 t < d 是精确的,即使 td 之间没有精确转换。舍入函数的使用示例

#include <iostream>
#include <boost/chrono/chrono_io.hpp>
#include <boost/chrono/floor.hpp>
#include <boost/chrono/round.hpp>
#include <boost/chrono/ceil.hpp>

int main()
{
    using namespace boost::chrono;
    milliseconds ms(2500);
    std::cout << floor<seconds>(ms) << '\n';
    std::cout << round<seconds>(ms) << '\n';
    std::cout << ceil<seconds>(ms) << '\n';
    ms = milliseconds(2516);
    typedef duration<long, boost::ratio<1, 30> > frame_rate;
    std::cout << floor<frame_rate>(ms) << '\n';
    std::cout << round<frame_rate>(ms) << '\n';
    std::cout << ceil<frame_rate>(ms) << '\n';

    return 0;
}

此程序的输出应为

2 seconds
2 seconds
3 seconds
75 [1/30]seconds
75 [1/30]seconds
76 [1/30]seconds

我不想到处写 duration_cast。我满足于我的浮点表示的精度。

没问题。当转换的目标具有浮点表示时,所有转换都允许隐式发生。

typedef boost::chrono::duration<double, ratio<60> > dminutes;
dminutes dm4 = m3 + us3;  // dm4.count() == 5.000000083333333

如果你手动编写这些转换,你不可能使其更高效。ratio 的使用确保了所有转换常数在编译时尽可能地简化。这通常会导致转换因子的分子或分母简化为 1,随后在转换节拍计数的运行时值时被忽略。

用户有几个选项

  • 如果函数作者希望接受任何 duration,并愿意使用浮点 duration,他可以简单地使用任何浮点 duration 作为参数
void f(boost::chrono::duration<double> d)  // accept floating-point seconds
{
    // d.count() == 3.e-6 when passed boost::chrono::microseconds(3)
}

f(boost::chrono::microseconds(3));
  • 如果函数的作者只想处理整数 duration,并且满足于处理不低于纳秒的精度(仅作示例),他可以简单地将纳秒指定为参数。
void f(boost::chrono::nanoseconds d)
{
    // d.count() == 3000 when passed boost::chrono::microseconds(3)
}

f(boost::chrono::microseconds(3));

在此设计中,如果客户端想要传入浮点 duration,或精度高于纳秒的 duration,则客户端负责在转换为纳秒时选择自己的舍入模式。

boost::chrono::duration<double> s(1./3);  // 1/3 of a second
f(boost::chrono::duration_cast<boost::chrono::nanoseconds>(s));  // round towards zero in conversion to nanoseconds

在上面的示例中,函数 f 的客户端选择了“向零舍入”作为所需的纳秒舍入模式。如果客户端的 duration 无法精确转换为纳秒,并且未能选择转换方式,则编译器将拒绝调用。

f(s);  // does not compile
  • 如果函数的作者希望接受任何 duration,但希望使用整数表示并在内部控制舍入模式,那么他可以将函数模板化
template <class Rep, class Period>
void f(boost::chrono::duration<Rep, Period> d)
{
    // convert d to nanoseconds, rounding up if it is not an exact conversion
    boost::chrono::nanoseconds ns = boost::chrono::duration_cast<boost::chrono::nanoseconds>(d);
    if (ns < d)
        ++ns;
    // ns.count() == 333333334 when passed 1/3 of a floating-point second
}

  f(boost::chrono::duration<double>(1./3));
  • 如果示例中的作者不希望接受基于浮点的 duration,他可以这样强制执行该行为
template <class Period>
void f(boost::chrono::duration<long long, Period> d)
{
    // convert d to nanoseconds, rounding up if it is not an exact conversion
    boost::chrono::nanoseconds ns = boost::chrono::duration_cast<nanoseconds>(d);
    if (ns < d)
        ++ns;
    // ns.count() == 333333334 when passed 333333333333 picoseconds
}
// About 1/3 of a second worth of picoseconds
f(boost::chrono::duration<long long, boost::pico>(333333333333));

使用浮点 duration 并希望使用 f 的客户端现在必须在将结果传递给 f 之前自行转换为整数 duration

总之,f 的作者在为客户提供的接口方面具有相当大的灵活性和控制力,并且可以轻松地在其函数内部操作 duration

用户是否可以将单位不明确的 duration 传递给函数?

否。无论 f 的作者选择哪种选项,以下客户端代码都不会编译

f(3);  // Will not compile, 3 is not implicitly convertible to any __duration

这取决于表示形式。默认的 typedef 使用的表示形式不处理溢出。用户可以根据其应用程序的需要定义自己的处理溢出的表示形式。

尽管 duration 只需要关注精度和表示,但时钟和 time_point 密切相关并相互引用。因为时钟更容易解释,所以我们首先解释时钟,而不完全解释 time_point。一旦引入了时钟,再解释 time_point 是什么就会更容易。

时钟是一个概念,它包含 3 件事

  1. 一个具体的 duration 类型。
  2. 一个具体的 time_point 类型。
  3. 一个名为 now() 的函数,它返回具体的 time_point

标准定义了三个与计算机时间相关的系统级时钟。

  • system_clock 表示可以与外部时钟同步的系统级实时时钟。
  • steady_clock 不能显式更改,并且自初始纪元以来的时间以稳定的方式增加。
  • high_resolution_clock 旨在利用平台提供的最高分辨率的系统级时钟。

Boost.Chrono 在底层平台支持时提供它们。给定的平台可能无法提供所有这三个时钟。

该库增加了一些特定于进程或线程的时钟,也就是说,每个进程或每个线程都有一个时钟。

用户也可以轻松创建更多时钟。

给定一个名为 Clock 的时钟,它将具有

class Clock {
public:
    typedef an arithmetic-like type        rep;
    typedef an instantiation of ratio      period;
    typedef boost::chrono::duration<rep, period> duration;
    typedef boost::chrono::time_point<Clock>     time_point;
    static constexpr bool is_steady =      true or false;

    static time_point now();
};

可以使用以下方式从 Clock 获取当前时间

Clock::time_point t1 = Clock::now();

并且可以通过以下方式获得与 Clock 关联的两个 time_point 之间的时间 duration

Clock::duration d = Clock::now() - t1;

并且可以指定过去或未来的 time_point

Clock::time_point t2 = Clock::now() + d;

请注意,即使某个特定时钟变得过时,下一个时钟也会具有相同的 API。无需学习新知识。源代码更改将只是简单地更改时钟的类型。随着新时钟的引入,相同的 durationtime_point 框架将继续工作。并且可以在同一个程序中安全轻松地处理多个时钟。

一个 time_point 表示时间中的一个点,而不是时间的 duration。另一种说法是,一个 time_point 表示一个纪元加上或减去一个 durationtime_point 的例子包括

  • 电脑启动后 3 分钟。
  • 2038年1月19日星期二03:14:07 UTC
  • 我启动计时器后 20 毫秒。

在上述每个示例中,都暗示着一个不同的纪元。有时一个纪元具有数千年的意义。其他时候,一个纪元的意义会在一段时间后消失(例如计时器的开始,或计算机启动时)。然而,如果已知两个 time_point 共享相同的纪元,它们可以相减,产生一个有效的 duration,即使纪元的定义不再有意义。

Boost.Chrono 中,纪元是一个纯粹抽象和未指定的概念。没有类型表示纪元。它只是一个将 time_point 与时钟相关联(或不相关联)的想法,并且在它们共享一个时钟的情况下,将 time_point 相互关联。time_points 与不同时钟关联通常不可互操作,除非已知与每个时钟关联的纪元之间的关系。

一个 time_point 具有一个时钟和一个 duration

template <class Clock, class Duration = typename Clock::duration> class time_point;

time_point 的时钟不存储。它只是嵌入到 time_point 的类型中,并服务于两个目的

  1. 由于来自不同时钟的 time_point 具有不同的类型,因此可以指示编译器在不适当的方式使用不兼容的 time_point 时失败。
  2. 给定一个 time_point,人们通常需要将该 time_point 与“现在”进行比较。只要 time_point 知道它是相对于哪个时钟定义的,这会非常简单。

一个 time_pointduration 作为 time_point 的唯一数据成员存储。因此,time_point 和其对应的 duration 具有完全相同的布局。但它们的含义却非常不同。例如,说我想睡 3 分钟是一回事。说我想睡到我启动计时器后的 3 分钟是完全不同的事情(除非你正好现在启动了计时器)。这两种含义(以及睡眠选项)在睡眠、等待条件变量和等待互斥锁的常见用例中都具有巨大的实际价值。相同的概念和工具可以在(例如)Ada 中找到。

计时器示例

void f()
{
    boost::chrono::steady_clock::time_point start = boost::chrono::steady_clock::now();
    g();
    h();
    duration<double> sec = boost::chrono::steady_clock::now() - start;
    cout << "f() took " << sec.count() << " seconds\n";
}

请注意,如果以 duration 精度重要的方式使用两个时钟 time_point 之间的 duration,则将时钟的 duration 转换为已知 duration 是一个好的做法。这使得代码免受未来可能对时钟精度进行的更改的影响。例如,steady_clock 可以很容易地基于 CPU 的时钟速度。当您升级到更快的机器时,您不希望假定该时钟具有某个节拍周期的代码由于您的计时代码悄悄地改变了含义而开始出现运行时故障。

延迟循环示例

// delay for at least 500 nanoseconds:
auto go = boost::chrono::steady_clock::now() + boost::chrono::nanoseconds(500);
while (boost::chrono::steady_clock::now() < go)
    ;

上述代码将尽可能接近半微秒地延迟,无论 steady_clock 的精度如何。steady_clock 越精确,延迟到 500 纳秒就越准确。

当您需要将时间与已知纪元关联以便将其转换为日历时间时,system_clock 非常有用。请注意 system_clock 类中的特定函数。

当您需要等待特定的时间量时,steady_clock 非常有用。steady_clock 时间不能重置。与其他稳定时钟一样,它通常基于处理器节拍。

这是一个轮询解决方案,但可能效率太低

boost::chrono::steady_clock::time_point start= chrono::steady_clock::now();
boost::chrono::steady_clock::duration delay= chrono::seconds(5);
while (boost::chrono::steady_clock::now() - start <= delay) {}

可用时,high_resolution_clock 通常比其他系统级时钟的开销更大,因此仅当应用程序需要提供的分辨率时才使用它们。

进程和线程时钟通常用于测量代码块花费的时间,作为对不同代码块进行基本时间开销分析的方法(Boost.Chrono.Stopwatch 是此用途的清晰示例)。

每当您想测量当前线程花费的时间时,您都可以使用 thread_clock。例如

boost::chrono::thread_clock::time_point start=boost::chrono::thread_clock::now();
// ... do something ...

typedef boost::chrono::milliseconds ms;
ms d = boost::chrono::thread_clock::now() - start;
// d now holds the number of milliseconds from start to end.
std::cout << d.count() << "ms\n";

如果您需要带浮点表示的秒数,您可以这样做

typedef boost::chrono::duration<double> sec;  // seconds, stored with a double.
sec d = end - start;
std::cout << sec.count() << "s\n";

如果您想以编程方式检查 thread_clock::duration,您可以使用 thread_clock::rep 获取表示类型,并使用 thread_clock::period 获取节拍周期(这应该是一个 ratio 类型,它具有嵌套值 ratio::numratio::den)。thread_clock 的节拍周期是 thread_clock::period::num / thread_clock::period::den 秒:在这种情况下是 1/1000000000(十亿分之一秒),存储在 long long 中。

任何 duration 都可以流式输出到 basic_ostreamduration 的运行时值根据 duration::rep get_duration_style 和 durationpunct facet 的规则和当前格式设置进行格式化。

格式为

<value> <unit>

<unit> <value>
[Warning] 警告

需要更改 之后是一个空格,然后是 duration 的编译时单位名称。此单位名称基于从 ratio_string<> 返回的字符串以及用于构造已插入到流的区域设置中的 duration_punct 的数据构建。如果 duration_punct 未插入到流的区域设置中,则将向流的区域设置添加一个默认构造的 duration_punct

duration 单位名称有两种:长格式(前缀)和短格式(符号)。默认构造的 duration_punct 提供长格式(前缀)的名称。这些名称是英语描述。其他语言通过构造一个具有“hours”、“minutes”和“seconds”及其缩写(用于短格式)的正确拼写的 duration_punct 来支持。可以通过分别流式传输 duration_short()duration_long() 操纵器,或使用参数化操纵器 duration_fmt(duration_style::prefix)duration_fmt(duration_style::symbol) 来轻松选择短格式或长格式。

示例

#include <iostream>
#include <boost/chrono/chrono_io.hpp>

int main()
{
    using namespace std;
    using namespace boost;

    cout << "milliseconds(1) = "
         <<  boost::chrono::milliseconds(1) << '\n';

    cout << "milliseconds(3) + microseconds(10) = "
         <<  boost::chrono::milliseconds(3) + boost::chrono::microseconds(10) << '\n';

    cout << "hours(3) + minutes(10) = "
         <<  boost::chrono::hours(3) + boost::chrono::minutes(10) << '\n';

    typedef boost::chrono::duration<long long, boost::ratio<1, 2500000000> > ClockTick;
    cout << "ClockTick(3) + boost::chrono::nanoseconds(10) = "
         <<  ClockTick(3) + boost::chrono::nanoseconds(10) << '\n';

   // ...
    return 0;
}

输出可能为

milliseconds(1) = 1 microsecond
milliseconds(3) + microseconds(10) = 3010 microseconds
hours(3) + minutes(10) = 190 minutes
ClockTick(3) + nanoseconds(10) = 56 [1/5000000000]seconds

Set cout to use short names:
milliseconds(3) + microseconds(10) = 3010 μs
hours(3) + minutes(10) = 190 m
ClockTick(3) + nanoseconds(10) = 56 [1/5000000000]s

system_clock::now() = 129387415616250000 [1/10000000]s since Jan 1, 1970
monotonic_clock::now() = 37297387636417 ns since boot

Set cout to use long names:
high_resolution_clock::now() = 37297387655134 nanoseconds since boot

可以看出,每种持续时间类型都可以流式传输,而无需手动在运行时值之后流式传输编译时单位。当编译时单位已知为“常用单位”时,使用英语名称。对于“非常用单位”,单位名称由关联 ratio 的简化分子和分母组成。用于 duration::rep 的任何流/区域设置都用于该值。此外,当值为 1 时,使用单位的单数形式。

有时需要使用 SI 符号而不是 SI 前缀来缩短这些名称。这可以通过使用 symbol_format 操纵器来实现 [1]

cout << "\nSet cout to use short names:\n";
cout << boost::chrono::symbol_format;

cout << "milliseconds(3) + microseconds(10) = "
     <<  boost::chrono::milliseconds(3) + boost::chrono::microseconds(10) << '\n';

cout << "hours(3) + minutes(10) = "
     <<  boost::chrono::hours(3) + boost::chrono::minutes(10) << '\n';

cout << "ClockTick(3) + nanoseconds(10) = "
     <<  ClockTick(3) + boost::chrono::nanoseconds(10) << '\n';

输出可能为

Set cout to use short names:
milliseconds(3) + microseconds(10) = 3010 μs
hours(3) + minutes(10) = 190 m
ClockTick(3) + nanoseconds(10) = 56 [1/5000000000]s

system_clock::now() = 129387415616250000 [1/10000000]s since Jan 1, 1970
monotonic_clock::now() = 37297387636417 ns since boot

Set cout to use long names:
high_resolution_clock::now() = 37297387655134 nanoseconds since boot

微秒的 μ 指定为 U+00B5,根据流的字符大小编码为 UTF-8、UTF-16 或 UTF-32。

当格式决定在运行时做出时,最好使用参数化操纵器 duration_fmt,例如

duration_style style;
//...
cout << duration_fmt(style);

解析 duration 遵循与 duration 转换构造函数类似的规则。从 basic_istream 读取一个值和一个单位(SI 符号或带前缀的)。如果 duration 具有整数表示,则解析的值必须能在目标 duration 中精确表示(转换为目标 duration 单位后),否则设置 failbit。基于浮点表示的 duration 可以使用任何不会导致溢出的单位进行解析。

例如,一个包含“5000 milliseconds”的流可以解析为秒,但如果流包含“3001 ms”,解析为 seconds 将导致 failbit 被设置。

示例

#include <boost/chrono/chrono_io.hpp>
#include <sstream>
#include <cassert>

int main()
{
    using namespace std;

    istringstream in("5000 milliseconds 4000 ms 3001 ms");
    boost::chrono::seconds d(0);
    in >> d;
    assert(in.good());
    assert(d == seconds(5));
    in >> d;
    assert(in.good());
    assert(d == seconds(4));
    in >> d;
    assert(in.fail());
    assert(d == seconds(4));

    return 0;
}

请注意,duration 故障可能在解析过程后期发生。这意味着构成流中失败解析的字符通常会被消耗,尽管未能成功解析。

有时在模板代码中,很难知道您的 duration 的单位是什么。这都是确定性的,并且可检查。但是这样做可能很不方便,特别是如果您只需要打印出一条“调试”语句。例如

// round to nearest, to even on tie
template <class To, class Rep, class Period>
To
round(const duration<Rep, Period>& d)
{
    To t0 = duration_cast<To>(d);
    To t1 = t0;
    ++t1;
    auto diff0 = d - t0;
    cout << "diff0 = " << diff0 << '\n';
    auto diff1 = t1 - d;
    cout << "diff1 = " << diff1 << '\n';
    if (diff0 == diff1)
    {
        if (t0.count() & 1)
            return t1;
        return t0;
    }
    else if (diff0 < diff1)
        return t0;
    return t1;
}

这就是 duration 的 I/O 真正出彩的地方。编译器知道 diff0 的类型,并且通过这个提案,该类型(带有适当的单位)将自动为您打印出来。例如

milliseconds ms = round<milliseconds>(nanoseconds(123));  // diff0 = 123 nanoseconds
                                                          // diff1 = 999877 nanoseconds
milliseconds ms = round<milliseconds>(Ticks(44));         // diff0 = 2 [1/3000]seconds
                                                          // diff1 = 1 [1/3000]second

这种简单的I/O将使程序员更容易访问duration。

system_clock 是特殊的。它是唯一一个在 time_pointtime_t 之间进行转换的时钟。C 随后通过 ctimegmtimelocaltimestrftime 将 time_t 与 公历 相关联。无论是 C 还是 POSIX,都没有将 time_t公历 以外的任何日历相关联。ISO 8601 仅以 公历 的形式指定。

Boost.Chrono 提供基于公历的 system_clock::time_point I/O,不提供其他日历的 I/O。然而,由于 system_clock::time_point 仍然可以与 time_t 相互转换,因此客户端可以创建与其他 time_t 兼容,并因此与 system_clock::time_point 兼容的其他日历。

此外,所有主要的托管操作系统都已将系统时间存储为协调世界时 (UTC) 格式,以便于显示。因此,Boost.Chrono 规定 system_clock::time_point 的默认输出格式表示相对于 UTC 的时间点。

cout << system_clock::now() << '\n';

可以输出

2011-09-15 18:36:59.325132 +0000

此格式深受 ISO 8601 的影响,但在日期和时间之间放置一个“ ”而不是“T”。前者似乎更准确地代表了现有做法。选择全数字格式是为了让尽可能多的读者能够理解。选择 24 小时格式也是出于同样的原因。

在引用的标准中,只有 ISO 8601 讨论了秒分数的输出。C 和 POSIX 都没有内置此功能。然而,似乎普遍存在(截至本文撰写之时),system_clock::period 是亚秒级的。而且似乎很希望,如果流出 system_clock::time_point,您应该能够将其流回并获得相同的值。因此,秒分数的流式传输(至少默认情况下)似乎是不可避免的。

最后,尾随的“+0000”将 UTC 格式的 system_clock::time_point 与以计算机本地时区格式化的时间点区分开来。后者可以通过以下方式轻松实现

cout << time_fmt(local) << system_clock::now() << '\n';

可能导致

2011-09-15 14:36:59.325132 -0400

请注意,system_clock::time_point 本身既不是 UTC,也不是本地时间。然而,在实践中,system_clock::time_point 是一个在某个纪元之后计数的节拍数,该纪元与 UTC 同步。因此,当移动计算机跨越时区时,时区遍历不会影响 system_clock::now() 生成的 system_clock::time_point 的值。而且,只有在将其格式化以便人类消费时,才能选择 UTC 或本地时区。C 和 POSIX 对待 time_t 的方式与 Boost.Chrono 对待 system_clock::time_point 的方式相同。

tm* gmtime(const time_t* timer) -> UTC
tm* localtime(const time_t* timer) -> local time

本提案只是将 C/POSIX time_t 功能扩展到 C++ 语法和 system_clock::time_point

操纵器 time_fmt() 是“粘性”的。它将一直有效,直到流被销毁或被更改。流可以使用以下方法重置为其默认状态

cout << time_fmt(utc);

并且可以通过使用时间格式序列进一步自定义格式。例如

cout << time_fmt(local, "%A %B %e, %Y %r");
cout << system_clock::now() << '\n';  // Sunday April 24, 2011 02:36:59 PM

为宽流指定格式化操纵器时,请使用宽字符串。

您可以将相同的操纵器与输入流一起使用来指定解析序列。

不幸的是,没有指示秒分数的格式/解析序列。Boost.Chrono 不提供此类序列。同时,可以通过默认格式或在 time_fmt() 中使用空字符串来格式化和解析 system_clock::time_point 的秒分数。

流的当前区域设置可能会影响提供给 system_clock::time_point 操纵器的解析/格式序列(例如,星期几的名称和月份的名称)。

system_clock::time_point 不同,其他时钟与 time_t 没有转换关系。steady_clock::time_point 与 UTC 之间可能完全没有关系(UTC 不稳定)。

通常,time_point 的格式化方式是先输出其内部的duration,然后是描述 time_point::clock 纪元的字符串。此字符串因不同的时钟以及所提供时钟的每个实现而异。

#ifdef BOOST_CHRONO_HAS_CLOCK_STEADY
    cout << "steady_clock::now() = " << boost::chrono::steady_clock::now() << '\n';
#endif
    cout << "\nSet cout to use long names:\n"
            << boost::chrono::duration_long
            << "high_resolution_clock::now() = "
            << boost::chrono::high_resolution_clock::now() << '\n';

输出可能为

steady_clock::now() = 37297387636417 ns since boot

Set cout to use long names:
high_resolution_clock::now() = 37297387655134 nanoseconds since boot

解析 time_point 涉及首先解析 duration,然后解析纪元字符串。如果纪元字符串与 time_point::clock 关联的字符串不匹配,则会设置 failbit。

示例

#include <boost/chrono/chrono_io.hpp>
#include <sstream>
#include <iostream>
#include <cassert>

int main()
{
    using namespace std;

    boost::chrono::high_resolution_clock::time_point t0 = boost::chrono::high_resolution_clock::now();
    stringstream io;
    io << t0;
    boost::chrono::high_resolution_clock::time_point t1;
    io >> t1;
    assert(!io.fail());
    cout << io.str() << '\n';
    cout << t0 << '\n';
    cout << t1 << '\n';
    boost::chrono::high_resolution_clock::time_point t = boost::chrono::high_resolution_clock::now();
    cout << t << '\n';

    cout << "That took " << t - t0 << '\n';
    cout << "That took " << t - t1 << '\n';

    return 0;
}

输出可能为

50908679121461 nanoseconds since boot
That took 649630 nanoseconds

这是一个简单示例,用于查找计算机已运行(在此平台上)多少小时

#include <boost/chrono/chrono_io.hpp>
#include <iostream>

int main()
{
    using namespace std;
    using namespace boost;

    typedef boost::chrono::time_point<boost::chrono::steady_clock, boost::chrono::duration<double, boost::ratio<3600> > > T;
    T tp = boost::chrono::steady_clock::now();
    std::cout << tp << '\n';
    return 0;
}

输出可能为

17.8666 hours since boot

前述 I/O 部分中描述的 I/O 接口是用户级别的。这些服务基于在编写库时有用的低级服务。低级服务与访问关联的 ios 状态和 locale facets 相关。该设计遵循 C++ IOStreams 标准设计

该库将 duration 的区域设置相关的解析和格式化封装到一个新的 facet 类中。在此示例中,我们重点关注格式化。相关的 facet 类是 duration_put,类似于 time_put、money_put 等。

此 facet 的使用与 time_put facet 类似。

接下来我们展示如何覆盖 duration 的默认构造函数以执行您想要的任何操作(在此示例中将其设置为零)。我们只需更改表示形式

namespace I_dont_like_the_default_duration_behavior {

template <class R>
class zero_default
{
public:
    typedef R rep;

private:
    rep rep_;
public:
    zero_default(rep i = 0) : rep_(i) {}
    operator rep() const {return rep_;}

    zero_default& operator+=(zero_default x) {rep_ += x.rep_; return *this;}
    zero_default& operator-=(zero_default x) {rep_ -= x.rep_; return *this;}
    zero_default& operator*=(zero_default x) {rep_ *= x.rep_; return *this;}
    zero_default& operator/=(zero_default x) {rep_ /= x.rep_; return *this;}

    zero_default  operator+ () const {return *this;}
    zero_default  operator- () const {return zero_default(-rep_);}
    zero_default& operator++()       {++rep_; return *this;}
    zero_default  operator++(int)    {return zero_default(rep_++);}
    zero_default& operator--()       {--rep_; return *this;}
    zero_default  operator--(int)    {return zero_default(rep_--);}

    friend zero_default operator+(zero_default x, zero_default y) {return x += y;}
    friend zero_default operator-(zero_default x, zero_default y) {return x -= y;}
    friend zero_default operator*(zero_default x, zero_default y) {return x *= y;}
    friend zero_default operator/(zero_default x, zero_default y) {return x /= y;}

    friend bool operator==(zero_default x, zero_default y) {return x.rep_ == y.rep_;}
    friend bool operator!=(zero_default x, zero_default y) {return !(x == y);}
    friend bool operator< (zero_default x, zero_default y) {return x.rep_ < y.rep_;}
    friend bool operator<=(zero_default x, zero_default y) {return !(y < x);}
    friend bool operator> (zero_default x, zero_default y) {return y < x;}
    friend bool operator>=(zero_default x, zero_default y) {return !(x < y);}
};

typedef boost::chrono::duration<zero_default<long long>, boost::nano        > nanoseconds;
typedef boost::chrono::duration<zero_default<long long>, boost::micro       > microseconds;
typedef boost::chrono::duration<zero_default<long long>, boost::milli       > milliseconds;
typedef boost::chrono::duration<zero_default<long long>                      > seconds;
typedef boost::chrono::duration<zero_default<long long>, boost::ratio<60>   > minutes;
typedef boost::chrono::duration<zero_default<long long>, boost::ratio<3600> > hours;
}

用法

using namespace I_dont_like_the_default_duration_behavior;

milliseconds ms;
std::cout << ms.count() << '\n';

请参阅源文件 example/i_dont_like_the_default_duration_behavior.cpp

开发了一种“饱和”有符号整数类型。此类型具有 +/- 无穷大和 NaN(类似于 IEEE 浮点),但否则遵循有符号整数算术。该类随后用作 boost::chrono::duration 中的模板参数 Rep,以演示一个不会悄然忽略溢出的 duration 类。

请参阅源文件 example/saturating.cpp

示例 round_up 实用程序:将 d 转换为 To,对于不精确的转换进行向上舍入。能够轻松编写此函数是一个主要特性!

#include <boost/chrono.hpp>
#include <boost/type_traits.hpp>

#include <iostream>

template <class To, class Rep, class Period>
To
round_up(boost::chrono::duration<Rep, Period> d)
{
    To result = boost::chrono::duration_cast<To>(d);
    if (result < d)
        ++result;
    return result;
}

演示与类似 xtime 的设施的交互

struct xtime
{
    long sec;
    unsigned long usec;
};

template <class Rep, class Period>
xtime
to_xtime_truncate(boost::chrono::duration<Rep, Period> d)
{
    xtime xt;
    xt.sec = static_cast<long>(boost::chrono::duration_cast<seconds>(d).count());
    xt.usec = static_cast<long>(boost::chrono::duration_cast<microseconds>(d - seconds(xt.sec)).count());
    return xt;
}

template <class Rep, class Period>
xtime
to_xtime_round_up(boost::chrono::duration<Rep, Period> d)
{
    xtime xt;
    xt.sec = static_cast<long>(boost::chrono::duration_cast<seconds>(d).count());
    xt.usec = static_cast<unsigned long>(round_up<boost::chrono::microseconds>(d - boost::chrono::seconds(xt.sec)).count());
    return xt;
}

microseconds
from_xtime(xtime xt)
{
    return boost::chrono::seconds(xt.sec) + boost::chrono::microseconds(xt.usec);
}

void print(xtime xt)
{
    std::cout << '{' << xt.sec << ',' << xt.usec << "}\n";
}

用法

xtime xt = to_xtime_truncate(seconds(3) + boost::chrono::milliseconds(251));
print(xt);
boost::chrono::milliseconds ms = boost::chrono::duration_cast<boost::chrono::milliseconds>(from_xtime(xt));
std::cout << ms.count() << " milliseconds\n";
xt = to_xtime_round_up(ms);
print(xt);
xt = to_xtime_truncate(boost::chrono::seconds(3) + nanoseconds(999));
print(xt);
xt = to_xtime_round_up(boost::chrono::seconds(3) + nanoseconds(999));
print(xt);

请参阅源文件 xtime.cpp

用户可以轻松创建自己的时钟,时间点和时间持续时间都具有自己选择的表示和精度。例如,如果有一个硬件计数器简单地以 CPU 的每个周期递增计数,则可以使用几十行代码非常轻松地在其上构建时钟、时间点和持续时间。此类系统可用于调用时间敏感的线程 API,例如睡眠、等待条件变量或等待互斥锁。此处提出的 API 对此是 300MHz 时钟(具有 3 1/3 纳秒的时钟周期)还是 3GHz 时钟(具有 1/3 纳秒的时钟周期)不敏感。并且生成的代码将与用户编写专用时钟周期计数器一样高效。

#include <boost/chrono.hpp>
#include <boost/type_traits.hpp>
#include <iostream>

template <long long speed>
struct cycle_count
{
    typedef typename boost::__ratio_multiply__<boost::ratio<speed>, boost::mega>::type
        frequency;  // Mhz
    typedef typename boost::__ratio_divide__<boost::ratio<1>, frequency>::type period;
    typedef long long rep;
    typedef boost::chrono::duration<rep, period> duration;
    typedef boost::chrono::time_point<cycle_count> time_point;

    static time_point now()
    {
        static long long tick = 0;
        // return exact cycle count
        return time_point(duration(++tick));  // fake access to clock cycle count
    }
};

template <long long speed>
struct approx_cycle_count
{
    static const long long frequency = speed * 1000000;  // MHz
    typedef nanoseconds duration;
    typedef duration::rep rep;
    typedef duration::period period;
    static const long long nanosec_per_sec = period::den;
    typedef boost::chrono::time_point<approx_cycle_count> time_point;

    static time_point now()
    {
        static long long tick = 0;
        // return cycle count as an approximate number of nanoseconds
        // compute as if nanoseconds is only duration in the std::lib
        return time_point(duration(++tick * nanosec_per_sec / frequency));
    }
};

请参阅源文件 cycle_count.cpp

此示例演示了使用类似 timeval 的结构作为 durationtime_point 的表示类型。

class xtime {
private:
    long tv_sec;
    long tv_usec;

    void fixup() {
        if (tv_usec < 0) {
            tv_usec += 1000000;
            --tv_sec;
        }
    }

public:
    explicit xtime(long sec, long usec) {
        tv_sec = sec;
        tv_usec = usec;
        if (tv_usec < 0 || tv_usec >= 1000000) {
            tv_sec += tv_usec / 1000000;
            tv_usec %= 1000000;
            fixup();
        }
    }

    explicit xtime(long long usec) {
        tv_usec = static_cast<long>(usec % 1000000);
        tv_sec  = static_cast<long>(usec / 1000000);
        fixup();
    }

    // explicit
    operator long long() const {return static_cast<long long>(tv_sec) * 1000000 + tv_usec;}

    xtime& operator += (xtime rhs) {
        tv_sec += rhs.tv_sec;
        tv_usec += rhs.tv_usec;
        if (tv_usec >= 1000000) {
            tv_usec -= 1000000;
            ++tv_sec;
        }
        return *this;
    }

    xtime& operator -= (xtime rhs) {
        tv_sec -= rhs.tv_sec;
        tv_usec -= rhs.tv_usec;
        fixup();
        return *this;
    }

    xtime& operator %= (xtime rhs) {
        long long t = tv_sec * 1000000 + tv_usec;
        long long r = rhs.tv_sec * 1000000 + rhs.tv_usec;
        t %= r;
        tv_sec = static_cast<long>(t / 1000000);
        tv_usec = static_cast<long>(t % 1000000);
        fixup();
        return *this;
    }

    friend xtime operator+(xtime x, xtime y) {return x += y;}
    friend xtime operator-(xtime x, xtime y) {return x -= y;}
    friend xtime operator%(xtime x, xtime y) {return x %= y;}

    friend bool operator==(xtime x, xtime y)
        { return (x.tv_sec == y.tv_sec && x.tv_usec == y.tv_usec); }

    friend bool operator<(xtime x, xtime y) {
        if (x.tv_sec == y.tv_sec)
            return (x.tv_usec < y.tv_usec);
        return (x.tv_sec < y.tv_sec);
    }

    friend bool operator!=(xtime x, xtime y) { return !(x == y); }
    friend bool operator> (xtime x, xtime y) { return y < x; }
    friend bool operator<=(xtime x, xtime y) { return !(y < x); }
    friend bool operator>=(xtime x, xtime y) { return !(x < y); }

    friend std::ostream& operator<<(std::ostream& os, xtime x)
        {return os << '{' << x.tv_sec << ',' << x.tv_usec << '}';}
};

基于类似 timeval 结构的计时器。

class xtime_clock
{
public:
    typedef xtime                                  rep;
    typedef boost::micro                           period;
    typedef boost::chrono::duration<rep, period>   duration;
    typedef boost::chrono::time_point<xtime_clock> time_point;

    static time_point now()
    {
    #if defined(BOOST_CHRONO_WINDOWS_API)
        time_point t(duration(xtime(0)));
        gettimeofday((timeval*)&t, 0);
        return t;

    #elif defined(BOOST_CHRONO_MAC_API)

        time_point t(duration(xtime(0)));
        gettimeofday((timeval*)&t, 0);
        return t;

    #elif defined(BOOST_CHRONO_POSIX_API)
        //time_point t(0,0);

        timespec ts;
        ::clock_gettime( CLOCK_REALTIME, &ts );

        xtime xt( ts.tv_sec, ts.tv_nsec/1000);
        return time_point(duration(xt));

    #endif  // POSIX
    }
};

xtime_clock 的用法

std::cout << "sizeof xtime_clock::time_point = " << sizeof(xtime_clock::time_point) << '\n';
std::cout << "sizeof xtime_clock::duration = " << sizeof(xtime_clock::duration) << '\n';
std::cout << "sizeof xtime_clock::rep = " << sizeof(xtime_clock::rep) << '\n';
xtime_clock::duration delay(boost::chrono::milliseconds(5));
xtime_clock::time_point start = xtime_clock::now();
while (xtime_clock::now() - start <= delay) {}
xtime_clock::time_point stop = xtime_clock::now();
xtime_clock::duration elapsed = stop - start;
std::cout << "paused " << boost::chrono::::nanoseconds(elapsed).count() << " nanoseconds\n";

请参阅源文件 example/timeval_demo.cpp

用户可以定义一个函数,返回最早的 time_point,如下所示

template <class Clock, class Duration1, class Duration2>
typename boost::common_type<time_point<Clock, Duration1>,
                     time_point<Clock, Duration2> >::type
min(time_point<Clock, Duration1> t1, time_point<Clock, Duration2> t2)
{
    return t2 < t1 ? t2 : t1;
}

能够轻松编写此函数是一个主要功能!

BOOST_AUTO(t1, system_clock::now() + seconds(3));
BOOST_AUTO(t2, system_clock::now() + nanoseconds(3));
BOOST_AUTO(t3, min(t1, t2));

请参阅源文件 example/min_time_point.cpp

#include <boost/chrono.hpp>
#include <iostream>
#include <iomanip>

using namespace boost::chrono;

template< class Clock >
class timer
{
  typename Clock::time_point start;
public:
  timer() : start( Clock::now() ) {}
  typename Clock::duration elapsed() const
  {
    return Clock::now() - start;
  }
  double seconds() const
  {
    return elapsed().count() * ((double)Clock::period::num/Clock::period::den);
  }
};

int main()
{
  timer<system_clock> t1;
  timer<steady_clock> t2;
  timer<high_resolution_clock> t3;

  std::cout << "Type the Enter key: ";
  std::cin.get();

  std::cout << std::fixed << std::setprecision(9);
  std::cout << "system_clock-----------: "
            << t1.seconds() << " seconds\n";
  std::cout << "steady_clock--------: "
            << t2.seconds() << " seconds\n";
  std::cout << "high_resolution_clock--: "
            << t3.seconds() << " seconds\n";

  system_clock::time_point d4 = system_clock::now();
  system_clock::time_point d5 = system_clock::now();

  std::cout << "\nsystem_clock latency-----------: " << (d5 - d4).count() << std::endl;

  steady_clock::time_point d6 = steady_clock::now();
  steady_clock::time_point d7 = steady_clock::now();

  std::cout << "steady_clock latency--------: " << (d7 - d6).count() << std::endl;

  high_resolution_clock::time_point d8 = high_resolution_clock::now();
  high_resolution_clock::time_point d9 = high_resolution_clock::now();

  std::cout << "high_resolution_clock latency--: " << (d9 - d8).count() << std::endl;

  std::time_t now = system_clock::to_time_t(system_clock::now());

  std::cout << "\nsystem_clock::now() reports UTC is "
    << std::asctime(std::gmtime(&now)) << "\n";

  return 0;
}

此程序的运行输出如下所示

请参阅源文件 example/await_keystroke.cpp

在上面的示例中,我们利用了这样一个事实:只要 time_point 具有相同的时钟,并且它们的内部 duration 可以转换,它们就可以进行转换。我们还利用了这样一个事实:具有浮点表示的 duration 可以从任何类型转换。最后,I/O 系统为我们的 duration<double, ratio<3600>> 发现了更易读的“小时”单位。

还有许多其他格式化 durationtime_point 的方法。例如,请参阅 ISO 8601。本文档旨在告知读者如何在需要时编写自定义 I/O,而不是将所有可能性都编码到 operator<< 中,这即使对于最微不足道的使用也会导致显著的代码膨胀。

举例来说,下面的函数使用以下格式将任意 duration 流式传输到任意 basic_ostreams

[-]d/hh:mm:ss.cc

其中

  • d天数
  • h小时数
  • m分钟数
  • ss.cc 是四舍五入到最近百分之一秒的秒数
    1. 包含 <boost/chrono/chrono_io.hpp>
    2. 包含 <ostream>
    3. 包含 <iostream>
// format duration as [-]d/hh::mm::ss.cc
template <class CharT, class Traits, class Rep, class Period>
std::basic_ostream<CharT, Traits>&
display(std::basic_ostream<CharT, Traits>& os,
        boost::chrono::duration<Rep, Period> d)
{
    using namespace std;
    using namespace boost;

    typedef boost::chrono::duration<long long, boost::ratio<86400> > days;
    typedef boost::chrono::duration<long long, boost:centi> centiseconds;

    // if negative, print negative sign and negate
    if (d < boost::chrono::duration<Rep, Period>(0))
    {
        d = -d;
        os << '-';
    }
    // round d to nearest centiseconds, to even on tie
    centiseconds cs = boost::chrono::duration_cast<centiseconds>(d);
    if (d - cs > boost::chrono::milliseconds(5)
        || (d - cs == boost::chrono::milliseconds(5) && cs.count() & 1))
        ++cs;
    // separate seconds from centiseconds
    boost::chrono::seconds s = boost::chrono::duration_cast<boost::chrono::seconds>(cs);
    cs -= s;
    // separate minutes from seconds
    boost::chrono::minutes m = boost::chrono::duration_cast<boost::chrono::minutes>(s);
    s -= m;
    // separate hours from minutes
    boost::chrono::hours h = boost::chrono::duration_cast<boost::chrono::hours>(m);
    m -= h;
    // separate days from hours
    days dy = boost::chrono::duration_cast<days>(h);
    h -= dy;
    // print d/hh:mm:ss.cc
    os << dy.count() << '/';
    if (h < boost::chrono::hours(10))
        os << '0';
    os << h.count() << ':';
    if (m < boost::chrono::minutes(10))
        os << '0';
    os << m.count() << ':';
    if (s < boost::chrono::seconds(10))
        os << '0';
    os << s.count() << '.';
    if (cs < boost::chrono::centiseconds(10))
        os << '0';
    os << cs.count();
    return os;
}

int main()
{
    using namespace std;
    using namespace boost;

    display(cout, boost::chrono::steady_clock::now().time_since_epoch()
                  + boost::chrono::duration<long, boost::mega>(1)) << '\n';
    display(cout, -boost::chrono::milliseconds(6)) << '\n';
    display(cout, boost::chrono::duration<long, boost::mega>(1)) << '\n';
    display(cout, -boost::chrono::duration<long, boost::mega>(1)) << '\n';
}

输出可能为

12/06:03:22.95
-0/00:00:00.01
11/13:46:40.00
-11/13:46:40.00

C++11 标准库的多线程库要求能够以与现代 C++ 实践一致的方式处理时间表示。下面是此接口的模拟。

非成员 sleep 函数可以按如下方式模拟

namespace boost { namespace this_thread {

template <class Rep, class Period>
void sleep_for(const chrono::duration<Rep, Period>& d) {
    chrono::microseconds t = chrono::duration_cast<chrono::microseconds>(d);
    if (t < d)
        ++t;
    if (t > chrono::microseconds(0))
        std::cout << "sleep_for " << t.count() << " microseconds\n";
}

template <class Clock, class Duration>
void sleep_until(const chrono::time_point<Clock, Duration>& t) {
    using namespace chrono;
    typedef time_point<Clock, Duration> Time;
    typedef system_clock::time_point SysTime;
    if (t > Clock::now()) {
        typedef typename common_type<typename Time::duration,
                                     typename SysTime::duration>::type D;
        /* auto */ D d = t - Clock::now();
        microseconds us = duration_cast<microseconds>(d);
        if (us < d)
            ++us;
        SysTime st = system_clock::now() + us;
        std::cout << "sleep_until    ";
        detail::print_time(st);
        std::cout << " which is " << (st - system_clock::now()).count() << " microseconds away\n";
    }
}

}}

接下来是修改后的 boost::thread::timed_mutex 函数

namespace boost {
struct timed_mutex {
    // ...

    template <class Rep, class Period>
    bool try_lock_for(const chrono::duration<Rep, Period>& d) {
        chrono::microseconds t = chrono::duration_cast<chrono::microseconds>(d);
        if (t <= chrono::microseconds(0))
            return try_lock();
        std::cout << "try_lock_for " << t.count() << " microseconds\n";
        return true;
    }

    template <class Clock, class Duration>
    bool try_lock_until(const chrono::time_point<Clock, Duration>& t)
    {
        using namespace chrono;
        typedef time_point<Clock, Duration> Time;
        typedef system_clock::time_point SysTime;
        if (t <= Clock::now())
            return try_lock();
        typedef typename common_type<typename Time::duration,
          typename Clock::duration>::type D;
        /* auto */ D d = t - Clock::now();
        microseconds us = duration_cast<microseconds>(d);
        SysTime st = system_clock::now() + us;
        std::cout << "try_lock_until ";
        detail::print_time(st);
        std::cout << " which is " << (st - system_clock::now()).count()
          << " microseconds away\n";
        return true;
    }
};
}

boost::thread::condition_variable 时间相关函数修改如下

namespace boost {
struct condition_variable
{
    // ...

    template <class Rep, class Period>
    bool wait_for(mutex&, const chrono::duration<Rep, Period>& d) {
        chrono::microseconds t = chrono::duration_cast<chrono::microseconds>(d);
        std::cout << "wait_for " << t.count() << " microseconds\n";
        return true;
    }

    template <class Clock, class Duration>
    bool wait_until(mutex&, const chrono::time_point<Clock, Duration>& t) {
        using namespace boost::chrono;
        typedef time_point<Clock, Duration> Time;
        typedef system_clock::time_point SysTime;
        if (t <= Clock::now())
            return false;
        typedef typename common_type<typename Time::duration,
          typename Clock::duration>::type D;
        /* auto */ D d = t - Clock::now();
        microseconds us = duration_cast<microseconds>(d);
        SysTime st = system_clock::now() + us;
         std::cout << "wait_until     ";
        detail::print_time(st);
        std::cout << " which is " << (st - system_clock::now()).count()
          << " microseconds away\n";
        return true;
    }
};
}

接下来是这些函数的简单用法

boost::mutex m;
boost::timed_mutex mut;
boost::condition_variable cv;

using namespace boost;

this_thread::sleep_for(chrono::seconds(3));
this_thread::sleep_for(chrono::nanoseconds(300));
chrono::system_clock::time_point time_limit = chrono::system_clock::now() + chrono::__seconds_(4) + chrono::milliseconds(500);
this_thread::sleep_until(time_limit);

mut.try_lock_for(chrono::milliseconds(30));
mut.try_lock_until(time_limit);

cv.wait_for(m, chrono::minutes(1));    // real code would put this in a loop
cv.wait_until(m, time_limit);  // real code would put this in a loop

// For those who prefer floating-point
this_thread::sleep_for(chrono::duration<double>(0.25));
this_thread::sleep_until(chrono::system_clock::now() + chrono::duration<double>(1.5));

请参阅源文件 example/simulated_thread_interface_demo.cpp

法语输出示例

#include <boost/chrono/chrono_io.hpp>
#include <iostream>
#include <locale>

int main()
{
    using namespace std;
    using namespace boost;
    using namespace boost::chrono;

    cout.imbue(locale(locale(), new duration_punct<char>
        (
            duration_punct<char>::use_long,
            "secondes", "minutes", "heures",
            "s", "m", "h"
        )));
    hours h(5);
    minutes m(45);
    seconds s(15);
    milliseconds ms(763);
    cout << h << ", " << m << ", " << s << " et " << ms << '\n';
}

输出是

5 heures, 45 minutes, 15 secondes et 763 millisecondes

请参阅源文件 example/french.cpp

C++ 标准委员会当前的工作论文

该库最权威的参考资料是 C++ 标准委员会当前的工作论文 (WP)。20.11 时间实用程序“time”

N2661 - A Foundation to Sleep On

来自 Howard E. Hinnant、Walter E. Brown、Jeff Garland 和 Marc Paterno。内容非常丰富,并提供了关键设计决策的动机

LGW 934。duration 缺少 operator%

来自 Terry Golubiewski。内容非常丰富,并提供了关键设计决策的动机

LGW 935。时钟错误处理需要指定

来自 Beman Dawes。此问题已被声明为 NAD Future。




PrevUpHomeNext