クロノライブラリの基本概念(C ++)

時間を無次元の量として扱うと、測定単位の変換で誤解やエラーが発生する可能性があります。







-聞いて、覚えてないよ、私たちは睡眠中に秒またはミリ秒を送信しますか?



-くそー、それは私が私の時間に360秒を持っていることが判明した、私はゼロを逃した。

このようなエラーを回避するために、クロノライブラリ(名前空間std :: chrono)が提供されています。 C ++ 11で追加され、後の標準で最終決定されました。 今ではすべてが論理的です:







using namespace std::chrono; int find_answer_to_the_ultimate_question_of_life() { //  std::this_thread::sleep_for(5s); //5  return 42; } std::future<int> f = std::async(find_answer_to_the_ultimate_question_of_life); //  2.5  if (f.wait_for(2500ms) == std::future_status::ready) std::cout << "Answer is: " << f.get() << "\n"; else std::cout << "Can't wait anymore\n";
      
      





ライブラリには、次の概念が実装されています。









std ::比率



std :: ratio-コンパイル時の通常分数(m / n)を実装するテンプレートクラス。 これはchronoに属していませんが、このライブラリで積極的に使用されているため、最初にそれを知って、それ以上の質問を引き起こさないようにします。







 template< std::intmax_t Num, // std::intmax_t Denom = 1 // > class ratio;
      
      





分子と分母がテンプレートconstexprパラメーターであることが重要です。 これにより、コンパイル段階で型を形成できます。 このクラスは補助(純粋に静的なヘルパークラス)であり、一般的には数学的な計算を目的とするものではありません。 ユニットを効果的に翻訳するために必要です。 たとえば、距離の異なる単位を使用したい場合:







 template<class _Ratio> class Length { double length_; public: explicit Length(double length) : length_(length) { } double length() const { return length_; } }; Length<Mm> len1(127.0); Length<Inches> len2(5.0); Length<Mm> len3 = len1 + len2;
      
      





ミリメートルを基本単位とすると、次のようになります。







 using Mm = std::ratio<1>; // == 1 //    ,   : using Inches = std::ratio<254, 10>; using Metre = std::ratio<1000, 1>;
      
      





コンストラクターでは、ベースユニットへの変換を実行できました。 ただし、この変換が必要な場合にのみより正確です。 メートルからミリメートルへの変換は、丸め中の損失を恐れることなく変換できるためです。







上記に関連して、完全を期すために、加算操作の最も成功した実装ではなく、単純な実装を示します。







 template<class _Ratio1, class _Ratio2> Length<Mm> operator+(const Length<_Ratio1> &left, const Length<_Ratio2> &right) { double len = left.length() / _Ratio1::den * _Ratio1::num + right.length() / _Ratio2::den * _Ratio2::num; return Length<Mm>((int)len); }
      
      





メートルとキロメートルを追加するときにメートルを取得するのが正しいでしょう。







期間-時間間隔



テンプレートクラスstd :: chrono :: durationは、時間間隔タイプです。 クロノの時間間隔は、一定の期間数です(元のティック期間内)。 この数値は、 int64_t



float



などのタイプによって特徴付けられます。 期間の長さは秒単位で測定され、std :: ratioを使用して自然な分数として表されます。







いくつかの一般的な間隔は既にライブラリで定義されています。 タイプは実装によって若干異なる場合があります。







 using nanoseconds = duration<long long, nano>; using microseconds = duration<long long, micro>; using milliseconds = duration<long long, milli>; using seconds = duration<long long>; using minutes = duration<int, ratio<60> >; using hours = duration<int, ratio<3600> >; // nano, micro, milli: using nano = ratio<1, 1000000000>; using micro = ratio<1, 1000000>; using milli = ratio<1, 1000>;
      
      





ただし、独自に定義できます。







 using namespace std::chrono; //3-   using Hourglass = duration<long, std::ratio<180>>; // using Hourglass = duration<long, std::ratio_multiply<std::ratio<3>, minutes::period>>; //      2.75  using MyTimeUnit = duration<long, std::ratio<11, 4>>; //  .   using fseconds = duration<float>; // -   using seconds16 = duration<uint16_t>;
      
      





次に、それらの使用方法。 暗黙的な初期化は禁止されています:







 seconds s = 5; // void foo(minutes); foo(42); //
      
      





明示的のみ:







 seconds s{8}; void foo(minutes); foo(minutes{42});
      
      





ところで、ブレースが使用される理由については、たとえばここを読むことができます 。 要するに:不可逆積分型の暗黙的な変換を避けるため。 T x(F());



別のケースを追加しT x(F());



xを初期化する代わりに、タイプF(*)()



関数へのポインターを取り、 T



を返す関数を宣言するものとして解釈されますT



解決策: T x{F()};



またはT x((F()));









C ++ 14は、基本単位のカスタムリテラルを追加します。







 seconds s = 4min; void foo(minutes); foo(42min);
      
      





以下を追加、減算、比較できます。







 seconds time1 = 5min + 17s; minutes time2 = 2h - 15min; bool less = 59s < 1min;
      
      





上記の例のように、時間を分から秒、分から秒、秒からミリ秒などに暗黙的に変換できますが、その逆はできません。







 minutes time3 = 20s; //   seconds time4 = 2s + 500ms; //  
      
      





一般に、周期比が整数の場合、整数型の暗黙的な変換が許可されます。







 //(20/15) / (1/3) = 4. ! duration<long, std::ratio<1, 3>> t1 = duration<long, std::ratio<20, 15>>{ 1 };
      
      





それ以外の場合、丸めと浮動小数点型への変換の2つの方法があります。







 //   -     minutes m1 = duration_cast<minutes>(-100s); //-1m //C++17.      minutes m2 = round<minutes>(-100s); //-2m //C++17.      minutes m3 = ceil<minutes>(-100s); //-1m //C++17.      minutes m4 = floor<minutes>(-100s); //-2m
      
      





2番目のオプション:







 using fminutes = duration<float, minutes::period>; fminutes m = -100s;
      
      





タイプuint64_tの秒数の冗長表現があるとします。 OK:







 using seconds16 = duration<uint16_t, seconds::period>; seconds16 s = 15s;
      
      





しかし、あなたはまだオーバーフローを恐れています。 ライブラリのクラスを使用して、安全に数字を操作できます。 標準にはそのような標準はありませんが(提案のみ)、サードパーティの実装があります。 VSにもあります、私たちはそれを使用します:







 #include <safeint.h> using sint = msl::utilities::SafeInt<uint16_t>; using safe_seconds16 = duration<sint, seconds::period>; safe_seconds16 ss = 60000s; try { ss += 10000s; } catch (msl::utilities::SafeIntException e) { // };
      
      





画面またはファイルに間隔値を表示するには、count()を使用する必要があります。







 seconds s = 15s; std::cout << s.count() << "s\n";
      
      





ただし、内部変換にはcountを使用しないでください!







time_point-時点



time_pointクラスは、時刻を表すことを目的としています。 時間の瞬間は、特定の基準点から開始する任意のタイマーで測定される時間間隔として特徴付けることができます。 たとえば、ストップウォッチを使用してスープを調理している場合、時刻は次のように表すことができます。







 0 :      420 :   1300 : 
      
      





壁掛け時計の分針の場合、同じ時点は次のようになります。







 17 :      24 :   39 : 
      
      





クラス自体:







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





時間間隔のタイプはすでによく知られているので、クロックタイマーに移りましょう。 ライブラリには3つのタイマーがあります。







  1. system_clock-システム時間を表します。 通常、このタイマーは、測定中にユーザーまたは同期プロセスによって時間を変更できるため、間隔の測定には適していません。 通常、1970年1月1日からの経過時間に基づいていますが、これは指定されていません。
  2. steady_clock-いわゆる安定したクロックを表します。つまり、そのコースは外部の変更の影響を受けません。 間隔の測定に適しています。 通常、その実装は、スイッチオン後のシステムの稼働時間に基づいています。
  3. high_resolution_clock-システムで使用可能な最小のサンプル期間を持つタイマー。 考えられているものの1つのエイリアスである可能性があります(ほぼ間違いなく、steady_clockです)。


クロックには静的変数is_steady



あり、これによりタイマーが単調かどうかを確認できます。 Clockには現在関数があり、time_pointの形式で現在の時刻を返します。 time_point



クラスのオブジェクトtime_point



あまり興味深いものではありません。その起源の瞬間は特定ではなく、ほとんど意味をなさないからです。 ただし、時間間隔を追加して、他の時点と比較することができます。







 time_point<steady_clock> start = steady_clock::now(); // steady_clock::time_point start = steady_clock::now(); // auto start = steady_clock::now(); foo(); if (steady_clock::now() < start + 1s) std::cout << "Less than a second!\n";
      
      





time_point



time_point



に追加することはできませんが、減算することはできます。これは時間の追跡に役立ちます。







 auto start = steady_clock::now(); foo(); auto end = steady_clock::now(); auto elapsed = duration_cast<milliseconds>(end - start);
      
      





カウントダウンの開始からの時間間隔を取得するには、 time_since_epoch



を呼び出しtime_since_epoch









 auto now = system_clock::now(); system_clock::duration tse = now.time_since_epoch();
      
      





time_point



を数値に変換する(たとえば、シリアル化または表示用)には、Cタイプのtime_tを使用します。







 auto now = system_clock::now(); time_t now_t = system_clock::to_time_t(now); auto now2 = system_clock::from_time_t(now_t);
      
      





結論の代わりに



最も一般的な質問:時間と日付を読みやすい形式で表示する方法。 クロノでは、何もありません。 time_tで遊ぶか、クロノ開発者の別のライブラリを使用できます。








All Articles