【问题标题】:Persisting std::chrono time_point instances持久化 std::chrono time_point 实例
【发布时间】:2014-04-13 01:04:48
【问题描述】:

持久化 std::chrono time_point 实例,然后将它们读回另一个相同类型的实例的正确方法是什么?

   typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;

   time_point_t tp = std::chrono::high_resolution_clock::now();
   serializer.write(tp);
   .
   .
   .
   time_point_t another_tp;
   serializer.read(another_tp);

对写入/读取的调用,假设类型为 time_point_t 的实例可以以某种方式转换为字节表示,然后可以从磁盘或套接字等写入或读取。

Alf 建议的可能解决方案如下:

   std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

   //Generate POD to write to disk
   unsigned long long ns0 = t0.time_since_epoch().count();

   //Read POD from disk and attempt to instantiate time_point
   std::chrono::high_resolution_clock::duration d(ns0)

   std::chrono::high_resolution_clock::time_point t1(d);

   unsigned long long ns1 = t1.time_since_epoch().count();

   if ((t0 != t1) || (ns0 != ns1))
   {
      std::cout << "Error time points don't match!\n";
   }

注意:上面的代码有一个bug,因为最终实例化的时间点与原来的不匹配。

在旧样式 time_t 的情况下,通常只是根据其 sizeof 将整个实体写入磁盘,然后以相同的方式将其读回 - 简而言之,与新的 std::chrono 类型等效的是什么?

【问题讨论】:

    标签: c++ serialization c++11 persistence chrono


    【解决方案1】:

    从磁盘或套接字读取意味着您可能正在读取未执行写入的应用程序实例。而在这种情况下,仅序列化持续时间是不够的。

    time_point 是自未指定时期以来的duration 时间量。时代可以是任何东西。在我的计算机上,std::chrono::high_resolution_clock 的时代是计算机启动时。 IE。此时钟报告自启动以来的纳秒数。

    如果一个应用程序写入 time_since_epoch().count(),计算机重新启动,然后另一个(或什至相同)应用程序将其读回,则读入的值没有任何意义,除非你碰巧以某种方式知道两次启动之间的时间量。

    要可靠地序列化time_point,必须安排作者和读者就某个时期达成一致,然后确保写入和读取的time_point 与该时期相关。例如,可以安排使用 POSIX 纪元:New Years 1970 UTC。

    事实证明,我知道的每个std::chrono::system_clock 实现都使用Unix time,这是从1970 年开始测量的UTC 的近似值。但是我知道std::chrono::high_resolution_clock 没有共同的时代。

    只有当您能以某种方式确保读取器和写入器时钟在一个共同的纪元上达成一致时,您才能将 time_point 序列化为持续时间。

    【讨论】:

      【解决方案2】:

      time_point 构造函数采用duration,您可以从成员time_since_epoch 获得duration。因此问题简化为序列化duration 值。并且duration 有一个构造函数,它接受多个滴答声,以及一个成员函数count,它产生滴答声的数量。

      这一切都只是通过谷歌搜索 std::chrono::time_point 并查看谷歌让我找到的 cppreference 文档。

      阅读 documentation 通常是个好主意。


      附录:一个例子。

      #include <chrono>
      #include <iostream>
      #include <typeinfo>
      using namespace std;
      
      auto main() -> int
      {
          using Clock = chrono::high_resolution_clock;
          using Time_point = Clock::time_point;
          using Duration = Clock::duration;
      
          Time_point const t0 = Clock::now();
      
          //Generate POD to write to disk
          Duration::rep const ns0 = t0.time_since_epoch().count();
      
          //Read POD from disk and attempt to instantiate time_point
          Duration const d(ns0);
          Time_point const t1(d);
      
          cout << "Basic number type is " << typeid( ns0 ).name() << "." << endl;
          if( t0 != t1 )
          {
              cout << "Error time points don't match!" << endl;
          }
          else
          {
              cout << "Reconstituted time is OK." << endl;
          }
      }
      

      Visual C++ 12.0 报告的基本类型是__int64,即long long,而Windows 中的g++ 4.8.2 报告的类型是x,大概意思相同。

      对于这两个编译器,重构时间与原始时间相同。

      附录:与 cmets 中的 noted by Dina 一样,从 C++14 开始,C++ 标准没有指定纪元,因此要使其跨机器或使用不同的时钟工作,有必要添加额外的步骤来规范化序列化数据的纪元,例如最自然的是Posix time,即自 1970 年 1 月 1 日星期四 00:00:00 协调世界时 (UTC) 以来的时间。

      【讨论】:

      • @Alf 所以你的意思是,必须从时钟到 time_point 到持续时间到 pod 到磁盘/存储,然后从磁盘/存储到 pod 到持续时间到 time_point - 这是正确的吗?
      • 是的。除了“必须”可能太强了。但这就是我要做的。 ;-)
      • @Alf 我已经尝试了基于您在问题中指出的方法的可能解决方案,但是它似乎返回了不同值的 time_point 类型。
      • @ZamfirKerlukson:奇怪。你的代码(带有duration_cast)对我来说很好用。但是,我怀疑可能是那个演员干扰了:你不需要它,因为count 已经产生了一个内置类型的值。
      • 据我了解,每个时钟的纪元可能不同,标准未指定。具体来说,它不必在不同的机器/系统中相同。如果您在同一台机器上进行序列化和反序列化,这将起作用,但不能保证在不同机器之间工作。 (基于我对informit.com/articles/article.aspx?p=1881386&seqNum=2 的评论)
      猜你喜欢
      • 1970-01-01
      • 2020-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-05
      相关资源
      最近更新 更多