Boost C++ 库

...世界上评价最高、设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ Coding Standards

用户指南 - 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

用于配置 purposes, ...

Boost.Exception

用于 throw_exception, ...

Boost.Integer

用于 cstdint conformance, ...

Boost.MPL

用于 MPL Assert and bool, logical ...

Boost.Operators

用于 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] 注意

请将任何问题、评论和 bug 报告发送到 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 时间 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 工具用于指定精度,因此只要精度可以用相对于秒的有理数常数来表示,该框架就可以精确地表示它(三分之一秒没问题,femto 秒的三分之一也没问题)。所有这些的实用性和灵活性都来自于使用零运行时开销的 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 中存储的唯一数据成员。如果表示法是浮点型的,它可以存储表示法精度的滴答分数。滴答周期由 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 时间 duration 库(通过模板而不是继承统一)经过时间考验的接口。

minutes m4 = m3 + us3;

它不会编译!理由是,不应允许隐式截断错误发生。如果这样做,m4 将保持 5 的值,与 m3 相同。us3 的值已被有效忽略。这类似于将 double 分配给 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 库的优点在于进行此类转换的简便性和准确性。例如,要将 milliseconds1/1000 秒)转换为 1/30 秒,必须将毫秒乘以 0.03。众所周知,您无法在计算机中精确表示 0.03。尽管如此,当发生这种情况时,round 会精确地(无舍入误差)检测平局并向偶数舍入。 diff0diff1 之间的差异不是近似值,而是精确的差异,即使 d 的单位是毫秒,而 To1/30 秒。 diff0diff1 的单位是 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));

想要使用 f 的浮点 duration 的客户端现在必须自己转换为整数 duration,然后再将结果传递给 f。

总而言之,f 的作者在他的接口方面拥有相当大的灵活性和控制权,可以为他的客户端提供他想要的接口,并且有简单的选项可以在他的函数内部操作该 duration

用户是否有可能将一个 duration 传递给一个单位不明确的函数?

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

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

这取决于表示法。默认的类型别名使用不处理溢出的表示法。用户可以定义自己的表示法,根据其应用程序的要求来管理溢出。

虽然 durations 只关心精度和表示法,但时钟和 time_points 密切相关并相互引用。由于时钟更容易解释,我们将先解释它们,而无需完全解释 time_points。一旦介绍了时钟,就更容易解释 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 框架随着新时钟的引入而继续工作。并且可以在同一个程序中安全轻松地处理多个时钟。

与时间 duration 相对,time_point 表示一个时间点。换句话说,time_point 表示一个纪元加上或减去一个 durationtime_point 的例子包括:

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

在上面的每个例子中,都隐含了一个不同的纪元。有时一个纪元可以持续数千年。其他时候,纪元的意义在一段时间后会丢失(例如计时器的开始,或计算机启动时)。但是,如果两个 time_point 被知晓共享同一个纪元,它们就可以相减,得到一个有效的 duration,即使纪元的定义不再有意义。

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

一个 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_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";
}

请注意,如果在使用两个时钟 time_point 之间的 duration 时,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

可以看出,每种 duration 类型都可以流式输出,而无需手动在运行时值之后流式输出编译时单位。当编译时单位被认为是“常用单位”时,会使用英文名称。对于“不常用单位”,单位名称由关联 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 毫秒”的流可以解析为秒,但如果流包含“3001 毫秒”,则解析为 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,其基于格里高利历,而不是其他日历。但是,由于 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 与相对于计算机本地时区的格式化 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.Chronosystem_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

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

您可以使用相同的操纵符与 istream 一起指定解析序列。

不幸的是,没有格式化/解析序列来指示分数秒。 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,然后解析 epoch 字符串。如果 epoch 字符串与 time_point::clock 关联的 epoch 字符串不匹配,则会设置 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 facet 相关。其设计遵循 C++ IOStreams 标准设计。

该库将 duration 的 locale 相关解析和格式化封装到一个新的 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,例如 sleep、在条件变量上等待或等待互斥锁。本文提出的 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 类结构的 Clock。

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 只要它们具有相同的时钟,并且它们的内部 durations 可以相互转换。我们还利用了具有浮点表示的 duration 可以从任何内容转换的事实。最后,I/O 系统为我们的 duration<double, ratio<3600>> 发现了更易读的“小时”单位。

有许多其他方法可以格式化 durationtime_point。例如,请参阅 ISO 8601。为了避免在 operator<< 中硬编码所有可能性,这即使对于最微小的用途也会导致显著的代码膨胀,本文档旨在告知读者如何在需要时编写自定义 I/O。

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

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

其中

  • ddays 的数量。
  • hhours 的数量。
  • mminutes 的数量。
  • ss.cc 是四舍五入到最接近百分之一秒的 seconds 的数量。
    1. include <boost/chrono/chrono_io.hpp>
    2. include <ostream>
    3. include <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。需要指定 clock 错误处理。

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




PrevUpHomeNext