如何创建自定义时钟以供在std :: chrono函数中使用?

32

我有一个任意的历元,比如1988年7月13日。实质上,我想相对于这个时间来测量时间。我正在考虑编写一个自定义的时钟类,这样我就可以像这样编写代码:

using std::chrono;
time_point<My_Clock> tp;
std::cout << duration_cast<seconds>(tp.time_since_epoch()).count() << std::endl;

这是可能的吗?如果不行,最干净的实现方式是什么?


我敢打赌标准中列出了要求。 - chris
time_point接受Clock作为模板,这是一个概念。你可以自己编写一个满足该特定概念要求的类。但不要完全相信我的话,我只是在胡思乱想。 - Daniel Kamil Kozar
1
@DanielKamilKozar 我查看了system_clock的源代码以获取此类示例,但是我没有取得太大进展。我没有看到实际纪元在哪里指定。我认为这取决于系统。我应该在哪里找到如何执行此操作的文档? - sagargp
1个回答

46
写这个自定义时钟的难点在于如何编写它的now()函数。在下面的示例中,我以system_clocknow()为基础来编写now()函数。首先,我进行了一些侦探工作,发现我的system_clock从1970年元旦开始计时,忽略闰秒。这被称为Unix时间。事实证明,我所知道的每个实现(我想我已经检查过它们所有)都有相同的起始时间(但这在C++11标准中没有指定)。

接下来,我计算出1988年07月13日是1970年01月01日之后的6768天。有了这两个事实,剩下的就很容易了:

#include <chrono>

struct My_Clock
{
    typedef std::chrono::seconds              duration;
    typedef duration::rep                     rep;
    typedef duration::period                  period;
    typedef std::chrono::time_point<My_Clock> time_point;
    static const bool is_steady =             false;

    static time_point now() noexcept
    {
        using namespace std::chrono;
        return time_point
          (
            duration_cast<duration>(system_clock::now().time_since_epoch()) -
            hours(6768*24)
          );
    }
};

MyClock需要嵌套的typedef来描述它的durationrepperiodtime_point。根据您的问题,我选择了seconds作为duration,但您可以选择任何您想要的。

对于now()函数,我只需调用system_clock::now()并以秒为单位减去时代。我通过以MyClock::duration的形式编写计算中的所有内容变得更加聪明,以便更轻松地更改duration。请注意,我能够以hours的形式减去时代,它隐式转换为duration(即seconds)。或者,我也可以构建自定义的duration为天数:

typedef std::chrono::duration<int, std::ratio_multiply<std::chrono::hours::period,
                                                       std::ratio<24>>> days;

接着now()的返回值可以这样写:

        return time_point
          (
            duration_cast<duration>(system_clock::now().time_since_epoch()) -
            days(6768)
          );

无论如何,现在你可以像这样使用它:

#include <iostream>

int
main()
{
    using namespace std::chrono;
    time_point<My_Clock> tp = My_Clock::now();
    std::cout << tp.time_since_epoch().count() << '\n';
}

对我来说,它只是打印出来了:

786664963

这表明今天(2013-06-16)距离1988-07-13大约已经过去了24.9年。

C++20的更新

在C++20中,您可以使用更新的<chrono>日历服务来消除实现中的“神奇数字”:

#include <chrono>

struct My_Clock
{
    typedef std::chrono::seconds              duration;
    typedef duration::rep                     rep;
    typedef duration::period                  period;
    typedef std::chrono::time_point<My_Clock> time_point;
    static const bool is_steady =             false;

    static time_point now() noexcept
    {
        using namespace std::chrono;
        return time_point
          {
            duration_cast<duration>(system_clock::now() - sys_days{July/13/1988})
          };
    }
};

这种便利不会增加运行时间的开销。子表达式sys_days{July/13/1988}在编译时被优化为自system_clock纪元以来的天数计数,然后进一步转换为system_clock::duration(仍然在编译时)并从system_clock::now()中减去。


1
从C++20开始,标准为system_clock指定了Unix纪元(1970-01-01)。 - John Gordon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接