2014-10-pre-Urbana mailingsのレビュー : N4130-N4137
引き続き、2014-10-pre-Urbana mailingsを解説していく。
[Thou shalt not use PDF] N4130: Pad Thy Atomics
「汝のアトミックを詰めよ」と題された論文。
現在、atomicのデフォルトコンストラクターは、たとえオブジェクトが非トリビアルなコンストラクターを持っていようとも、オブジェクトの型としては未初期化の状態にすると規定されている。これはC言語との互換性のためだが、C++のクラス型も、未初期化の状態になってしまう。
これをCの互換性を保ったまま解決しようとすると、atomic型だけPODなのにPODではないとか、あるいは逆にPODではないのにPODであるなどとという例外的な状態にしなければならない。
未初期化で何が問題になるかといえば、padding bitsだ。未初期化の状態では、padding bitの状態が未規定である。では、たとえばatomic_compare_exchangeはpadding bitに対してどう振る舞うのか。結局、多くの実装ではatomicにはハードウェア支援されたロックフリーな機能を使うのであるからして、padding bitsも比較の際に考慮される実装がほとんどだろう。しかし、規格上はその挙動を定義していない。さてどうするか。
論文ではpadding bitsの状態が未規定であることによる弊害を列挙し、また問題を解決する提案をいくつか挙げているが、結論は出していない。
N4131: Another response to N4074; explicit should never be implicit
N4074で、return {expr}は、暗黙にexplicitコンストラクターを呼べるようにしようと提案されたが、それに対して、explicitは暗黙的になるべきではないとする反論。
N4074の提案では、以下のコードが合法になる。
struct Type1 {
explicit Type1 (int);
};
Type1 example_f1 () {
return { 0 };
}
曰く、explicitの意味は、「そういう初期化はできるぜ。でもたぶんお前の思っている結果にはならないぜ。マジでやりたいなら手を動かせ」という意味である。もし、利用者が本当にその初期化を望むのならば、明示的に書かなければならない。
曰く、そもそも現在のリスト初期化は、初期化の文法の統一を目的としたものであって、初期化の明示的なルールの迂回方法を目的としたものではない。
曰く、リスト初期化にはNarrowing Conversionがあるのに、なぜ制限を緩くするのだ。
曰く、return文は、そのオペランドであるexpression-or-braced-init-listを関数の戻り値として返すのではない。関数の戻り値の型の初期化子として使うのだ。提案では、関数の戻り値の型と関数のreturn文は、関数を書いた人間や保守する人間が把握しているとのことだが、そんなことはない。そもそも、戻り値の型を変えることはありえる。例えばstd::chrono::microsecondsを返すべきところでunsigned longを返していた関数を直すとかだ。そこで暗黙の型変換が走ってもらっては困る。
筆者も制限緩和には反対の立場である。
N4132: Contiguous Iterators
contiguous iteratorの提案。これは、ランダムアクセスイテレーターかつ、イテレーターが連続したストレージ上を指し示すもので、すなわち、イテレーターaと整数nにおいて、*(a + n)と*(std::addressof(*a) + n)が等しいイテレーターとなる。
原稿の標準ライブラリの中でcontiguou iteratorの制約を満たすものは、vector, string, valarray, arrayである。また、ライブラリTSに提案されているstring_viewもこの制約を満たす。
N4133: Cleanup for exception-specification and throw-expression
例外指定とthrow式の文面を整理する提案。意味上の変更はない。
[惜しむらくはPDF] N4134: Resumable Functions v.2
Resuamble Functionの提案。かなり長く、基本的な概念から解説していて、論文としてはよく書けている。前提知識の学習にも最適だ。この論文で提案しているのは、低級で軽量なスタックレスコルーチンだ。
論文によれば、この提案をMSVCで実装して知見を得たそうだ。論文著者はMS社員である。
ちなみにこの論文、9ページ目で、decltype(auto)と書くべきところを、declspec(auto)と書き間違えている。いくら著者がMS社員とはいえ、なかなかおもしろい間違い方をするものだ。
ただし、この提案はコア言語だけで完結せず、だいぶライブラリに依存する形になっているのだが、肝心のお手本となるべき標準ライブラリは含まれていない。論文には、コア言語が完成した後に、標準ライブラリとして我々が選ぶべきものが世上に開発されるであろう、という極めて楽観的な記述がある。また、全体的にやや理解が難しい。
不完全で難しい機能が使われるわけがなく、使われないものにライブラリが書かれるわけがない。これなら最初から純粋にライブラリだけで完結させたほうがマシだ。机上の上では悪くなさそうに見えるが、実際には使いにくい、いかにもMicrosoftらしき提案よ。
N4135: Language Support for Runtime Contract Validation (Revision 8)
N4075までは防衛的プログラミングと題されていた、実行時の前提条件検証ライブラリ。いわゆるassertライブラリの提案。
素晴らしい変更点として、プリプロセッサーの使用を推奨する文面が削られた。まだプリプロセッサーを使うところはあるのだが、コンパイル時のビルドモード切り替えにとどまっている。
このライブラリは、実行時の制約をチェックするためのassertライブラリである。従来のasertライブラリを模した作りにしているが、現実によくあるリリースビルドやデバッグビルドといったビルド時の切り替え機能に対応している。また、assertに引っかかった際に呼び出されるハンドラーを指定できる。
ビルドモード
opt, dbg, safeと、三種類のビルドモードと、何も指定しないという状態を合わせて、4種類のビルドモードが存在する。何も指定しない場合、一切のチェックが行われない。optは、最も軽い制約チェックのみ行う。dbgは、重いチェックも行う。safeは、すべてのチェックを行う。
ビルドモードは、ライブラリのヘッダーである<experimental/contract_assert>を#includeするまえに、プリプロセッサーマクロ、contract_assert_build_xxxを定義して指定する。
#define contract_assert_build_dbg 1
#include <experimental/contract_assert>
assertは、contract_assert_xxxで行う。例えばポインターの指し示す先のストレージをチェックしたいとする。
void do_something( char * p, std::size_t size )
{
// 軽いチェック
// nullポインターかどうかのチェックを行う
contract_assert_opt( p != nullptr ) ;
// デバッグ用のチェック
// 独自に実装しているヒープメモリ上で確かに確保されているメモリかどうかを確認する
contract_assert_dbg( custom_allocator::is_valid_area( p, size ) ) ;
// 安全のためのチェック
// ポインターの参照先のビット列が期待しているフォーマットを満たしているかどうかのチェックを行う
contract_assert_safe( is_valid_format( p, size ) ) ;
}
なにもモードがつかないcontract_assertというものもあり、これはcontract_assert_dbgの別名となっている。
assertが失敗した時には、あらかじめ設定しておいたハンドラーが呼ばれる。ハンドラーはstd::contract_violation_info const &型の引数を取る。この型は、制約違反が起きた式の文字列、ファイル名、行番号を取得できる。例えば、以下は例外を投げるハンドラーである。
void violate()
{
contract_assert( false ) ;
}
int main()
{
std::set_contract_violation_handler(
[]( auto && info )
{ throw info ; } ) ;
try {
violate() ;
} catch( std::contract_violation_info const & info )
{
std::cout
<< "expression: " << info.expression_text << '\n'
<< "filename: " << info.filename << '\n'
<< "lien number: " << info.line_number << '\n' ;
}
}
また、スレッドごと、ブロックスコープごとのハンドラーを設定するには、std::contract_violation_guardというライブラリが用意されている。
void f()
{
{
std::contract_violation_guard g( [](auto && info ){ throw info ; } ) ;
// gのハンドラーが有効
}
// gのハンドラーは無効
}
また、ユニットテスト用に、assertの可否によってbool値を返す機能もある。
void do_something( int * ptr )
{
contract_assert( ptr != null ) ;
}
int main()
{
int i = 0 ;
bool b1 = contract_assert_pass( do_something( &i ) ) ; // true
bool b2 = contract_assert_pass( do_something( false ) ) ; // false
bool b3 = contract_assert_fail( do_something( &i ) ) ; // false
bool b4 = contract_assert_fail( do_something( nullptr ) ) ; // true
}
[読む気の失せる典型的な学術論文クソ固定レイアウトのPDF] N4136: C Concurrency Challenges Draft
これは読む気が失せるほどにクソ典型的クソ学術クソ論文クソ固定クソレイアウトのクソPDFにて候。こののケンブリッジ大学の著者5人に申すことの候。彼らは天然の石頭にして、要望はすなわち鶏卵を岩石に投ずると一般なるも、あえて言わん。汝ら論文を紙で読みたるや。否、たとい紙で読みつるとも、物理的な単一ページを論理的な複数ページに分割するのは、そも何のためぞや。美しとでも思うてありや。うるさきことこの上なし、やめよ。
中身は、本来学術論文としてだす予定の論文の下書き原稿にて候。C++標準化委員会の文書としてもいだしたとなん言いける。
肝心の内容は、CとC++のアトミック周りの定義と理論の解説と、所謂thin air(虚空から現れいでたる)と名づけたるプログラムからは何らの脈絡もなき挙動の解決の難しきを論じ、結論して曰く、解決策なしと。
N4137: Business Plan and Convener's Report
議長のHerb SutterによるISOに提出するお堅い報告書
ドワンゴ広告
この記事はドワンゴ勤務中に書かれた。
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0