C++11 STL 的時間函式庫:chrono

| | 0 Comments| 16:34
Categories:

這篇算是簡單介紹一下,C 11 的標準函式庫裡面,用來處理時間和日期的函式庫「chrono」。這個函式庫是在 C 11 才引進的,以微軟的 Visual C 來說,要到 2012 版才開始提供;而如果使用的開發環境沒有支援的話,也可以使用 Boost C Libraries 所提供的版本。

Chrono 這個函式庫,主要是為 C STL 加入一個可以取得、處理時間與日期的物件導向函式庫;透過這個函式庫,我們可以在程式裡面取得時間、並針對時間做處理、計算。而像是 STL Threadsleep_for()sleep_until(),也都是透過 chrono 提供的型別來做設定。

要使用這個函式庫,要 include 他的 header 檔,也就是加上

#include <chrono>

之後用的東西,基本上都是在 std:chrono 這個 namespace 下。

使用 chrono 時,最主要應該是下面這兩種用來記錄時間的類別:

  • 紀錄時間點的 time_point
  • 紀錄時間長度的 duration

duration

duration 是 chrono 裡面,用來記錄時間長度的類別,他基本上是一個 template class,可以自行定義他的意義;chrono 也有提供一些比較常見的時間類別,可以直接拿來使用,下面就是內建的 duration 的型別:

typedef duration<long long, nano> nanoseconds;
typedef duration<long long, micro> microseconds;
typedef duration<long long, milli> milliseconds;
typedef duration<long long> seconds;
typedef duration<int, ratio<60> > minutes;
typedef duration<int, ratio<3600> > hours;

其中可以看到,第一個 template 參數是要用來儲存資料的型別,第二個則是他相對於「秒」的比例。這邊也使用了 ratio 這個 C 11 的另一個新的函式庫的類別,他是用來記錄「有理數」(可以寫成分數的數)的新類別,有興趣可以參考 cppreference 的介紹

基本上,一般會用到時間單位這邊都有定義好了,如果不合用的話,也可以自己去定義;而由於 chrono 也有把相關的計算都定義了,所以也可以直接拿來做計算,就算是時間單位不同,也不會有問題。

下面就是一個簡單的例子:

std::chrono::minutes t1( 10 );
std::chrono::seconds t2( 60 );
std::chrono::seconds t3 = t1 - t2;
std::cout << t3.count() << " second" << std::endl;

其中,t1 是代表 10 分鐘、 t2 是代表 60 秒,t3 則是 t1 減去 t2,也就是 600 – 60 = 540 秒。

而如果要取得一個 duration 的值的話,則是要呼叫他的 count() 這個函式;像在上面的例子裡面,就會把 t3 的值輸出,所以最後會出現「540 second」。

而如果想要做強制的時間單位轉換,也可以使用 duration_cast<>() 這個函式來做;下面就是一個把以秒為單位的 t3 轉換成分鐘後再輸出。

cout << chrono::duration_cast<chrono::minutes>( t3 ).count() << endl;

time_point

相較於 duration 是用來紀錄時間的長度的,time_point 是用來記錄一個特定時間點的資料類別。他一樣是一個 template class,需要指定要使用的 clock 與時間單位(duration)。

Chrono 一般來說有提供兩種 clock 可以使用,分別是:system_clocksteady_clock。其中 system_clock 是直接去抓系統的時間,有可能在使用中會被被修改(參考);而 steady_clock 則是確實地去紀錄時間的流逝,所以不會出現時間倒退的狀況(參考)。

一般要使用的話,大概會是下面的樣子:

std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
std::cout << "Hello World\n";
std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();
std::cout << "Printing took "
<< std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()
<< "us.\n";

透過 clock 類別所提供的 now() 這個函式,可以快速地取得現在的時間;而兩者相減的話,則會產生一個型別為 duration 的結果;在上面的例子裡面,就是一開始先取得當下的時間 t1,然後輸出一個字串後、再去取得一個時間 t2,之後兩者相減,就可以取得中間過程所花費的時間了。在這邊則是在相減後,把結果轉換成以 micro second 為單位後,再做輸出。

time_point 也可以和 duration 做計算,得出新的 time_point;例如下面的程式碼,就是計算 10 個小時候的時間:

std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::chrono::system_clock::time_point nt = now std::chrono::hours(10);

另外,chrono 也有定義 high_resolution_clock,提供更高的精確度(參考);但是實際上在 MSVC11 上,他就等同於 system_clock

而如果要把不同定義的 time_point 做轉換,則也可以使用 time_point_cast<>() 這個函式來處理,不過這邊就不多加說明了。


time_point 的輸出

STL 的 chrono 並沒有定義 time_point 的輸出方式,所以我們並不能直接透過 output stream 來輸出 time_point 的資料,所以如果要把他輸出成字串的話,其實還有點麻煩…

如果想要輸出的話,一個方法是透過 clock 提供的 to_time_t() 這個函式,把 time_point 先把他轉換成 C-style 的 time_t,然後再透過 ctime() 這類的函式做輸出;下面是一個簡單的範例:

std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t( now );
std::cout << std::ctime( &now_c ) << std::endl;

而如果是使用 Boost 的版本的話,Boost 則是另外有提供 chrono_io.hpp 這個檔案,在裡面替 durationtime_point 定義了輸出的格式,可以直接使用,相當地方便~有興趣的話,可以參考 Boost 的官方說明


參考資料:

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *