C++ 11では、std::localtime
を印刷するには、std::gmtime
およびstd::chrono::time_point
を間接指定として使用する必要があります。これらの関数は、内部静的構造体へのポインタを返すため、C++ 11で導入されたマルチスレッド環境で安全に使用できません。これは、C++ 11が同じ理由でほとんど使用できない便利な関数std::put_time
を導入したため、特に面倒です。
なぜこれが根本的に壊れているのか、何かを見落としているのですか?
N2661 によると、<chrono>
を追加した論文:
このペーパーでは、Cの
time_t
との間の最小限のマッピングを除き、カレンダーサービスを提供していません。このペーパーでは日付/時刻ライブラリの提案もエポックの指定も行っていないため、うるう秒にも対応していません。ただし、日付/時刻ライブラリは、これを構築するための優れた基盤であると判断します。
このホワイトペーパーでは、汎用物理量ライブラリを提案していません。
このホワイトペーパーでは、将来、一般的な物理ユニットライブラリの互換性のある開始点を提供できる強固な基盤を提案します。このような将来のライブラリはいくつかの形式のいずれかをとる可能性がありますが、現在の提案は、実際に物理ユニットライブラリであるということを十分にやめません。この提案は時間に固有のものであり、スレッドライブラリの時間に関連するニーズに引き続き動機付けられています。
この提案の主な目標は、標準ライブラリスレッド化APIのニーズを、使いやすく、安全で、効率的で、10年後または100年後でも時代遅れにならないほど柔軟な方法で満たすことです。この提案に含まれるすべての機能は、モチベーションとしての実用的なユースケースを持つ特定の理由のためにここにあります。 「クール」のカテゴリに分類されるもの、「便利だと思われるもの」、「このインターフェースでは必要ではないが非常に役立つもの」は含まれていません。そのようなアイテムは、他の提案に登場する可能性があり、場合によってはTRをターゲットにします。
<chrono>
の主要な目標は「カレンダーライブラリサービスを必要としない標準ライブラリスレッドAPIのニーズを満たすこと」であることに注意してください。
localtime
およびgmtime
には静的な内部ストレージがあります。つまり、スレッドセーフではありません(データ構造へのポインターを返す必要があるため、動的に割り当てる必要があります。静的な値またはグローバル値-動的に割り当てるとメモリがリークするため、合理的な解決策ではありません。つまり、グローバル変数または静的変数でなければなりません[理論的には、TLSで割り当てて保存し、そのようにスレッドセーフにできます])。
ほとんどのシステムにはスレッドセーフの代替がありますが、それらは標準ライブラリの一部ではありません。たとえば、Linux/Posixにはlocaltime_r
およびgmtime_r
、結果の追加パラメーターを受け取ります。例を参照してください http://pubs.opengroup.org/onlinepubs/7908799/xsh/gmtime.html
同様に、Microsoftライブラリにはgmtime_s
もリエントラントであり、同様の方法で動作します(出力パラメーターを入力として渡します)。 http://msdn.Microsoft.com/en-us/library/3stkd9be.aspx を参照してください
標準C++ 11ライブラリがこれらの関数を使用しない理由は何ですか?その仕様を書いた人々に尋ねなければならないだろう-私はそれが移植性と便利さを期待しているが、私は完全に確信しているわけではない。
std::localtime
とstd::gmtime
に代わるスレッドセーフな代替手段はありません。なぜなら、あなたはそれを提案し、標準化プロセス全体を通してそれをマーシャリングしなかったからです。そして、誰もしませんでした。
chrono
sカレンダーコードのみは、既存のtime_t
関数をラップするコードです。新しいものの標準化または作成は、chrono
プロジェクトのドメイン外でした。このような標準化を行うには、より多くの時間と労力が必要になり、依存関係が増えます。各time_t
関数を単純にラップすることは簡単で、依存関係がほとんどなく、迅速でした。
彼らは自分の努力を狭く集中しました。そして、彼らは焦点を当てることに成功しました。
<calendar>
の作業を開始するか、std
の堅牢なカレンダーAPIを作成するこのような取り組みに参加することをお勧めします。幸運とゴッドスピード!
無料のオープンソースのサードパーティライブラリ を使用する場合は、UTCでstd::chrono::system_clock::time_point
を出力する方法を次に示します。
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << system_clock::now() << " UTC\n";
}
これは、最新のC++構文を使用するstd::gmtime
のスレッドセーフな代替手段です。
最新のスレッドセーフstd::localtime
置換には、これと密接に関連する 高レベルタイムゾーンライブラリ が必要です。構文は次のようになります。
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << make_zoned(current_zone(), system_clock::now()) << "\n";
}
これらは両方とも、system_clock
がサポートする精度で出力されます。例:
2016-07-05 10:03:01.608080 EDT
(macOSではマイクロ秒)
これらのライブラリは、gmtime
およびlocaltime
の置換にとどまりません。たとえば、ユリウス暦で現在の日付を表示しますか?
#include "julian.h"
#include <iostream>
int
main()
{
using namespace std::chrono;
std::cout << julian::year_month_day(date::floor<date::days>(system_clock::now())) << "\n";
}
2016-06-22
現在のGPS時間はどうですか?
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
std::cout << std::chrono::system_clock::now() << " UTC\n";
std::cout << gps_clock::now() << " GPS\n";
}
2016-07-05 14:13:02.138091 UTC
2016-07-05 14:13:19.138524 GPS
https://github.com/HowardHinnant/date
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0355r0.html
更新
「date.h」および「tz.h」ライブラリは、C++ 2a仕様のドラフトに含まれており、非常に小さな変更が加えられており、「a」が「0」であることを期待しています。それらはヘッダー<chrono>
とnamespace std::chrono
の下にあります(date namespace
はありません)。
他の人が言及したように、利用可能なC++標準にはスレッドセーフの利便性とポータブルな時間フォーマットアプローチはありませんが、いくつかの古くからあるプリプロセッサ技術が使用可能です( CppCon 2015 slide 17&のAndrei Alexandrescuのおかげです) 18):
std::mutex gmtime_call_mutex;
template< size_t For_Separating_Instantiations >
std::tm const * utc_impl( std::chrono::system_clock::time_point const & tp )
{
thread_local static std::tm tm = {};
std::time_t const time = std::chrono::system_clock::to_time_t( tp );
{
std::unique_lock< std::mutex > ul( gmtime_call_mutex );
tm = *std::gmtime( &time );
}
return &tm;
}
#ifdef __COUNTER__
#define utc( arg ) utc_impl<__COUNTER__>( (arg) )
#else
#define utc( arg ) utc_impl<__LINE__>( (arg) )
#endif
ここでは、size_t
テンプレート引数を使用して関数を宣言し、静的メンバーstd::tm
へのポインターを返します。これで、異なるテンプレート引数を使用してこの関数を呼び出すたびに、新しい静的std::tm
変数を使用して新しい関数が作成されます。 __COUNTER__
マクロが定義されている場合は、使用するたびにインクリメントされた整数値で置き換える必要があります。そうでない場合は、__LINE__
マクロを使用します。この場合、マクロutc
1行に2回。
グローバルgmtime_call_mutex
は、各インスタンス化でスレッドセーフではないstd::gmtime
呼び出しを保護します。少なくともLinuxでは、ロックの取得は最初にスピンロックの周囲で実行されるため、パフォーマンスの問題になることはありません。本物のスレッドロックでアップ。
thread_local
は、utc
呼び出しで同じコードを実行するさまざまなスレッドが、さまざまなstd::tm
変数で機能することを保証します。
使用例:
void work_with_range(
std::chrono::system_clock::time_point from = {}
, std::chrono::system_clock::time_point to = {}
)
{
std::cout << "Will work with range from "
<< ( from == decltype(from)()
? std::put_time( nullptr, "beginning" )
: std::put_time( utc( from ), "%Y-%m-%d %H:%M:%S" )
)
<< " to "
<< ( to == decltype(to)()
? std::put_time( nullptr, "end" )
: std::put_time( utc( to ), "%Y-%m-%d %H:%M:%S" )
)
<< "."
<< std::endl;
// ...
}