2015-01-26

C++11の時間ライブラリ: chrono

<chrono>は、C++11で追加された時間ライブラリである。

単位時間を扱うためのduration、起点からの経過時間を扱うためのtime_point、現在の起点からの経過時間を取得するためのclockからなる。Cの標準ライブラリのtime_tとtime(), clock_gettime()を置き換えることが出来る。日付機能は含まれていない。

duration

時間について考える。一時間は60分である。1分は60秒である。1秒は1000ミリ秒である。

単位の異なる時間の値を相互に変換するのは、簡単な計算だ。

unsigned int min_to_sec( unsigned int min )
{
    return min * 60 ;
}

しかし、実引数minに渡される値の単位が分であることを保証する方法はない。間違えたとしても、コンパイルエラーにはならない。

chronoでは、時間単位を扱うライブラリ、durationを追加した。これは型安全に時間の計算をしてくれる。


#include 

int main()
{
    // 15分
    std::chrono::minutes min(15) ;

    // 分を秒に変換
    std::chrono::seconds sec = min ;

    // 900
    std::cout << sec.count() << std::endl ;

    // エラー、余りが発生する可能性があるため
    min = sec ;

    // OK
    min = std::chrono::duration_cast<std::chrono::minutes>( sec ) ;
}

durationテンプレートには、よく使う時間単位、hours, minutes, seconds, miliseconds, microseconds, nanosecondsというtypedef名があらかじめ宣言されている。

また、C++14からは、時間単位のtypedef名へのユーザー定義リテラルが定義されている。それぞれ、h, min, s, ms, us, nsとなっている。

auto hours = 3h ;
auto seconds = 100s ;

auto sum = 1h + 5min + 3s ;

durationクラスは、メンバー関数countにより、内部表現の値を得ることができる。単位はdurationテンプレートの特殊化のとおりだ。

int main()
{
    std::chrono::seconds s(10) ;

    s.count() ; // 10

    auto s2 = s + s ;

    s2.count() ; // 20

    std::chrono::hours h(1) ;
    h.count() ; // 1

    s = h ;

    s.count() ; // 3600
}

time_point

time_pointは、ある起点時間からの経過時間を表現するクラスだ。time_point同士を減算すると、その結果はdurationになる。time_pointを直接構築することはあまりない。time_pointは、clockから得ることができる。C標準ライブラリのtime_tに比べて、型安全になっている。

clock

clockは、現在のtime_pointを取得するクラスだ。staticメンバー関数のnowでtime_pointを取得できる。

int main()
{
    // 処理前の起点からの経過時間
    auto t1 = std::chrono::system_clock::now() ;

    // 処理
    std::this_thread::sleep_for( std::chrono::seconds(1) ) ;

    // 処理後の起点からの経過時間
    auto t2 = std::chrono::system_clock::now() ;

    // 処理の経過時間
    auto elapsed = t2 - t1 ;

    // 単位は未規定
    std::cout << elapsed.count() << std::endl ;
}

system_clockは、システム上のリアルタイムクロックを表現するclockである。

このクロックの使うdurationは未規定である。そのため、経過時間を実際の時間単位で知りたければ、duration_castが必要になる。

int main()
{
    // 処理前の起点からの経過時間
    auto t1 = std::chrono::system_clock::now() ;

    // 処理
    std::this_thread::sleep_for( std::chrono::seconds(1) ) ;

    // 処理後の起点からの経過時間
    auto t2 = std::chrono::system_clock::now() ;

    // 処理の経過時間をミリ秒で取得
    auto elapsed = std::chrono::duration_cast< std::chrono::milliseconds >(t2 - t1) ;

    // 単位はミリ秒
    std::cout << elapsed.count() << std::endl ;
}

C++規格は、起点時間がいつなのかを規定していない。経過時間はtime_pointのメンバー関数tiem_since_epochで取得できる。また、system_clockから得られるtiem_pointは、time_tに変換できる。

int main()
{
    // time_point
    auto t1 = std::chrono::system_clock::now() ;
    // 起点時間からの経過時間
    std::cout << t1.time_since_epoch().count() << '\n' ;

    // time_t
    auto t2 = std::chrono::system_clock::to_time_t( t1 ) ;
    std::cout << t2 << '\n' ;

    // tme_point
    auto t3 = std::chrono::system_clock::from_time_t( t2 ) ;

    std::cout << t3.time_since_epoch().count() << std::endl ;
}

system_clockのtime_pointとtime_tが、同じ時間単位の分解能を使っているとは限らない。

clockはsystem_clockだけではない。他にも、steady_clockがある。これは、実時間の経過によって、time_pointの経過時間が減らないことが保証されている。

int main()
{
    // time_point
    auto t1 = std::chrono::steady_clock::now() ;

    // この間にシステムの時刻が過去に変更されるかもしれない


    auto t2 = std::chrono::steady_clock::now() ;


    // trueであることが保証されている
    bool b = t2 >= t1 ;
}

その他のclockにも、constexpr staticデータメンバーのis_steadyの値によって、steady_clockと同じ保証があるかどうかを確かめることができる。

// true/false
bool b = std::chrono::system_clock::is_steady ;

C++規格はもうひとつ、high_resolution_clockを規定している。これは、時間の分解能が高いclockであると規定されている。

auto t1 = std::chrono::high_resolution_clock::now() ;
// 処理
auto t2 = std::chrono::high_resolution_clock::now() ;

// 処理のかかった時間
auto e = t2 - t1 ;

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2 comments:

Anonymous said...

『1分は60秒である』
C(ISO/IEC 9899:2011)ではそんなことは無いのですがC++だとそうなんですか?

Anonymous said...

単位時間を扱うためのduration、起点からの経過時間を扱うためのtime_point、現在の起点からの経過時間を取得するためのclockからなる

いくら何でもこれは無茶苦茶です。お疲れでしょうか