クロノライブラリの基本概念(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_tfloatなどのタイプによって特徴付けられます。 期間の長さは秒単位で測定され、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_pointtime_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で遊ぶか、クロノ開発者の別のライブラリを使用できます。



Source: https://habr.com/ru/post/J324984/


All Articles