2014-09-17

電車を盛大に乗り過ごした

会社に出勤すべく家を出て、東西線に乗った。幸い、今の仕事には定時という概念が無いため、ラッシュの時間帯を避けることができる。そのため、電車の中はすいており、座ることができた。

周りの人間が不自由なバカフォ操作に勤しむ中、筆者は通勤中に紙の本で読書をしたり、昔の不自由なゲーム専用機で遊んだりしている。今は、カサノヴァ回顧録を読んでいる。

ジャコモ・カサノヴァは真の自由人である。彼は自己の自由を制限する危難に何度も遭遇しつつ、そのたびにあふれる才気と運で自由を勝ち得てきた男である。

物語は、鉛の監獄から、まさに脱出するところであった。カサノヴァは唾棄すべき卑屈な間諜の精神を叩きのめして屈服させ邪魔を排除し、やはり唾棄すべき救いようのない糞坊主を操縦して、天井に穴を穿ち、宮殿の鉛の屋根を伝って窓をぶち破る。

ふと顔を上げると、なぜか窓の外が明るい。地下鉄であるはずの東西線が地上を走っている。どうやらだいぶ乗り過ごしたらしい。次の停車駅を見ると、なんと浦安となっている。

さて、岩波文庫のカサノヴァ回顧録も、残すところ後一冊となった。残念ながら、翻訳者の岸田國士が翻訳作業中に死んでしまったので、岩波文庫のカサノヴァ回顧録は未完に終わっている。

ドワンゴ広告

この記事はドワンゴ勤務中に書いた。こんな記事でも多少の宣伝効果は得てんや。覚つかなし。

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

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

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

2014-09-16

最近の料理事情

しばらくブログには書いていなかったが、折を見ては、新しい料理を試している。

今日はトマトカレーを作った。作り方を調べると、ホールトマト缶で煮込んでカレー粉を加えるのだそうだ。試しに作ってみたが、なかなかうまかった。ただし、トマトの味が強すぎて、むしろトマト煮ではないかとの感想も得た。

プレーンヨーグルトにドライフルーツをまぜ込んで一晩置くと、ドライフルーツがふやけてヨーグルトが甘く美味しくなるというのは、すでに試したが、今はさらに一歩進めて、その出来上がったドライフルーツヨーグルトを、アロエにかけている。なかなかいいスイーツ(笑)となる。

また、保存のきく面白そうな食材も、ちょくちょく買っている。いずれ試さなければなるまい。

とうがらしパスタと、イカスミパスタが売られていたので、買ってみた。まだ試していないが、いずれゆでてみよう。

また、この前に買った大豆ミートは、未だに調理する機会がない。保存はきくので、いずれ。

2014-09-15

mp3fs : FUSEを利用したファイルシステム経由のmp3変換

mp3fs

mp3fsとは、FUSEを利用したリードオンリーのファイルシステムである。これは、ファイルが開かれた際に、FLACからMP3へのフォーマット変換をその場で行うものである。

用途としては、FLACの音楽集を、MP3しか対応していないソフトウェアやハードウェアで再生する場合に使える。

Hacker Newsのコメントでは、case-insensitiveなファイルシステムを作ったとか、大学の課題でmp3ファイルからID3タグを得て返すファイルシステムを作ったなどのコメントが上がっている。

ファイルシステムを経由した組み合わせ可能(composable)なツールというのは、従来のパイプより可能性が広がるので、なかなか面白い。

CppCon 2014のスライド資料

CppCon 2014のスライド資料が公開されている。

CppCon2014/Presentations at master · CppCon/CppCon2014

雑記

この土日は、主に昼寝と読書をして怠惰に過ごした。

読んだ本であるが、あまり重いものを読む気分ではなかったので、すこし前に買ってあった、ピーターラビット全集を読むことにした。たまたま、ブックオフで原書の全集が3000円で売っているのを見つけたのだ。だいぶ迷ったが、見つけたのも何かの縁と思って買うことにしたのであった。

買ってすぐに、お気に入りの話を何冊か読んだままで放置していたが、この機会に通して読むことにした。

ピーターラビットの英語は、110年前のものなので、さすがに古めかしい言い回しもあるものの、だいぶ読みやすい。

ただし、歌にはどのような節回しを付けるべきなのか分からない。

日曜日は、神保町に行って古本を漁るとか、秋葉原に行ってRaspberry Piを買いたい衝動に駆られたが、読む本はまだ残っているし、Raspberry Piも買ったところで何に使うあてがあるわけでもないので、結局行かなかった。

日曜日の夜は酔っぱらいが来たので、夜遅くまでくだらない雑談をしていた。

さて、月曜日こそはどこかに出かけたいが、どこに行くというあてもない。

2014-09-12

三連休

明日から三連休だ。たまたま妖怪ハウスにビールと氷結が余っているので、この週末に飲みに来る人がいればどうぞ。金曜日の夜は遅くまで帰らないが、三連休はだいたい家にいるはずだ。

cpで大量のファイルをコピーした(4億3200万件、39TB)

GNUのcpを使って大量のファイルをコピーしたところ、cpの設計上の問題で、極めてコピーが遅かったというお話。

My experience with using cp to copy a lot of files (432 millions, 39 TB)

よう。俺は最近、大量のファイルをコピーする必要があったんだが、UNIXは20年もやってきた俺の経験からも、cpの挙動には驚かされたし、俺の意見はコミュニティに共有されるべきだと思う。

環境:古いDellのサーバー(2コア、初期メモリ2GB、追加して10GB、Ubuntu Trusty)と、新しいDellのストレージ格納機(MD 1200)にある、12個の4TBディスクでRAID 6が設定してあって、全体で40TBの要領を持ち、二つのドライブが同時に失敗しても問題ない環境。サーバーは遠隔地バックアップに使われていて、ディスクへの書き込みしかしてない。ほとんどのファイルにrsnapshotをかけてるので、リンク数が多い(30+)

ある朝、俺はディスクが故障している通知を受け取った。まあ問題ない。たまにあることだ。Dellに電話して、次の日に交換ディスクを受け取った。リビルド中に、交換ディスクが故障した。そして、また別のディスクも故障した。さて、Dellのサポートは単に故障ディスクを交換するのは辞めるように支持してきた。というのも、全体があなぬけである可能性があるから。もちろん、俺も分かっていることだが、ディスクというものは十分に多くの不良ブロックがないと故障だと通知されない。運が悪ければ、異なるディスクの三つのブロックが短期間でやられたの可能性があり、RAIDコントローラーは失敗を検知して、パリティからデータを再計算して、どこか別の場所に記録するということができていないかもしれない。そういうわけで、ヤバイディスクは二つだけだが、データが失われた可能性はある。

容量もほぼ使いきっていたことだし、ストレージ格納機をもうひとつ注文して、古いのから新しいのにファイルをコピーして、古いのを直して、全体の容量を増やそうと考えた。普通ならば、ブロックレベルでコピー/ムーブするところだが(ddとかpvmove)、不良ブロックの疑いがあるので、ファイルレベルのコピーをすることにした。そうすることによって、どのファイルが不良ブロック上にあるのか分かるからだ。他人が大量のファイルをコピーする経験をネット上で検索した結果、cpは必要な処理をやってくれるだろうと結論した。ハードリンクを保つには、どのファイルがすでにコピーされたかを把握しなければならないからだ。俺はサーバーに8GBのRAMを増設し、スワップ領域ももっと多く取るように設定した。

新しい機材が届いたので、コピーを始めた。当初は、iotopで計測すると、300-400MB/sぐらいの良好な進み具合だった。しばらくすると、速度が極端に下がり始めた。ほとんどの時間は、ハードリンクを作成するのに費やされ、ファイルシステムが整合性の取れた状態であることを保証するのには時間がかかるからだ。俺はXFSを使っている。たぶん、write barrierを無効にするのを忘れていたためだろう。RAIDコントローラーには信頼できるバッテリーバックアップ付きのwrite cacheがあるので、無効にしても問題なかったはずだ。予想通り、cpのメモリ使用量が上がり始め、すぐにギガバイト単位に達した。

コピーを開始してから数日後、驚き第一段がやってきた。コピーが止まったのだ。straceによると、cpは一切のシステムコールを発行していなかった。ソースコードを読むと、cpは、どのファイルがコピーされたのかを、ハッシュテーブルで管理していて、頻繁な衝突を防ぐために、たまにサイズを増やしている。RAMを使い切った場合、これは極めて遅い操作となる。

ハッシュテーブルのリサイズはいずれ終わり、cpは処理を再開するだろうと考えた。しばらくすると、またコピーを始めた。何度か途中で泊まり、ハッシュテーブルのリサイズが行われた。それぞれ、かなりの時間がかかった。ついに、10日間ものコピーとハッシュテーブルのリサイズの末、新しいファイルシステムは、古い奴と同量のブロックとinodeを持ったことを、dfで確認した。しかし、驚いたことに、cpコマンドは終了しない。またソースを読むと、cpはハッシュテーブルのデータ構造を、コピーが終わった後でご丁寧にも破棄しようとしている(forget_all呼び出し)。この時点でcpプロセスの仮想メモリのサイズは17GB以上あり、サーバーには10GBのRAMしかないため、大量のスワップが発生する。

cpは"-v"オプション付きで実行されていて、出力(stdoutとstderr両方)はteeコマンドにパイプして(巨大な!)ログファイルに格納してある。どうやらどこかでcpの出力はバッファーされていて、ログファイルは行の途中で終わっている。バッファをフラッシュさせて完全なログファイルを得たいがために、cpがハッシュテーブルを破棄し終わるまで一日ほど待ったが、諦めてプロセスをkillした。

これを書いている今、両方のファイルシステムに対して"ls -laR"を実行していて、すべてコピーされたかどうかを確認中だ。ただ、cpの失われた出力の最後の部分にエラーが含まれていたのでもなければ、I/Oエラーを出したのはたったひとつのファイルだけだ(運良く、そのファイルには別のコピーがあった)

こういう状況はめったに起こるものではないが、cpは管理だけの処理で待ちぼうけを食らわせるのではなく、I/Oを待つ間に管理処理を行うべきではないか。それから、まともなメモリ管理が行われていない大昔のシステムをサポートする必要があるのでもなければ、cp.cの最後のforget_all呼び出しは削除すべきしても問題ないのではないか。

今回得た知見をまとめると。

ハードウェアとファイルシステムがまともであると信頼するのであれば、ファイルシステム全体をコピーするにはブロックレベルのコピーを行え。空き容量が大量にあるのでもなければ、その方が速い。また、メモリ使用量も少ない。

多くのファイルがあって、ハードリンクを保持したい場合、ファイルレベルのコピーを行う場合は、十分なメモリを用意しろ。

データ構造をご丁寧に破棄することは、単にプロセスを終了させて後始末を任せるより、相当に時間がかかる。

故障したハードディスクの数と、不良ブロックのあるハードディスクの数は、別物だ。RAID 6においても、運が悪ければ、3つのハードディスク不良を待たずして、データを失うこともある。RAID 5でも同じだ。一つのディスク不良か、ディスク不良なしでも、運が悪ければデータを失うことがある。

これが誰かの役に立つか、興味を引くといいな。

敬具など

Rasmus Borup Hansen

cpで大量のファイルをコピーする際の問題は、たまにハッシュテーブルをリサイズするが、その間にI/Oを一切行わないというのと、終了時にご丁寧にもハッシュテーブルで確保したメモリを破棄しようとするため、恐ろしいほど時間がかかるということだ。近代的なOSはメモリ管理をしっかりとしているので、今すぐ終了するのであれば、細切れにしたヒープの中身(しかも実メモリが足りないためスワップ発声)をわざわざ意味のない状態にしていく必要はない。

なお、MLでは、これを受けてcpを改良する動きが見られる。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

来週末はBoost勉強会大阪で発表する予定で、そのためにConcept Liteのドラフトを読み込んでいる。軽量コンセプトはある程度理解した。

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

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

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

2014-09-11

Old New Thing: ブルースクリーンを書いた人間は、まあ、僕さ

Old New Thingの著者で、古参MS社員のRaymond Chenが、Windows 95のブルースクリーンを書いた時のことをブログに書いている。

I wrote the original blue screen of death, sort of - The Old New Thing - Site Home - MSDN Blogs

この前、Windows 95の話を持ちだした。ここで書いたように、Ctrl + Alt + Delダイアログは、Windows 3.1で導入され、Windows 95では、すでになくなった。Windows 95では、Ctrl + Alt + Delを押すと、以下のようなダイアログボックスが表示される。

Close Program × 
Explorer
Contoso Deluxe Composer [not responding]
Fabrikam Chart 2.0
LitWare Chess Challenger
Systray
WARNING: Pressing CTRL+ALT+DEL again will restart your computer. You will lose unsaved information in all programs that are running.
End Task
Shut Down
Cancel

(Systrayについては以前学んだよね)

Windows 3.1では、深刻なエラーではクラッシュしてブラックスクリーンになったが、Windows 95では、青で表示するようにした。僕はそのコードを書いたうちの一人だ。少なくとも、最後に書き換えた人間だな。

私はブルースクリーンメッセージを表示するコードを担当していた。カーネルモードビデオドライバーにテキストモードに切り替えるよう指示し、画面を青背景にして、白文字を表示して、ユーザーがキー入力をするのを待ち、画面を元に戻し、ユーザーの入力を、メッセージを表示するよう要請したコンポーネントに伝える[1]。

デバイスドライバーがクラッシュした場合、Windows 95はカーネルモードコンポーネント内の深刻な問題に、最善を尽くして対処しようとする。死の青画面(blue screen of death)というべきなのか、不便な青画面(blue screen of lameness)というべきなのか。ブルースクリーンを見たことがない幸運なもののために、以下がその例だ。



 Windows 


An exception 0D has occurred at 0028:80014812. This was called from 0028:80014C34. It may be possible to continue normally.

* Press any key to attempt to continue.
* Press CTRL+ALT+DEL to restart your computer. You will
  lose any unsaved information in all applications.

「通常通り動作を実行できるかもしれませんよ」という楽観的な文面に注目されたい。みんな忘れているが、Windows 95は、ブルースクリーンエラーを表示した後、なるべくそのエラーを無視して実行し続けようとすることだ。つまり、まあ、スキャナーのドライバーはクラッシュしたので、スキャナーは動かなくなったかもしれないけれど、それ以外のシステムは無事だというわけだ。

(これを今日おこなったらどうなるだろうか。「カーネルパニックを無視するには何かのキーを押してください」)

技術的に何が行われているかというと、バーチャルマシンマネージャーが現在行われているイベントを破棄して、イベントディスパッチャーに戻るのだ。これは、カーネル内における、例外をウインドウプロシージャで飲み込んで、メッセージループに戻るようなものだ。継続できるイベントがなければ、現在のアプリケーションが終了される。

時に、問題はシステム全体に及ぶこともあり、現在のイベントを破棄したり、アプリケーションを終了しても、問題を解決しないことがある。次に起こるのは、次のイベントやアプリケーションが、同じ問題に引っかかり、数ミリ秒後に、またブルースクリーンメッセージを目にするということだ。何回か似たようなメッセージを目にした後、おそらくは諦めて、Ctrl + Alt + Delを押すだろう。

さて、当初のメッセージは、上のようであった。しかし、メッセージには問題上がる。デバイスドライバーがカーネルに読み込まれるアドレスというのは予測可能ではないので、生のアドレスが表示されていても、大して役に立たない。もし誰かが、「うちんとこの重役がこんなクラッシュメッセージ出したんだけど、何があったか分かるかい?」と言われたとしても、得られるのは無意味な数値だけだ。

その誰かというのは、僕でもあったわけだ。

この問題に対処しやすくするために、僕はメッセージを書き開けて、ドライバーの名前と、セクション番号と、セクションからのオフセットを追加した。



 Windows 


An exception 0D has occurred at 0028:80014812 in VxD CONTOSO(03) + 00000152. This was called from 0028:80014C34 in VxD CONTOSO(03) + 00000574. It may be possible to continue normally.

* Press any key to attempt to continue.
* Press CTRL+ALT+DEL to restart your computer. You will
  lose any unsaved information in all applications.

これで、クラッシュしたドライバーの名前が分かる。何もわからなかったとしても、問題がどこにあるかのヒントは得られるわけだ。ドライバーのMAPファイルにアクセスした誰かさんは、アドレスを探して、クラッシュした箇所を特定できる。最高ではないにせよ、何もないよりマシだ。僕がこの変更をするまでは、何もなかったのだ。

そういうわけで、僕は不便なブルースクリーンを、僕の仕事を多少マシにするために書き換えたと言える。

余話:その後、誰か(僕だったかどうかは覚えていないので、誰か同僚としておこう)が、クラッシュアドレスを調べるためのコードを追加した。もし、箇所がカーネルのヒープマネージャー出会った場合、メッセージは多少変更される。



 Windows 


A 32-bit device driver has corrupted critical system memory, resulting in an exception 0D at 0028:80001812 in VxD VMM(01) + 00001812. This was called from 0028:80014C34 in VxD CONTOSO(03) + 00000575.

* Press any key to attempt to continue.
* Press CTRL+ALT+DEL to restart your computer. You will
  lose any unsaved information in all applications.

この場合、「通常通り動作を実行できるかもしれませんよ」という文面が消える。なぜなら、この場合、そういうことはまずないからだ。

余話:よくやったな。Slashdot君。訂正をしたつもりかもしれないが、その訂正も間違っているよ。まあ、間違いには気がついたみたいだけどね。

[1]: このコードはカーネルで動くので、キーボードレイアウト情報にはアクセスできない。読者がChinese-Bopomofoキーボードレイアウトを使っているかどうかなんてわかりっこない。その場合、"OK"とタイプするには、C, L, 3だ。特に訳には立たないけどね。カーネルにはIMEなんてないんだから。そういうわけで、入力は、EnterとかESCみたいな、言語からは独立したキーに割り当てられていた。

だいぶ久しぶりにOld New Thingを訳した気がする。以前はだいぶ苦労していた翻訳も、今はそれほど苦労しないのは、英語力が上がったからであろうか。それとも単に慣れたのか。

2014-09-09

寄生獣に感じるデジャブ

駅のホームの広告で、寄生獣が映画化されるという広告を見た。はて、寄生獣、確か妖怪ハウスにマンガがあったはずだ。

妖怪ハウスに寄生獣が転がっていたので読んでみた。なるほど、面白い漫画だ。しかし、なにか引っかかる。似たような話を昔読んだ記憶があるのだ。

たしか、私の通っていた小学校の図書館にあったSF小説だ。宇宙から飛来した宇宙人が人間の体の中に入って、傷口を止血したり、網膜に影を作って文字を表示して宿主と会話したりしていた。設定が似ているし、他の犯罪宇宙人の宿主の人間を探すという話の筋まで似ている。

ほのかに覚えている内容では、「主人公は腕を深く切り裂いてしまったが宇宙人が止血、軽いキズに見えたので、消毒液をたらすと宇宙人が激痛に飛び退いて、申告な傷となった」、「泥水に躊躇せずに飛び込んだ人間が宿主ではないのかと疑う」、「主人公ははしゃいで裸足で走ったので、ガラスの破片が足にささったが、宇宙人が対処した」など。

しかし、タイトルが思い出せない。誰かこのようなSF小説に心当たりはないだろうか。

もちろん、設定や筋書きなどはアイディアでしかなく、表現ではないので、著作権侵害だと糾弾するつもりはない。ただ、不思議なデジャブ感を何とか解決したいものだ。

2014-09-06

What if? : 高額な靴箱

Expensive Shoebox

サイズ11の靴箱に詰め込める最も高額なものって何? (例えば、64GBのMicroSDカードに大量の購入済み音楽を詰め込むとか)

Rick Lewis

靴箱に高額なものを詰め込んだ結果の上限は、どうやら20億ドルぐらいだ。面白いことに、この上限は高額な候補どれでも同じぐらいだ。

MicroSDカードはいいアイディアだ。iTunesの音楽はひとつあたり1ドルぐらいかかり、MicroSDカードは1ガロン(3.785411784立法メートル)あたり1.6ペタバイトの容量を持つ。男性用サイズ11の靴箱は、靴のブランドや種類により異なるが、だいたい10-15リットルぐらいだ。つまり、4MBの音楽を15億曲(それぞれ約1ドル)となる。(これは、ITunesが提供する楽曲の20倍の個数であり、同じ曲を何度も買わなければならないだろうが)

Adobe®©™ Photoshop®©™ CS®™ 5™のような高額なソフトウェアは、メガバイトあたりの価格率で、多少上回る。ただし、数百ドルで数百メガバイトの容量がある。いや、かつてはそうだった。Adobeは今や、クラウドモデルに移行してしまった。

ソフトウェアの価格を考察にいれると、好きなだけ無制限にアプリ内課金ができるものによってコストを水増しできる。ゲーム内のRPGキャラは廃課金の結果の産物であるとしても、そのキャラが1兆ドルの価値を持つと真面目くさった顔で主張するのは、いかにも無理がある。

親愛なるアメリカ国民よ。我らの国家負債が期せずして2兆ドル跳ね上がった。

ところで、全く関係ないんだけどさ、俺のキャラは超かっこいい剣を大量に持ってるんだぜ。

というわけで、現物を考えてみよう。

もちろん、金というものがある。13リットルの金は、本記事を執筆している時点で、約1000万ドルの価値がある。プラチナは、もう少し高くて、1靴箱あたり1300万ドル[1]だ。これは、靴箱に100ドル札を詰めるより10倍の価値があるが。ところで、靴箱いっぱいの金は、子馬ほどの重さがある。

[1]: 残念ながら、まだSI単位ではない。

もっと高額な金属はある。例えば、1グラムの純粋なプルトニウムは、約5000ドルの価値がある。ボーナスとして、プルトニウムは金より密度が高いので、靴箱に300キログラムほど詰められる。

読者が早速にもプルトニウムを30億ドルほど購入しようと考えているのであれば、注意してもらいたい。プルトニウムの臨海量は10キログラムである。そのため、確かに、靴箱の中に300キログラム詰め込めるとはいえ、それはほんの一瞬の間だけだ。

高品質なダイヤモンドは高額であるが、適正価格を定めるのが難しい。というのも、業界全体が詐欺によって成り立っている宝石市場は複雑だから。あるWebサイトはフローレス600mg(3カラット)のダイヤモンドに30万ドル以上もの値を示している。つまり、完全な品質のダイヤモンドを詰めた靴箱は、200億ドルであるが、10億から20億ドルぐらいが妥当な価格だろう。

さて、同一の大きさのラウンドカットダイヤモンドを最も効率的に詰める方法は。

12時間待て。

違法な薬物は、重さあたりで、金より価値がある。コカインの価格はばらつきがあるが、多くの地域で、グラムあたり100ドル[2]近辺だろう。金は現在、その半分の価値しかない。しかし、コカインは金より密度が低い[3]ため、靴箱いっぱいのコカインは、金より価値が低いだろう。

[2]: ドラッグの末端価格について調査した後の私の検索履歴により、筆者は政府のあらゆる種類のウォッチリスト入りすることだろう。このブログに必要な過去の調査のためにすでにリスト入りしていなければの話だが。

[3]: でも待てよ。コカインの密度ってどのくらいだ。いつも通り、Straight Dope掲示板で議論されている。このスレでは、CRC HandbookやMerck Indexを参照したが、結局諦めて、多くの有機物が取りうる約1kg/Lであろうと結論している。ただし彼らは、コカインの沸点と、オリーブオイルに対する溶解度を学んだようだ。

コカインは重量あたり最も高額なドラッグではない。LSD--おそらく、マイクログラム単位で販売される中でもっとも消費されているもの--は、重量あたりコカインの約千倍の価値がある。靴箱に純粋なLSDを詰めると、約25億ドルの価値がある。

一部の処方箋医療薬は、LSDと同じぐらい高額である。一回分のブレンツキシマブ ベドチン(アドセトリス)は、13500ドルかかる。これは、平均的な患者にとって、靴箱をLSDやプルトニウムやMicroSDカードと同じぐらい高額にできるということだ。他の医療薬には、もっと高額なものもある。

もちろん、靴箱には靴を入れることもできる。

変なの

オズの魔法使いで演じたJudy Garlandsの靴は、オークションで666000ドルの値がついた。この靴は、我々が考察してきた様々なものと違って、かつて靴箱に入っていたかもしれない。

靴箱を任意のお金でいっぱいにしたいのであれば、アメリカ合衆国財務省に10兆ドルのプラチナ硬貨を鋳造させることができる。

とはいえ、通貨の権威者に任意の物体に価値を生じさせるのであれば・・・

単に小切手を書けばよい。

2014-09-04

2014-07-post-Rapperswilのレビュー: N4109-N4121

2014年7月の論文集の最後。

[一発目からPDF] N4109: A proposal to add a utility class to represent expected monad - Revision 1

HaskellのEitherに似たライブラリ、expected<T, E>の提案。expectedは、T型か、あるいはエラー通知のためのE型を格納するクラスである。

前回からの変更点で最も大きなものは、テンプレート仮引数の順番を変えた。expected<E, T>だったものを、expected<T, E>にした。

[論文のpreconditionとしてPDFを禁止したい] N4110: Exploring the design space of contract specifications for C++

C++のコア言語で契約(contract)をサポートする提案。

関数の宣言に契約を指定して、契約に違反した場合、std::terminateを呼び出す。

用語の説明。

エラー状態
プログラムが復帰できるかできないかにかかわらずエラーとなる状態
Precondition
関数を実行する前に満たしている必要のある状態
postcondition
関数を実行後に満たしているべき状態
invariant
関数の実行前、実行後に満たしているべき条件のkと
契約(contract)
ある関数に指定された一連のprecondition, postcondition, invariantのこと。
契約違反
関数を実行前にそのpreconditionかinvariantが満たされていないこと、もしくは、関数実行後にpostconditionかinvariantが満たされていないこと。

契約をコア言語でサポートするために、関数の宣言として、precondition, postcondition, invariantを記述できるようにし、契約違反であれば、std::terminateを呼び出す。あるいは契約違反ハンドラーを設定できるようにする。

論文では、すでに契約をサポートしている言語として、Eiffiel, Ada2012, Dを挙げ、サンプルコードを示している。

またC++にはすでに、防衛的プログラミングという名前で高級なassertマクロが提案されている。

論文は、ライブラリによる契約の実装では、ツールによる解析や、コンパイラーが契約条件を把握することによるよりよいコードの生成などができないとしている。

だいぶ野心的な提案だが、すでにサポートしている言語は複数あるので、机上の空論というわけでもない。ただ、記述するのは面倒そうだが。

[PDFバリヤー、PDFは跳ね返る] N4111: Static reflection (rev. 2)

静的リフレクション機能の提案の一つ。クラスのメンバーなどといったプログラムの構造をテンプレートメタプログラミングの要領で取得できるライブラリの提案。論文としてはN3996の改訂版だが、内容的には全面的に書きなおしになっている。

ただ問題は、テンプレートメタプログラミングの延長線上でプログラムの構造を取得するようになっているので、設計がBoost.MPLもびっくりの複雑なものになっているし、実際に使うのも冗長なコードを書かねばならないだろう。

個人的には、機能的には面白いものの、文法が悲惨すぎて破綻する印象しかない。たしかに、テンプレートメタプログラミングの要領で操作できるのは魅力的でもあるのだが、その文法があまりにも冗長すぎる。

[論文にPDFフォーマットを使うなというNBコメントを出したい] N4112: File System PDTS National Body Comments Record of Response

File System TSに対するNBコメント。

[静的リフレクションの前にPDFを何とかして欲しい] N4113: Reflection Type Traits For Classes, Unions and Enumerations (rev 3)

静的リフレクション機能として、テンプレートメタプログラミングの要領でプログラムの構造を取得できるライブラリの提案。こちらはN4027の改訂版。

これはまだ小規模で理解可能な範囲だ。

N4114: Defaulted comparison operators

クラスの演算子==, !=, <, >, <=, >=をデフォルト生成する機能を追加する提案。明示的なデフォルト演算子。

クラスのメンバーの等号や大小比較ができるとき、メンバーごとの比較をして等価か大小を決定する演算子オーバーロードを書くのは、極めて機械的で面倒な作業である。いちいちメンバーを手で列挙しなければならないし、特に大小比較の場合は、極めて面倒なコードを書かなければならない。そのような作業は、コンパイラーにデフォルトの実装として生成させてしまえばよい。

この演算子を自動生成させるには、明示的に指定する必要がある。なぜならば、いままで存在しなかった演算子が勝手にデフォルト定義されてしまっては、既存のコードを壊す恐れがあるからである。

会議の結果、以下の二つの文法が提案されている。

struct Thing
{
    int a, b, c;
    std::string d;
};

bool operator==(const Thing &, const Thing &)= default;
bool operator!=(const Thing &, const Thing &)= default;

このように、explicited defaultedの文法を流用して、明示的に実装を生成するように指示する。

メンバーがprivateの場合は、演算子はfriendを指定子なければならない。その場合は、以下のように書く。

class AnotherThing
{
    int a, b;

public:
    // ...

    friend bool operator<(Thing, Thing) = default;
    friend bool operator>(Thing, Thing) = default;
    friend bool operator<=(Thing, Thing) = default;
    friend bool operator>=(Thing, Thing) = default;
};

会議による議論では、このように演算子を個別に冗長に指定するのは面倒であるので、もっと短く書きたいという意見が多かった。会議で提案された短い文法は以下の通り。

struct Thing
{
    int a, b, c;
    std::string d;

    default: ==, !=, <, >, <=, >=;
};

この簡潔な文法は、冗長でコピペを誘発する文法に比べて優れているし、最適な引数の型などは、コンパイラーがよきにはからって正しく、パフォーマンス上優位になるよう、決定してくれる。

その他の演算子も同様に生成できるのではないかという点については、論文では、それほど役に立たないとしている。

論文では、mutableメンバーの扱いをどうするかで、議論が沸かれているとしている。解決方法としては、mutableメンバーは比較対象から除外するか、除外せずに対等に扱うかだ。論文著者は除外する方を好むとしている。標準化委員会のデッドロックを避けるために何らかの決定をせねばならず、提案では、mutableメンバーを含むクラスの場合は、デフォルト実装の生成は行われないとするそうだ。こうすることで、将来解決するために保留できる。

totally orderedではない型はどうすればいいのだろうか。例えば、ポインターの値の大小比較は、同じ連続したストレージ上から確保されたメモリーでなければ意味をなさない。IEEE浮動小数点数にはNaNがあり、NaNの比較はかなり特殊な定義がなされている。

議論の結果として、

  1. 浮動小数点数型のメンバーが存在する場合、明示的なデフォルト演算子は生成は生成できない。
  2. ポインター型のメンバーが存在する場合、等価比較の明示的なデフォルト演算子は生成できる。
  3. ポインター型のメンバーが存在する場合、大小比較の明示的なデフォルト演算子は生成できない。

筆者が思うに、この決定はどうかと思う。結局この機能は、一般的な比較演算子を簡単に生成する機能なのだから、浮動小数点数やポインターといったありふれた型の存在によって、明示的なデフォルト演算子が使えないとあっては、とても使い道が制限される。

そういう責任はユーザー側にまかせて、コンパイラーはユーザーが指定したならば生成するべきであると思う。

N4115: Parameter Pack Searching

パラメーターパックの中に指定の型が含まれているかどうかを調べるtraits、is_contained_in<T, P>と、パラメーターパックが別のパラメーターパックの中の型をすべて含むかどうかを調べるcontains_type< T, P >の提案。

まず、パラメーターパックを格納するクラステンプレート、packer。

template <class... T> struct packer { };

packerはtupleに似ているが、tupleのようにメンバーを持たないので、必ずインスタンス化できる。

パラメーターパックPにT型が含まれているかを調べるis_contained_in traits。

template <class T, class... P> struct is_contained_in;

template <class T, class... P>
  constexpr bool is_contained_in_v = is_contained_in<T,P>::value;

その実装例

template <class T> struct is_contained_in<T> : false_type { };

template <class First, class... Rest>
  struct is_contained_in<First, First, Rest...> : true_type { };

template <class T, class First, class... Rest>
  struct is_contained_in<T, First, Rest...>
    : is_contained_in<T, Rest...> { };

あるpacker Tのパラメーターパックが、別のpacker Uのパラメーターパックをすべて含んでいるかどうかを調べるcontaines_type。

template <class T, class U> struct contains_types;

template <class T, class U>
  constexpr bool contains_types_v = contains_types<T,U>::value;

その実装例


template <class... TPack>
  struct contains_types<packer<TPack...>, packer<>> : true_type { };

template <class... TPack, class UFirst, class... URest>
  struct contains_types<packer<TPack...>, packer<UFirst, URest...>>
    : integral_constant<bool,
        is_contained_in<UFirst, TPack...>::value &&
        contains_types<packer<TPack...>, packer<URest...>>::value> { };

まあ、あっても困らない小粒なライブラリだ。

[PDFは消え去るべき] N4116: Nested Namespace Definition (rev 1)

名前空間のネスト。

namespace A { namespace B { namespace C {
} } }

という冗長なコードを、


namespace A::B::C {
}

と書けるようになる。

議論ではnamespaceのinlineや別名をどうするかという問題も出たそうだが、とりあえずそれは保留して、最小限の提案だけをする。つまりinline namespaceには対応しない。

別名の問題というのは、以下のようなコードが通らないということだ。

namespace A::C {} 
    using namespace B = A; 
    namespace B::C {} // ill-formed

ただし、もともと名前空間の別名は、このようには使えなかった。

namespace A { } 
    using namespace B = A; 
    namespace B { } // ill-formed

つまり、現状維持ということになる。

N4117: C++ Standard Library Active Issues List
N4118: C++ Standard Library Defect Report List
N4119: C++ Standard Library Closed Issues List

標準ライブラリに持ち上がっている問題、解決された問題、議論の結果問題ではないとされた問題の一覧。

N4120: Null Coalescing Conditional Operator

ある値を使う前に、値のnullチェックをするような処理は、プログラミングで頻出する処理である。これは、現在のC++14の文法では、条件式を使うことで実現できるが、条件式を使うのは色々とめんどくさい。

x ? x : y ;

という形になるのだが、xが長い複雑な式である場合、二度重複して書かなければならない。また、このままではxは二回評価されてしまうので、評価されることによる副作用を避けたいのであれば、まず一時オブジェクトに式を評価した結果を格納しなければならない。

auto && temp = x ;
temp ? temp : y ;

このため、他の言語にも存在し、またGCCの独自拡張で、Clangにも実装されている文法を、標準に提案する。この文法を使えば、以下のように書ける。


x ? : y ;

これは、以下のコードとほぼ同じ意味を持つ。

auto && temp = x ;
temp ? temp : y ;

違いは、tempはxと同じ値カテゴリーだとみなされるということだ。

なかなか小粒ではあるが便利であれば嬉しい提案だ。

N4121: Compile-Time String: std::string_literal<n>

Library Fundamentals TSに、std::string_literal<n>を追加する提案。

このライブラリは、コンパイル時の文字列を扱うためのライブラリである。リテラル型なのでコンパイル時に扱える。つまり、constexprオブジェクトにできるし、constexpr関数で使うこともできる。

コンパイル時に文字列を扱うクラステンプレートの実装方法としては、二つの方法がある。この論文で提案されているのは、

template < std::size_t n >
struct string_litera
{
    char data[n] ;
} ;

という形だ。文字数をテンプレート引数で受け取って、配列で文字列を持つ。これはリテラル型なので、コンパイル時に扱える。

もうひとつの実装方法としては、非型テンプレートパラメーターパックで文字を受け取ることだ。

tempalte < char ... data >
struct string_literal
{ } ;

まず、両方共、相互に簡単に変換可能である。

この二つの利点と欠点を考えると、パラメーターパックを使う実装は、文字列ごとに別のインスタンス化が必要で、文字列を名前マングリングで受け取り、りんカーを酷使する、スケールしない実装方法である。パラメーターパックによるコンパイル時文字列の実装、またテンプレートメタプログラミングは、たまたまチューリング完全であったために酷使された利用法であって、近代的なC++14では、constexprオブジェクトのほうが使いやすい。

静的リフレクションを入れるにも、何はともあれコンパイル時文字列は必要になるので、いずれ必要になるライブラリである。

ドワンゴ広告

今日は不自由なソフトウェアであるスーパーマリオランド2 6つの金貨で遊びながら通勤し、また昼休みにも6つの金貨で遊んだ。ドワンゴには路地裏と呼ばれているスペースがあり、ゲーム機が置かれているが、あまり遊ばれているのをみたことはない。なんでもぷよぷよ勢のために稼働するメガドライブなどが維持されているのだとか。

もちろん、今日はドワンゴ勤務中にこの記事を書いた。次の論文集は10月だが、明日からはBoost勉強会大阪の発表資料を作成するので、暇にはならない。

ところで、そろそろまたドワンゴのセミナールームを使ってC++勉強会を開くべきだろうか。

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

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

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

2014-09-03

2014-07-post-Rapperswilのレビュー: N4090-N4108

N4090からN4108までの論文は、問題集であったり、文面案だったりするので、特に詳細に解説することはない。概要だけ紹介する。

[概要すら解説する気が失せるPDF] N4090: The Maladies of All Member Templates: An Incomplete Biography of Specialization (DR727 + DR1755)

すでに上がっている問題に、DR727DR1755とがある。これは、現在の文面の解釈が曖昧なため、コンパイラーごとにテンプレートの明示的特殊化(論文では、わかりやすさのために完全特殊化という言葉を使っている)、部分的特殊化の挙動が微妙に違う。この問題を解決するためにIssaquah会議で話し合った結果、意見の一致するルールが浮かび上がってきたので、そのルールを記載し、テストコードを示す。合意したルールの挙動にするClangのパッチも書かれたようだ。

N4091: C++ Standard Core Language Active Issues
N4092: C++ Standard Core Language Defect Reports and Accepted Issues
N4093: C++ Standard Core Language Closed Issues

C++のコア言語で認知されている問題、解決済みの問題、議論の結果、問題ではないとされた問題の一覧。

N4094: Response To: Let return Be Explicit

N4074で、return {expr}はexplicitを無視することが提案された。つまり、以下のようなコードが合法になる。

struct X
{
    explicit X(int) { }
} ;

X f()
{
    // C++14では違法
    // N4094提案では合法
    return {0} ;
}

N4094は、この提案に反論する論文である。

反論に用いている例がいろいろある。この解説ではかなり簡略化して解説するが、論文ではすこし違う例を出している。例えば、chronoライブラリだ。std::chrono::secondsで、ある数値を秒、分、時の単位で解釈して秒数を返す関数を考える。

enum struct unit { seconds, minutes, hours } ;

std::chrono::seconds
convert( unit u, int count )
{
    switch( u )
    {
        case unit::seconds :
            return { u } ;
        case unit::minutes :
            return { u } ;
        case unit::hours :
            return hours{ u } ;
    }
}

このコードは、enumによって、数値の単位を切り替えて秒数を返す関数である。しかし、このコードにはバグがある。case unit::minutesのところで、return { u }としてしまっている。std::chrono::secondsのコンストラクターはexplicitなので、C++14では、このバグはコンパイル時に検出可能である。しかし、N4074提案では、このコードはコンパイルが通ってしまう。そして、実行時に期待しない結果をもたらすだろう。

C++11が発行されてから、まだ3年しかたっていない。return {expr}という形は、まだ今から挙動を変更してもいいほど使われていないのだろうか。

しかし、ネット上で検索しただけで、実際に使っている例が見つかる。実際に使われているものの挙動を変更するのは危険だ。

N4074の発端になったtupleだが、return { 1, 2 }に対して、std::tuple< std::chrono::seconds, std::chrono::nanoseconds >が戻り値の型の場合、どうするのか。

std::tuple< std::chrono::seconds, std::chrono::nanoseconds >
test1()
{
    // 危険、単位が不明
    return { 1, 2 } ;
}

std::tuple< std::chrono::seconds, std::chrono::nanoseconds >
test2()
{
    // 安全、単位が分かっているので正しく変換される。
    return { 1h, 2ms } ;
}

N3739では、Perfect Initializationと呼ばれる技法を利用したtuple側による対応により、test1は弾くがtest2は通すようにできる。これは理想的である。N4074提案は危険である。

そもそも、戻り値の型とreturn文とが、どちらも同じプログラマーの管理下にあるということは断定できない。

// 誰か他人が書いた関数
SafeData GetSomeData() ;

// 安全なデータは暗黙の型変換を許し
// 非安全なデータは明示的な型変換を要求するクラス
struct SomeClass
{
    SomeClass( SafeData ) ;
    explicit ( UnsafeData ) ;
} ;

SomeClass f()
{
    return { GetSomeData() }  ;
}

この場合、GetSomeDataという関数の戻り値の型が変えられたとすると、C++14ならばコンパイルエラーになってくれるが、N4074提案では、コンパイルが通ってしまう。そして、実行時に期待しない結果をもたらすだろう。

スマートポインターと組み合わせるのも危険だ。そもそも、C++14では、newなど使わない。make_uniqueやmake_sharedを使う。そういうレガシーなコードとのやりとりのために、型安全性を犠牲にしてよいわけがない。

そもそも、{}を使うと暗黙に型変換するというのは、一貫性に欠ける。リスト初期化では、narrowing conversionを禁止しているではないか。

char f()
{
    int x = 0 ;
    return { x } ; // ill-formed、intからcharへのnarrowing conversion
}

このように、{}を使う文法で暗黙の型変換に制限しておきながら、他方で緩めるとは一貫性に欠ける。

論文ではこれらの例を挙げて、N4074提案に反対している。

N4095: File System TS Active Issues List (Revision R2)
N4096: File System TS Closed Issues List (Revision R2)
N4097: File System TS Defect Report List (Revision R2)

File System TSに持ち上がっている問題、議論の結果問題ではないとされた問題、解決された問題の一覧。

N4098: File System TS Editor's Report

File System TSの編集者による最新のドラフト、N4099に対する変更報告書。

N4099: Draft Filesystem Technical Specification

File System TSの現時点での最新のドラフト文面。

File Systemは、ファイルシステム、つまりファイルやディレクトリなどの列挙や操作を行うライブラリである。

[PDF注意] N4100: Programming Languages — C++ — File System Technical Specification

File System TS。

N4101: C++ Standard Evolution Active Issues List
N4102: C++ Standard Evolution Completed Issues List
N4103: C++ Standard Evolution Closed Issues List

C++の新機能に持ち上がっている問題、解決済みの問題、議論の結果問題ではないとされた問題の一覧。

N4104: Technical Specification for C++ Extensions for Parallelism, Working Draft,

STLのアルゴリズムに並列実行版を追加するParallelism TSのドラフト。

[PDF注意] N4105: Information technology – Programming languages, their environments and system software interfaces – Technical Specification for C++ Extensions for Parallelism

Parallelism TS。

N4106: Parallelism TS Editor's Report, post-Rapperswil

Parallelism TSの編集者による変更報告書。

N4107: Technical Specification for C++ Extensions for Concurrency, Working Draft

std::futureの機能を拡張するConcurrency TSのドラフト。

N4108: Concurrency TS Editor's Report, February 2014

Concurrency TSの編集者による変更報告書。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

今日は、社内でビブリオスというドラフトゲームを行った。社内にビブリオスをインストできる人間がいたので助かった。このビブリオスはドワンゴの社内同好会であるボドゲ部の部費で購入したそうだ。

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

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

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

2014-09-01

2014-07 post Rapperswil mailingのレビュー: N4080-N4089

またもやC++論文のレビュー10本、今回は全く新しい提案ではなく、議論された挙句の文面案が多いので、あまり書くことがない。

N4080: File System Immediate Issues

filesystemライブラリのTS案に持ち上がっている問題のうち、Rapperswil会議で対応が決定されたもの一覧。

N4081: C++ Extensions for Library Fundamentals, Working Draft

標準ライブラリに対する拡張のTSのドラフト。

N4082: Programming Languages — C++ Extensions for Library Fundamentals

N4081のPDF版?

N4083: Editor's Report — Library Fundamentals TS V1

ライブラリ拡張TSの編集者による編集報告書。

N4084: C++ Extensions for Library Fundamentals, Version 2, Working Draft

ライブラリ拡張TSのバージョン2。変更は軽微の模様。

N4085: Editor's Report — Library Fundamentals TS V2

ライブラリ拡張TSのバージョン2に対する編集者の変更報告書。

N4086: Removing trigraphs??!

規格文面からトライグラフを除去し、Annex C(deprecatedな機能)に移す提案。

トライグラフはまともに使われていなかった。alternative tokensはいまだに残っているので注意。

N4087: Multidimensional bounds, index and array_view, revision 3

Viewable(ストレージのサイズと専用へのポインターを返すsizeとdataメンバー関数を持つ型)から、見かけ上多次元配列を作り出すラッパーライブラリの文面案。

[PDF注意] N4088: Task Region R3

strict fork-join task parallelismというパラダイムによる並列実行ライブラリ、task regionの提案。

task regionはMicrosoftのPPLやIntelのTBBを土台に設計されている。また、IntelのC++拡張機能であるClik Plusからも影響を受けている。

[危険PDF] N4089: Safe conversions in unique_ptr<T[]>, revision 2

unique_ptr<T[]>で、constを付け足すような安全なポインターの型変換を許可する変更。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

この記事は、ドワンゴに12時に出勤して、2時間執筆し、2時間昼寝して、1時間執筆して書き上げた。

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

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

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

2014-08-29

2014-07 post Rapperswil mailingのレビュー: N4070-N4079

またもや次の論文10本

N4070: Improving the specification of the vector execution policy in Parallelism TS

Parallel TSのベクトル実行アルゴリズム内で実行される関数が同期をすることは禁止されている。しかし、標準ライブラリの関数のうち、同期を行わない保証のある関数なのかということは、明記していない。

これを解決するために手始めにそのことを警告する文面を追加する提案。

N4071: Technical Specification for C++ Extensions for Parallelism, Working Draft

C++のアルゴリズムに並列実行と並列ベクトル実行版のオーバーロードを追加して拡張するTSのドラフト。

従来のアルゴリズムに、タグを指定することで、シーケンシャル実行、並列実行、並列ベクトル実行を切り替えることができる。

// N4071提案
#include <experimental/execution_policy>
#include <experimental/algorithm>

template < typename Iterator >
void f( Iterator first, Iterator last )
{
// シーケンシャル実行
    std::stable_sort( std::seq, first, last ) ;

// 並列実行
    std::stable_sort( std::par, first, last ) ;

// 並列ベクトル実行
    std::stable_sort( std::par_vec, first, last ) ;
}

N4072: Fixed Size Parameter Packs

パラメーターパックの個数を指定できる機能、固定数パラメーターパックの提案。

// N4072提案

// argsは個数3、型はint型
void f( int ...[3] args ) ;

// argsの個数は3
template < typename ... Types >
void g( Types ... [3] args ) ;

// Argsの個数は3
template < typename ...[3] Args >
void h( Args ... args ) ;

T ...[N] pは、N個のT型のパラメーターパックpである。Nは定数式でなければならない。

いったいこの機能何に使うのか。論文では、いくつかの利用例を挙げている。

指定した個数の実引数を取りたい場合を考える。

struct my_vector3 {
    // ...
    my_vector3(int x, int y, int z)
        : values{x, y, z} {}
};

こんなクラスがあるとする。my_vector4とかmy_vector10などを手で書くのではなく、my_vector<N>のように、テンプレートで個数を指定したい。

実は、これは現行のC++14の枠内で行うのは、とてつもなく面倒で長ったらしいコードを書かなければならない。しかし、固定数パラメーターパックがあれば、以下のようにあっさりと書ける。


template<unsigned int N>
struct my_vector {
    // ...
    my_vector(int...[N] v) : values{v...} {}
};

指定した個数のある型の引数を取りたい。

void foo3( int, int, int )のような関数があるとする。ここで、foo4のような関数を手で書くのではなく、foo<N>のように、テンプレートで個数を指定したい。

しかし、これをC++14で簡潔に書く方法がない。固定数パラメーターパックならば、以下のように簡潔に書ける。


template<unsigned int N>
void foo(int...[N] v);

あるいは、単に機械的なパターンを手で書く代わりに使うこともできる。


void foo(int x, int y, int z, int w) { do_magic(x, y, z, w); }

template<typename A, typename B, typename C, typename D, typename E>
void bar(A a, B b, C c, D d, E e);

のような機械的なパターンのあるコードは、


void foo(int...[4] v) { do_magic(v...); }

template<typename...[5] T>
void bar(T... v);

このように書くことができる。

この提案では、実引数推定で、固定数を推定することができる。

例えば、以下のような例は実引数推定できる。

template < unsigned int N >
void f( int ... [N] ) { }

f( 1, 2, 3 ) ; // Nは3と推定される

template<unsigned int N> void f(int, int...[N]) {}
f(1,2,3); // Nは2と推定される

template<unsigned int N, unsigned int M> void f(int...[N], int...[M]) {}
f<2>(1,2,3); // Mは1と推定される。Nは推定できないが、明示的に指定されている

例えば、以下のような場合は、固定数パラメーターパックで実引数推定できない。


template<unsigned int N> void f(int...[N], int) {}
f(1,2,3);

template<unsigned int N> void f(int...[N], int...[N]) {}
f(1,2,1,2);

template<unsigned int N> void f(int...[N*2]) {}
f(1,2,3,4);

[暗い未来を予感させるPDF] N4073: A Proposal to Add 2D Graphics Rendering and Display to C++

C++の標準ライブラリグラフィックライブラリを追加することに対する考察と提案。cairoに機械的な変換を書けてC++風の設計にしている。

[Let PDF-writer die in agony] N4074: Let return {expr} Be Explicit, Revision 2

return {expr}の意味を、return-type var {expr} ; return var ;と同じ意味にしようという提案。

以下のようなコードを考える。

struct very_long_type_name
{
    explicit very_long_type_name(int) { }
} ;

very_long_type_name f()
{
    return { 0 } ; // ill-formed 
}

このコードを合法にする提案だ。

return文のオペランドを{}で囲んだ場合、戻り値の型のローカル変数を直接初期化して返すのと同等の効果にしようという提案である。

関数の戻り値の型と、その関数内のreturn文とは、関数の中だけの話だから、十分に明示的である。{}で囲むというのも明示的である。

very_long_type_name f()
{
    return very_long_type_name{0} ;
}

これはいかにも冗長だ。

[無駄にPDF] N4075: Centralized Defensive-Programming Support for Narrow Contracts (Revision 5)

防衛的プログラミングと称し、assertマクロの高級版を追加する提案。

assertに引っかかった時の挙動を指定できたり、テストできたりと、色々と高機能だが、Cプリプロセッサーマクロを使うという時点で、筆者は反対する。

N4076: A proposal to add a generalized callable negator (Revision 4)

汎用的なnegator、not_fnの提案。

not1やnot2は引数がひとつか二つのとても制約の強い関数オブジェクトしからっぷできなかった。not_fnは、Variadic Templatesや最近のメタプログラミング技法を使い、任意個の引数を取る任意の呼び出し可能なオブジェクトをラップできる。

// N4076提案
auto f() { return true ; }

int main()
{
    auto n =  std::not_fn(f) ;
    n() ; // false
}

N4077: Experimental shared_ptr for Library Fundamentals TS

TSに提案されるライブラリが使うために、実験的なshared_ptr実装である<experimental/memory>と、独立したshared_ptr, weak_ptr, owner_less, enable_shared_from_thisの提案。

詳しい経緯はN4041にかかれているが、TS用の拡張として既存の標準ライブラリを使う場合、サードパーティによる規格準拠の実装が作れないという問題がある。そこで、規格準拠な実験的実装を行えるように、必要な標準ライブラリを、同一機能であるが、別ヘッダー、別名前空間の別ライブラリとして定義する提案。

N4078: Fixes for optional objects

Issaquah会議で指摘された、Optionalのドラフト文面に存在する問題の修正。どれも小粒な文面上の修正となっている。

N4079: C++ Standard Library Issues Resolved Directly In Rapperswil, 2014

rapperswil会議で修正された標準ライブラリに持ち上がっている問題集。

些細な文面上の誤りの修正が多い。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

今日は都合で、いつもとは違い丸ノ内線に乗って通勤したが、明らかに周囲の背広集団から浮いている変わった服装と目立つ話し方をする人間を見かけた。銀座駅で降りて会社に向かったが、エレベーターの中で再開した。みると、ドワンゴの社員証を首から下げている。

さては同僚であったのか。

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

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

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

あるポルノサイトのアクセス解析によるOSシェア率によれば、GNU/Linuxユーザーは1.7%

この記事は大真面目に書いた。

[リンク先に危険な画像はない] OS Battle – Porn by the Platform

Phoronixに載っていて知ったのだが、有名なポルノサイトであるpornhubが、Webサイト上のトラフィックのOSに特化した解析結果をブログで大真面目に公開している。その例に習って、筆者も大真面目に内容の紹介と、大真面目な感想を書こうと思う。

まず、デスクトップOSのシェア率は、Windowsが85.5%、Macが10.9%、GNU/Linuxが1.7%だそうだ。

いかにWindows優位な市場とはいえ、Macのシェア率が世間一般より低いように思われる。Macユーザーはあまりポルノを必要としないのであろう。

Windowsの具体的なバージョンは、Windows 7が一番多く、ついでWindows XPである。なんと、Windows NTやWindows 2000を使っているものもいるらしい。また、月数千人は、Windows 95や98の利用者がいるそうだ。

モバイルOSのシェア率は、Androidが48.34%で、ついでiOSが40.60%だそうだ。

これも、実際の市場のシェア率とは異なり、iOSが低すぎるように思う。やはりiOSの利用者はあまりポルノ閲覧を必要としないに違いない。

タブレットOSでは、iOSが圧倒的なシェアを占めている。

これはどうみるべきだろう。Apple製品のユーザーはあまりポルノ閲覧を必要としないのではないかと思っていたが、Appleのタブレットユーザーはポルノを閲覧するらしい。あるいは、iPadの市場シェアが多すぎるためにこうなったのだろうか。

ゲーム機のシェア率もまとめられている。今のゲーム機はブラウザーを搭載している。ブラウザーを搭載しているということは、当然pornhubを閲覧できる。

ゲーム機のシェア率は、PlaystationとXBoxが二強である。Nintendo Wiiでポルノを閲覧する人はあまりいないらしい。

人気の検索クエリーで面白いのは、GNU/Linuxユーザーの検索クエリーは、インド女優の名前やインド人が多いらしい。なぜかというと、GNU/Linuxの利用者のかなりがインド人だからである。

OSごとの国別の閲覧者は、WindowsとMacではアメリカ合衆国が圧倒的であるが、GNU/Linuxでは、二位に圧倒的にインドが上がってくる。どうやら、インドのポルノ閲覧者におけるGNU/Linuxの普及率が相当に高いらしい。

ソースがソースなだけに、なかなか興味深いアクセス解析結果だ。

ちなみに、ここ一ヶ月のこの本の虫ブログ閲覧者のOSシェアは、Windowsが46.74%、iOSが20.58%、Macが13.40%、Androidが12.14%、GNU/Linuxが6.53%である。6割がデスクトップ、3割がスマートフォン、タブレットは4%ほどだ。

2014-08-25

大豆肉を入手した

アマゾンで、大豆肉を注文した。注文したのは、 マイセン まるっきりお肉フィレタイプ 200gと、 マイセン まるっきりお肉ミンチタイプ 300gである。

大豆と玄米から作られており、肉ではないが、肉のような食感を持つという。そろそろ料理に新しい刺激が欲しいので、試しに買ってみた。どのように料理するか考えている。面白そうなので人を呼んで食べさせたい。

ミンチタイプは、ミートソースにでもしようかとおもう。フィレタイプはどう料理しようか。

コンピューターを新調したいが踏み切れない

私は今、貰い物のラップトップをメインのコンピューターとして使っている。そろそろコンピューターを新調したいと思いつつも、なかなか踏み切れないでいる。

問題は、今のラップトップは、計算性能だけを考えれば、何の不満もないことだ。私の日々の作業に対して、CPUは十分に速く、メモリは十分な容量で、GPUだけはIntelのHD Graphics 3000なのですこし不満はあるが、私はグラフィック性能をそれほど必要としないので問題はない。ストレージもSSDだ。

不満は、純粋な計算性能以外の点にある。

  • 組み込みディスプレイの解像度が低すぎる。また、外部ディスプレイ出力もHDMIしかなく、規格のバージョンも古いので4k出力に対応していない。
  • WiFiも5Ghz帯に対応していない。

結局、不満点といえば、これぐらいしかないのだ。このためだけにラップトップを変えるというのはどうだろうか。しかし、ディスプレイもWiFiもその性能は致命的に低くはないものの、より優れたものが市場に出回っている現状だ。CPUの処理能力やメモリ容量がそれほど問題にならなくなっている今、純粋な演算能力以外のスペックが重要になってくる。

では、現時点で買うとしたら何であろうか。

東芝のdynabook T954は、なんと組み込みのディスプレイが4Kであるという。15.6インチのディスプレイが3840x2160の解像度を持つという。しかし、それ以外のスペックになんとも魅力がない。

Lenovo ThinkPadは、組み込みディスプレイのサイズが3kと、すこし劣るが、無線LANモジュールが選択できるという利点がある。しかし、Lenovoには悪い思い出しかないので、Lenovo製品を買うのは避けたい。不自由なnVidiaのGPUが載っていて、しかもOptimusであるのが最悪だ。

もうしばらく様子見がいいだろう。しかし、この様子見というのは問題のある態度だ。結局、我慢しようと思えば我慢出来てしまう以上、いつまでも最新のコンピューターの性能を体験できない。性能で作業効率が上がるのであれば、いい投資であるのだが。

GNU/Linuxで動くデバッガーのGUIフロントエンド比較

13 Linux Debuggers for C++ Reviewed | Dr Dobb's

drdobbs.comの記事に、GNU/Linuxで動くデバッガーのGUIフロントエンドを比較している記事があったので、メモ代わりに紹介しておく。

2014-07 post Rapperswil mailingのレビュー: N4060-N4069

[PDFも変更されるべき] N4060: Changes to vector_execution_policy

Parallelism TSに対する、ベクトル実行の定義を変更する提案。

並列実行とベクトル実行は、ループの個々の繰り返し単位を、順不同で実行するものである。このため、コードが実行される順序という保証がなくなる。また、例外や同期などの処理も行えないという制約がある。

並列実行ポリシー(parallel_execution_policy)とは、スレッド風の実行エージェントよりループを並列に実行するものである。ベクトル実行ポリシー(vector_execution_policy)とは、SIMDやGPUによる実行エージェントによりループを並列に実行するものである。

しかし、現実のアーキテクチャが提供するSIMD命令を考えると、SIMDによる1スレッド内のベクトル実行による実行エージェントとしては、現在のベクトル実行ポリシーの定義は、必要以上に制約が強すぎる。制約というのは、順序の無保証だ。1スレッド内のSIMD命令による並列実行では、先のループ単位の実行が、後のループ単位を追い越さないという保証を与えることができる。

当時、parallelism TSを議論している際には、実行ポリシーを、シーケンシャル、パラレル、ベクトル、パラレル+ベクトルに分類していた。しかし、純粋なベクトル実行に与えられるこの順序保証の需要があるのかどうか不明であったため、標準化委員会は、純粋なベクトルを廃して、パラレル+ベクトルを、ベクトルと称した。これにより、現在のベクトル実行はパラレル実行の制約をさらに強くする形で定義され、GPUなどの実行エージェントもサポートできるようになった。

ところが、Intelが最近、顧客のコードを検証したところ、ベクトル実行の順序保証を活用したコードが、すでに数多く書かれていることが判明した。コードが存在する以上、需要はある。そこで、この論文は、現在のパラレル+ベクトルとなっているベクトルの定義の変更を提案している。また、この変更は、もはやTSを変更する時間的余裕が無いため、TS本体への適用ではなく、TSに対する参考として公開する形をとっている。

提案は二つある。

最小限の変更による提案は、現在のベクトルは、実際にはパラレル+ベクトルなので、vector_execution_policyをparallel_vector_execution_policyに、vecをparvecに改名するものである。

理想の提案は、ベクトル実行を純粋にベクトル実行として、並列実行とは別に、最小限度の制限で定義するものである。

結局、こんなことになったのも、コードが表に出ないからだ。情報が表に出ないからだ。公開されていない情報は、存在しないも同義である。存在しないものには需要があるわけがない。標準化委員会が新機能を設計するにあたって需要を認識できるわけがない。C++は、長年、C++に利害関係を持つものが標準化委員会に参加し、知見を出し、規格に貢献して今の位置に至ったのだ。標準化委員会に参加しないどころか、情報すら表に出さないのでは、その分野は永久に標準化されることはない。

お前ら表にでろ。

[PDFを最小公倍数にするな] N4061: Greatest Common Divisor and Least Common Multiple, v3

最大公約数と最小公倍数を求める関数、gcdとlcmを<numerical>に追加する提案論文の第三版。

ちなみに、constexpr関数であり、さらに任意の整数型に対して使える関数テンプレートとなっている。

実装は短いが、標準でほしい関数ではある。

// 実装例
template< class T >
constexpr auto abs( T i ) -> std::enable_if_t< std::is_integral<T>{}(), T >
{ return i < T(0) ? -i : i; }

template< class M, class N = M >
using common_int_t = std::enable_if_t< std::is_integral<M>{}() and std::is_integral<N>{}()
, std::common_type_t<M,N>
>;

template< class M, class N >
constexpr common_int_t<M,N> gcd( M m, N n )
{ return n == 0 ? abs(m) : gcd(n, abs(m) % abs(n)); }

template< class M, class N >
constexpr common_int_t<M,N> lcm( M m, N n )
{ return m == 0 or n == 0 ? 0 : (abs(m) / gcd(m,n)) * abs(n); }

int main()
{
    gcd( 2, 3 ) ; // 1
    gcd( 10, 15 ) ; // 3

    lcm( 2, 3 ) ; // 6
    lcm( 8455, 99 ) ; // 837045
}

前回の論文からの変更はそれほど特筆するものはないようだ。

[PDFは平行世界に飛ばされるべき] N4063: On Parallel Invocations of Functions in Parallelism TS

Parallerism TSで、sortなどのユーザーの提供する関数オブジェクトを取るアルゴリズムを並列実行した時に、その関数を並列に呼び出せるのかどうかということについて、文面は何も規定していないということを、Hans Boehmが指摘した。この問題を議論するために、論文を書き、Rapperswil会議で議論した結果の改訂版。

BinaryPredicate, Compare, BinaryOperationは、実引数を変更してはならないという文面が付け加えられる。これにより、安全に並列に呼び出すことができる。

N4064: Improving Pair and Tuple (Revision 2)

tupleとpairで、以下のようなコードが動かないので、動くように変更する提案。

std::tuple<int, int> pixel_coordinates() 
{
  return {10, -15};  // Oops: Error
}

struct NonCopyable { NonCopyable(int); NonCopyable(const NonCopyable&) = delete; };

std::pair<NonCopyable, double> pmd{42, 3.14};  // Oops: Error

これは当然動いてもいいコードで、実際、このコードが動くようにtupleやpairを実装することが可能である。なぜ動かないのか。

実は、当時ライブラリを設計していた時の議論で、危険な暗黙の型変換を防止するために、必要以上に制約を加えてしまったのだ。

更に調査を進めると、tupleは、TR1の複数のテンプレート仮引数を使う設計から、標準規格のVariadic Templatesを使う実装に変更する際に、極めて重要な規程が抜け落ちてしまっていた。

template <class T1 = unspecified ,
          class T2 = unspecified ,
          ...,
          class TM = unspecified >
class tuple
{
public:
  tuple();
  explicit tuple(P1, P2, ..., PN); // iff N > 0
  […]
};

コメント部分の、"iff N > 0"という制約が、tupleのVariadic Template版の文面を作成する際に、抜け落ちてしまったのだ。

そして、論文では、危険すぎる暗黙の型変換は防ぎつつ初期化が行える、Pefect Initializationというテクニックを紹介している。

#include <type_traits>
#include <utility>

template<class T>
struct A {
  template<class U,
    typename std::enable_if<
      std::is_constructible<T, U>::value &&
      std::is_convertible<U, T>::value
    , bool>::type = false
  >
  A(U&& u) : t(std::forward<U>(u)) {}

 template<class U,
    typename std::enable_if<
      std::is_constructible<T, U>::value &&
      !std::is_convertible<U, T>::value
    , bool>::type = false
  >
  explicit A(U&& u) : t(std::forward<U>(u)) {}
  
  T t;
};

非explicitコンストラクターとexplicitコンストラクターを、SFINAE技法で制約を加えることにより、以下のように、危険な暗黙の型変換は防ぎつつ、自然に初期化ができるようになる。

struct Im{ Im(int){} };
struct Ex{ explicit Ex(int){} };

A<Im> ai1(1); // OK
A<Im> ai2{2}; // OK

A<Im> ai3 = 3;   // OK
A<Im> ai4 = {4}; // OK

A<Ex> ae1(1); // OK
A<Ex> ae2{2}; // OK

A<Ex> ae3 = 3;   // Error
A<Ex> ae4 = {4}; // Error

なぜ複数の引数を取るコンストラクターがexplicitではないとまずいのかということを、以下のような面白いコードで説明している。


#include <tuple>
#include <chrono>
#include <iostream>

using hms_t = std::tuple<std::chrono::hours, std::chrono::minutes, std::chrono::seconds>;

void launch_rocket_at(std::chrono::seconds s)
{
  std::cout << "launching rocket in " << s.count() << " seconds!\n";
}

void launch_rocket_at(hms_t times)
{
  using namespace std;
  launch_rocket_at(get<0>(times) + get<1>(times) + get<2>(times));
}

int main()
{
  using namespace std;
  launch_rocket_at(make_tuple(1, 2, 3)); // #1: ヤバイ
  launch_rocket_at({1, 2, 3});           // #2: もっとヤバイ
  using namespace std::chrono;
  launch_rocket_at(make_tuple(hours(1), minutes(2), seconds(3))); // #3: すっげーわかりやすい
  launch_rocket_at({hours(1), minutes(2), seconds(3)});           // #4: これもわかりやすい
  launch_rocket_at(hms_t{1, 2, 3});                               // #5: これでもいい
}

explicitではないと、暗黙の型変換が仕事をしすぎてしまうので、型による警告ができない。

N4065: make_array, revision 2

arrayを返すmake_arrayの提案

// N4065提案
// std::array< int, 4 >
auto a = std::make_array( 1, 2, 3, 4 ) ;

前回のN4031からの変更点として、型を明示的に指定する場合、make_arrayではなく、array_ofを使うようになった。

// N4031提案
auto a = std::::make_array<long>( 1L, 2L ) ;

// N4065提案
auto b = std::array_of<long>( 1L, 2L ) ;

文字列リテラルをcharのarrayにするto_arrayに変更はない。

// N4065提案
// std::array< char, 6 >
auto a = std::to_array("hello") ;

実装例がGitHubに上がっている。

Factory function of std::array

N4066: Delimited iterators (Rev. 3)

ostream_iteratorにはデリミターを指定できる。ただし、このデリミターは、実際にはサフィックスというべきである。

std::vector<int> v = { 1, 2, 3 } ;

std::copy( v.begin(), v.end(), std::ostream_iterator<int>( std::cout, ", " ) ;

このコードを実行した結果の出力は、"1, 2, 3, "となる。

この論文は、正しくデリミターとして働く、つまり要素の間にしかデリミターを出力しない、ostream_joinerというライブラリを提案している。上記コードでostream_iteratorの代わりに使うと、"1, 2, 3"となるクラスだ。

N4067: Experimental function etc.

標準ライブラリに対する拡張TSである、N4023: Working Draft, C++ Extensions for Library Fundamentalsでは現在、std::functionなど、標準規格の標準ライブラリを直接参照している。これはTSをサードパーティが実装するのに都合が悪い。そのため、function, promise, packaged_taskと同等のものを、std::experimental名前空間に存在させるための文面変更案。

N4068: Toward More Expressive Iterator Tags

Rapperswil会議で、N3976で提案されている多次元配列ビューを議論したところ、この論文で提案されているイテレーターの一部は、random access iteratorであると自称しながらも、実はforward iteratorの要件すら満たしていないことが指摘された。

これは、現在のイテレーターカテゴリーの要件が粒度が荒すぎるのが問題である。もっと細かい要件に分割して、要件を組み合わせて指定できるべきである。そのための方法としては、議論ではコンセプトが最適であるという方向で一致したが、とりあえずイテレータータグで対応する提案。

論文で提案されている細かい粒度のイテレータータグは以下の通り。

struct referene_tag { } ;

*iterがプロクシーではなく、実際にvalue_type&であり、その値はイテレーターによりキャッシュされたものではないこと。

struct lvalue_tag { } ;
struct rvalue_tag { } ;

*iterが変更可能なlvalue、もしくはrvalueであること(両方であることもあり得る)

struct equality_comparable_tag { } ;

iter1 == iter2がwell-formedであること

struct multipass_tag { } ;

iter1 == iter2であれば、++iter1 == ++iter2、かつ、&*iter1 == &*iter2であること

struct decrementable_tag { } ;

--iterがwell-formedであること。

struct random_move_tag { } ;

iterが任意の距離を移動でき、less-than comparableであること

さらに、これらのタグをまとめて指定できるbasic_iterator_tagを以下のように定義する。

template < typename ... Tags >
struct basic_iterator_tag { } ;

すると、既存のイテレータータグは、以下のように定義できる。

    typedef basic_iterator_tag<lvalue_tag> output_iterator_tag;

    typedef basic_iterator_tag<rvalue_tag, equality_comparable_tag>
              input_iterator_tag;

    typedef basic_iterator_tag<reference_tag,
                               lvalue_tag,
                               rvalue_tag,
                               equality_comparable_tag,
                               multipass_tag>
              forward_iterator_tag;

    typedef basic_iterator_tag<reference_tag,
                               lvalue_tag,
                               rvalue_tag,
                               equality_comparable_tag,
                               multipass_tag,
                               decrementable_tag>
              bidirectional_iterator_tag;

    typedef basic_iterator_tag<reference_tag,
                               lvalue_tag,
                               rvalue_tag,
                               equality_comparable_tag,
                               multipass_tag,
                               decrementable_tag,
                               random_move_tag>
              random_access_iterator_tag;

これを使えば、例えばvector<bool>のイテレータータグは以下のように定義できる。

    typedef basic_iterator_tag<lvalue_tag,
                               rvalue_tag,
                               equality_comparable_tag,
                               multipass_tag,
                               decrementable_tag,
                               random_move_tag>
              vector_bool_iterator_tag;

ドワンゴ広告

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

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

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

2014-08-21

ファイルシステムの破損?、付記using宣言とusingディレクティブの違い

ここ最近、筆者の使っているコンピューターは不可解な挙動に見舞われてきた。再現性なく機能停止に陥るのだ。

機能停止、というのはなんとも大雑把な言葉だが、そうとしか表現しようのない状態になる。

何の前触れもなく、急に画面が真っ暗、あるいは単色になり、そのまま止まってしまう。ターミナルに切り替えることもできず、SysRqすらきかない。

しかし、Ubuntuのバグ報告をみても、それらしき現象はない。こんなにも頻繁に遭遇しているのに何故だろうか。ハードウェアの故障であろうか。

ところで、今日、不思議な現象に出くわした。bashのglob上からみると存在するはずのファイルが、なぜか実際には存在していないという現象である。つまり、echo *とlsで表示されるファイルが異なる。globからしか見えないファイルがある。不思議だ。例に再起動してみたが、やはりglobでしか見えないファイルがある。

はて・・・、これはいったい。

fsckでもかけてみようか。GNU/Linuxで起動時にfsckをかけるには、ルートディレクトリにforcefsckというファイルを作成すればよい。

sudo touch /forcefsck
shutdown -r now

fsckは一瞬で終わった。再起動後、glob経由でしか見えないファイルはなくなっていた。

いったい何だったのだろうか。さて、再現性なく発生する機能停止はなくなるだろうか、しばらく様子を見よう。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

今、N4064のPerfect Initializationを理解するのに手間取っている。

ドワンゴ社内の近所の席から、GCCでusing宣言が動かないという声を耳にして、そんなバカなとコードをみたところ、

using namespace std::vector ;

なるコードが書かれていた。これはusing directiveだ。

usingディレクティブ

using directiveとは、書かれたスコープに、指定された名前空間スコープ内の名前を持ち込むものである。

// usingディレクティブ
namespace NS { int x ; }

void f()
{
    using namespace NS ;
    x ; // well-formed, NS::x
}

当然、usingディレクティブで指定する名前は、名前空間名である。stdは名前空間名である。std::vectorは名前空間名ではない。

using宣言

using declarationとは、書かれたスコープに、指定された名前を持ち込むものである。

// using宣言
namespace NS
{
    int x ;
    int y ;
}

void f()
{
    using NS::x ;
    x ; // well-formed, NS::x
    y ; // ill-formed. 名前yは見つからない
}

当然、using宣言で指定する名前は、名前空間名であってはならない。

確かに、多くのプログラマは言語を表面的な理解だけで使っているし、それでいい。普通のプログラマーは規格の細部に関わるよりも、動くコードを書くことに注力するべきである。

実際、usingディレクティブとusing宣言は、どちらもusingキーワードを使うし、その意味も、スコープに名前を持ち込むという似通った性質を持つので、混同されやすいのだろう。

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

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

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

fork()は失敗するんだぜ、覚えときな

fork() can fail: this is important

あー、fork()のことね。プロセスがもっとプロセス作るためのやつな。いや、他にもプロセス作る方法はあるけどな。ま、面白い話がもうひとつあるから聞かせてやるよ。

forkは失敗するんだぜ。分かってるか? マジで分かってるか? マジだぜ。forkは失敗するもんだ。mallocと同じさ。失敗することもある。そんなに頻繁にってわけじゃないけどさ、でも失敗したら、無視できっこないぜ。ちっとは脳みそ働かせなきゃならん。

forkが0を返したら、そいつは子プロセスで、親なら正数を返すってことは、みんな知ってるよな。その値は子のpidだ。こいつを保存しといて、あとで使うってわけだ。

失敗を確認しない場合どうなるか知ってるか? そうだよ。お前多分、"-1"(forkのエラー通知)をpidとして扱ってるんだろ。

さて、問題の始まりだ。本当の問題は、シグナルを送るときにやってくるんだ。たとえば、お前、子プロセスを終了させたいとするだろ。

おまえkill(pid, signal)してるか? いや、kill(pid, 9)してるかな。

お前、pidが-1だったときどうなるか知ってるか? マジで知ってるべきだぜ。ヤバイからな。マジモンにヤバイぜ。

...

...

...

ほら、俺のLinux機からkill(2)のmanページをコピペしてやるよ。

もし、pidが-1と等しい場合、呼び出したプロセスがシグナルを送る権限を持つすべてのプロセスにsigが送られる。ただし、process 1(init)を除く

お分かりか? "pid -1"をkillするってのは、シグナル送れるすべてのプロセスを虐殺するってことだぜ。お前がrootなら、ほとんどすべてだ。お前は生き残るし、initも生き残る。だがそれだけだ。他は全部死ぬ。

お前、プロセスを管理するコード書いてるか? お前、テキストコンソールのgetty/login(initによって再起動される)とプロセスマネージャー以外ぜんぶ死んだ経験はないか? お前カーネルのoomkillerの責任だと思ったか?

そいつぁ濡れ衣かもしれないぜ。kill -1してないか今すぐ確認しろ。

Unix: この業界を飽きさせないために十分な数の落とし穴とトラバサミが仕掛けられている

ドワンゴ広告

この記事はドワンゴ出勤前に自宅で書いた。やはり作業はL字型の広い机で行うに限る。

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

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

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

2014-08-20

Sparc上のNetBSDでejectするとパニックに陷る問題の修正

CVS commit: src/sys/arch/sparc/dev

Modified Files:

src/sys/arch/sparc/dev: fd.c

Log Message:

fd(4)をオープンすることによるpanicを修正。違うポインターをmemset()に渡していたことが原因。

なんでこの18年もののバグがこれ以前に問題を引き起こさなかったのか謎だが(少なくとも、オレの昔の5.99.23カーネルでは動く)、おそらくはgcc 4.8がメモリ確保をよりアグレッシブに行うために表面化したのではなかろうか。

この問題はNobuyoshi Satoがfd(4)にeject(1)を試みた結果、発見された。

2014-08-19

Haskellの入門書を読んだ

先週、一週間丸々有給とリフレッシュ休暇を取って、都合9日間の休暇を作り出し、京都の吉田寮に遊びに行った。まだまだ読むべき論文がたまっているが、たまにはプログラミングもネットも一切忘れて息抜きをしてもよいだろう。吉田寮でだらだらと過ごすうちに、ふと、吉田寮に転がっていたHaskell入門書を読んでみた。

その結果、Haskellは、なかなか興味深い言語であることがわかった。

ここ数年、C++に提案されている新機能の多くに、Haskellにすでに存在する機能を強く連想させるものがある。なるほど、知り合いのC++プログラマーが、数年前にHaskellに手を出していたのは、そういう理由があったのか。

Haskellという言語に対する感想であるが、あまりに理想的すぎる印象を持った。なるほど、確かにコンパイラーの最適化が究極に賢ければ、Haskellのパフォーマンスは素晴らしい物になるだろう。しかし、現実にはコンパイラーがそこまで賢くなることは、近い将来には期待できない。

かつ、Haskellの入門書にかかれているサンプルコードは、極めて簡潔で理論的で言語的に美しいものであるが、その処理方法をよく考えてみれば、極めて効率が悪い処理である。

プログラミングの世界は、理論が数十年先行している。理論的には決着がついているものを、メモリやアドレスやキャッシュやパイプラインや同期といった制約のある現実のハードウェアに落としこむ必要がある。かつては、オブジェクト指向もそのような理論の一つだった。

Haskellの実用度は疑問ながら、その理論には痛く感心した私に、吉田寮にいる生物博士が言うのである。「次はCommon Lispの番だよ。遅かれ早かれC++はマクロを取り入れなければならないから、Common Lispはやらなければならない」と。C++風にまともなマクロを取り入れるのであれば、静的リフレクション機能として入るだろう。

完全にプログラミングから離れて遊ぶための休暇だったが、何の制約もない時間を確保できたことによって、かえって普段は手を出すことのない分野を学ぶことができた。やはり休暇は重要だ。

さて、Haskellの入門書を一冊読んだものの、まだ実際にコードを書いてはいない。やはりコードも書かねばなるまい。しかし、GNU/Linux環境でのx86-64アセンブリも書いてみたいし、C++の論文もたまっているし、C++の入門書も執筆してみたい。そういえば、勉強会のスライド資料作成もしなければならない。やることは多いが、とりあえず論文を片付けよう。

ドワンゴ広告

この記事はドワンゴの仕事をサボるために書いた。

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

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

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

2014-08-18

2014-07 post Rapperswil mailingのレビュー: N4052-N4059

>2014-07 post Rapperswil mailingをレビューしていく。次の論文集の発表までに終わらせたいものだ。

N4052: WG21 2014-06-06 Telecon Minutes

2014年6月6日に行われた電話会議の議事録。

N4053: WG21 2014-06 Rapperswil Minutes
[PDF?] N4054: PL22.16 2014-06 Rapperswil Minutes

2014年6月の16日から21日にかけて行われたRapperswil会議の議事録。二つの議事録の内容はほぼ同一のようだ。

N4055: Ruminations on (node-based) containers and noexcept

vectorを、ムーブコンストラクターがnoexceptな要素と、そうでない要素でパフォーマンスを比較したところ、極端な違いがある。これは、vectorは強い例外保証のために、内部のストレージのサイズを拡大するときに、要素の移動にstd::move_if_noexceptを使ってムーブを試み、noexceptではないムーブコンストラクターを持つ要素の場合は、コピーにフォールバックするからである。

これは、std::vector< std::list<int> > のようなネストされたコンテナーの場合、より極端になる。

なぜlistのようなノードベースのコンテナーのムーブはnoexceptではないのか。listはその設計上、要素の最後に番兵が必要だ。なぜならば、end()はデクリメントできなければならず、デクリメントできるだけの何かを指していなければならないからだ。

さて、この番兵用のノードをどのように確保するかという問題がある。動的に確保するのは普通の方法だ。しかし、どうせ一つしか必要なく、またlistクラスのオブジェクトの生存期間中、常に必要になるので、クラスのオブジェクトのデータメンバーに含めてしまうというのはどうか。これにより、番兵用のノードを動的確保する必要がなくなる。

しかしその場合、listをムーブやswapした時に、番兵用のノードまでムーブやswapできなくなってしまう。つまり、番兵をオブジェクトに含める自走では、listのend()が絡むイテレーターのペアが、ムーブやswap時に無効化されてしまう。listの素晴らしい点は、イテレーターが無効化しないという強い保証にあるというのに。

実は、当初から、listをswapすると、end()イテレーターが無効になるかもしれないと、規格上は定義されていた。そのため、swapした結果、end()イテレーターが無効になっても、規格準拠の標準ライブラリの実装である。問題は、既存の主要なSTLの実装に、swapでend()が無効化しない実装があり、利用者はその挙動に依存してしまっている。それに、先程もいったように、いくら原稿規格が許しているからといって、listでイテレーターが無効になるというのは、listの強いイテレーター保証に対して違和感がある。

結局これは、ムーブ後の標準ライブラリのオブジェクトの状態は、そのオブジェクトは規定されているように使い続けられるという保証があるためにおこる。vectorのストレージ拡大の場合は、そもそもムーブ後のオブジェクトは即座に破棄するので、そもそも必要ない。

論文では、いくつかの改善案を示しているが、どれも根本的に問題を解決するものではないように思われる。

[PDF注意] N4034の破壊的ムーブも、この問題を解決するために提案されたのであろう。

N4056: Minimal incomplete type support for standard containers

STLのコンテナーを再帰的なデータ構造で使えるようにする提案。

// N4056提案
struct X
{
    std::vector<X> vec ;
} ;

のようなコードが書けるようになる。具体的には、コンテナーの要素型として、不完全型を認める制限緩和の提案になる。クラスは定義終了である}をもって初めて定義されたとみなされるので、クラス定義の中では、クラス名は不完全型なのだ。

既存の主要なSTLの実装は、かなりのコンテナーで要素型に不完全型を認めている。不完全型でも問題がないかどうかは、実装次第なのだ。Clangのlibc++の実装は、他の実装ならば認めているコンテナーでも、不完全型を認めていないコンテナーがある。

この論文の初版では、すべてのコンテナーに不完全型を認めようという提案であったが、Rapperswil会議で、ひとまず手始めにvectorとlistとforward_listに対しては不完全型を認める制限緩和をしようというコンセンサスが得られたらしいので、そのような提案になっている。

libc++の実装は、この提案を受けて変更が入っている。

[PDFを伝播する必要はない] N4057: A Proposal to Add a Const-Propagating Wrapper to the Standard Library

constメンバー関数は、ポインター風データメンバーの非constメンバー関数を呼び出すことができてしまう。

struct A
{
    void bar() const { } // #1
    void bar() { } // #2
} ;

struct B
{
    B() : ptr( std::make_unique<A>() ) { }

    void foo() const { } // #3
    void foo() { } // #4

    std::unique_ptr<A> ptr ;
} ;

int main()
{
    B b ;
    b.foo() ; // #4, #2

    B const cb ;
    cb.foo() ; // #3, #2
}

つまり、メンバー関数のconst性は、ポインターやポインター風のデータメンバーに伝播しない。

これは、C++の文法上、当然のことなのだが、論理的にはひっかかるところもある。そのために、論文では、const性を伝播させるライブラリ、propagate_constを提案している。


struct B
{
// ...

    std::propagate_const< std::unique_ptr<A> > ptr ;
} ;

このようにpropagate_constライブラリを使うことで、メンバー関数のconst性を正しく伝播させることができる。

[PDFを使うのは賢くない] N4058: Atomic Smart Pointers

アトミックに操作できる、atomic<shared_ptr<T>>, atomic<weak_ptr<T>>, atomic<unique_ptr<T>>の提案。

これらのスマートポインターに対して、特別にアトミックな特殊化の実装が使われる。

C++では、もはや生のポインターを扱うのはバカのやることである。unique_ptrは生のポインターを扱うのと同等のパフォーマンスと安全性を提供してくれるし、unique_ptrが対応できないような状況にも、shared_ptrは対応できる。しかし、現在のC++で、依然として生のポインターを扱わなければならない分野が存在する。アトミック操作を使ってロックフリーな処理を実装する場合である。このため、atomicに既存のスマートポインター用の特殊化を追加して、アトミックに操作できるようにする提案。

すでに、shared_ptrにはアトミックに操作する関数があるが、クラス外の関数で非常に使いづらい。

なかなかいい提案ではないかと思う。

N4059: Spring 2015 C++ Standards Committee Meeting

2015年4月の4日か9日にかけてに開かれるC++国際会議の案内パンフレット。

Perceptive Softwareがホスト、場所はミズーリ州カンザスシティ。

ドワンゴ広告

先週、土日と有給と、ドワンゴ独自のリフレッシュ休暇を組み合わせて9連休を作り出して、京都に遊びに行った。

休暇中に、なんとなしに吉田寮に転がっていたHaskell入門書を読んでみると、C++にここ数年提案されている新機能は、かなりHaskellの影響を受けているようであった。そろそろ理論を現実に落としこむ時期に来ているのだろう。

ちなみに、あるいかにも研究者らしい生物博士と雑談したところ、次はCommon Lispの番だという。いずれはLispのマクロを導入しなければならず、当然我々はLispをやるべきなのだという。

Cプリプロセッサーのような単なるトークン列によらない、文法やスコープを尊重するマクロは、入るとするならば、おそらく静的リフレクション機能の一つとして入るだろう。コード情報をコンパイル時に扱うことができ、またコンパイル時にコード生成を行えるような機能だ。

完全に遊ぶつもりの休暇であったが、色々と面白いことがあった。その次第は近日中に書く。

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

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

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

2014-08-08

2014-05-pre-Rapperswil-mailingのレビュー: N4040-N4051

[PDFとはお先真っ暗] N4040: Working Draft, C++ Extensions for Concepts

軽量コンセプトのドラフト文面案

Concerns with changing existing types in Technical Specifications

Issaquah会議で、TSはstd名前空間のライブラリを変更してよいと決定された。つまり、std::experimentalなどの実験的な別の名前空間の中にそっくりライブラリをコピーして変更し、またTSの文面もすべてをコピーした上で変更ではなく、TS文面は差分だけで良くなったし、std名前空間を直接書き換えてよくなった。

これは、色々と懸念事項もある。

ある実装がTSの実験的実装をしたとして、コンパイラーオプションなどで有効にできるようにしていたとする。TS機能を有効にした場合、ライブラリがABI互換性を静かに壊してしまう可能性がある。

また、std名前空間は標準ライブラリの実装にしか使う権利がないので、TSに完全準拠したサードパーティ実装ができなくなる。

TSの機能は、実際に実装されて、現実の利用者による利用経験が積まれなければ、規格に正式に取り入れるかどうかの判断ができない。

論文では、この懸念に対する明確な結論を出していない。

[PDFからまともなフォーマットへの安全な変換が望まれる] N4042: Safe conversions in unique_ptr<T[]>

現在、提案されているunique_ptr<T[]>は、派生クラスへのポインターから基本クラスへのポインターの変換は、危険なので禁止している。

struct base { } ;
struct derived { } ;

int main()
{
    // ill-formed
    std::unique_ptr<base[]> p( new derived[10] ) ;
}

しかし、この制限は強すぎて、以下のような安全な型変換も禁止してしまう。


unique_ptr< Foo const [] > ptr1( new Foo[10] ) ; // ill-formed
unique_ptr< Foo [] > ptr2( new Foo[10] ) ;
unique_ptr< Foo const [] > ptr3 = move( ptr2 ) ; // ill-formed
unique_ptr< Foo const [] > ptr4 ;
ptr4.reset( new Foo[10] ) ; // ill-formed

これは明らかに安全であるので、これは認めようという提案。

このような細かい条件でill-formedにするかどうかを切り替える方法には、static_assertを使う方法と、SFINAEを使う方法がある。static_assertはエラーメッセージも読みやすくはなるのだが、ハードエラーになってしまうという問題がある。そのため、論文では、規格の他の場面でもよく使われている、SFINAEによる方法を提案している。

この制限緩和は然るべきであると思う。

N4043: Dynarray Allocation Context

dynarrayで問題になったように、明示的なデストラクターの呼び出しとplacement newは問題になる。この論文はその問題に対処するために、デストラクター呼び出しとplacement newで再構築されない自動ストレージに関しては、実装依存の最適化を許す文面を追加することで解決しようとしている。

そういうここからここまでは実装依存と定義するだけの簡単な問題ではないと思うのだが。

N4044: A Three-Class IP Address Proposal, Revision 1

IPアドレスを表現するライブラリの提案

前回の論文からの変更点で、特にある人物の興味を引きそうな変更:「多くの関数がnoeceptと指定された」

[またPDFだ] N4045: Library Foundations for Asynchronous Operations, Revision 2

コールバックとfutureの両モデルをサポートする設計の紹介論文。

[PDFで書かれた論文の価値は低い] N4046: Executors and Asynchronous Operations

タスク単位を自動的に並列実行してくれる並列実行ライブラリの論文。

[暗い未来を予感させるPDFフォーマット] N4047: A Module System for C++

#includeの代替機能、モジュールの提案。

C++にはライブラリをコンポーネント化する近代的な機能が欠けている。C++にあるのは、Cから受け継いた#includeである。#includeとは単なるテキストデータをその位置に挿入するものである。コンパイラーからしてみればコピペだ。

#includeはC++の長いビルド時間の原因にもなっている。#includeされるソースコードの意味は、すでに定義されているマクロによって変わるし、#includeの後に続くソースコードの意味も、#includeされるソースコードで定義されているマクロによって変わる。

C++には、もっと近代的なライブラリの分離のための機能が必要だ。その機能はモジュールと名付けられ、設計が進んでいる。

この論文の提案は、既存のプリプロセッサー自体には何も変更を加えない。過去に、プリプロセッサーに付け焼刃的な変更を加えて害悪を軽減しようという提案はいくつもあったが、どれも失敗に終わっている。ぷりプロセッサーには長い歴史があり、短期的に廃止できるものではない。したがって、新しい機能は、プリプロセッサーと共存しなければならない。

提案では、新しいキーワードとして、moduleとimportのふたつを追加する。

モジュールを使うソースコードは、モジュールをimportする。

// N4047提案
import std.vector ;

int main()
{
    std::vector<int> v ;
}

モジュールは、最初の宣言でモジュール名を宣言する

// N4047提案
module std.vector ;

export
{
// 外部に見せる名前の宣言
}

export { }で囲まない名前は、外部に公開されない。ローカルな定義になり、他の翻訳単位でその名前を使った定義があったとしても、ODR違反にはならない。

// 1.cpp
module one ;

export { void f() { } }

void g() { }
// 2.cpp
module two ;

export { void g() { } }

void f() { }
// main.cpp
import one ;
import two ;

int main()
{
    f() ; // oneのf
    g() ; // twoのg
}

この三つの翻訳単位からなるプログラムをコンパイルしてもODR違反とはならない。モジュールでは、export { ... } で囲んだ名前のみが外部に公開され、それ以外の名前は外部に公開されず、ODR違反にもならないようになるからだ。

モジュールは相互の参照もヘンテコな前方宣言なしに書くことができる。

// A.cpp
module A ;

export {

import B ;

class A
{
    B * ptr ;
} ;
}
// B.cpp
module B ;

export {
import A ;

class B
{
    A * ptr ;
} ;

}

早くモジュールが実用化されて欲しい。

[PDFも改良してほしい] N4048: More Improvements to std::future<T> - Revision 1

N3865の改訂版。optionalのより汎用なライブラリ、expectedに対応した変更があるようだ。

[無PDF原則も提案したいところ] N4049: 0-overhead-principle violations in exception handling

例外において、ゼロオーバーヘッドの原則が尊重されていない例の提示。

例外にはオーバーヘッドがある。標準ライブラリは例外に強く依存しているので、例外を使ったコードを吐く。また、生成されるコードも、例外に対応するためのバッファーなどを持っている。これらはたとえ例外を使わなかったとしてもメモリ使用量を増加させる。そして、既存のリンク時コード除去やLTOは、これらのコードやバッファーを取り除くほど賢くない。

GCCには例外を使わないオプション-fno-exceptionsがある。しかし、これはフロントエンドで例外を使わないという指定だけであって、ライブラリは依然として例外を使うし、バックエンドが吐くコードも、例外のコードを吐くし、例外をサポートするためのコードや、例外が緊急時に使うバッファーなども確保する。これらはメモリ使用料を増加させる。

これらのオーバーヘッドを取り除くにはツールチェインに手を入れて、本当に例外を使わないようにするしかない。

論文は、現時点でのGCCの実装を示して、問題提起をするだけで、何も結論を出していない。

N4050: Dynarray Semi-Editorial Issues

dynarrayに対する文面上の些細な修正。とはいっても、単なる文法ミスや誤字脱字よりはすこし真面目な問題を修正している。

N4051: Allow typename in a template template parameter

テンプレートテンプレートパラメーターにtypenameキーワードを利用できるようにする提案。

今までは、

// 現状
template < 
    template < typename T >
    class U // classキーワードが必要
    >
class C ;

このように、テンプレートテンプレートパラメーターは、文法上の制約でclassキーワードを使わなければならなかったが、この提案では、typenameキーワードも使えるようになる。

// N4051提案
template < 
    template < typename T >
    typename U // typenameキーワードが使える
    >
class C ;

些細な変更だが、プログラマーの無用の混乱を防ぐために、入って欲しい変更だ。

ちなみに、Clangが-std=c++1zでN4051提案の実験的実装をしている。

Clang - C++1z, C++14, C++11 and C++98 Status

これで、2014年5月分の論文集が終わった。次は7月分だ。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

来週は遠慮無く有給休暇を取ったので丸々休みだ。久しぶりに京都に帰省する。

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

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

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

2014-08-07

2014-05-pre-Rapperswil-mailingのレビュー: N4030-N4039

さて、5月分の論文集も残すところ後わずか。

N4030: Feature-testing recommendations for C++

C++の新機能が実装によってサポートされているかどうかを確かめる標準のCプリプロセッサーマクロの提案。具体的なマクロ名などは論文を参照。

// N4030提案
#if __cpp_binary_literals
int const packed_zero_to_three = 0b00011011;
#else
int const packed_zero_to_three = 0x1B;
#endif

__cpp_binary_literalsは、実装が二進数リテラルに対応しているかどうかを調べるマクロである。

筆者はCプリプロセッサーを使ういかなる機能にも反対の立場である。

N4031: make_array, revision 1

make_tupleやmake_pairのようなインターフェースで、std::arrayを返すmake_arrayの提案。

これにより、arrayのテンプレート実引数を推定でき、わざわざ手書きする必要がなくなる。

// 面倒
std::array< int, 5 > a = { 1, 2, 3, 4, 5 } 

// 簡単
auto a = std::make_array( 1, 2, 3, 4, 5 ) ;

// 型を明示することもできる
auto b = std::make_array<int>( 1 ) ; 

// array<char const *, 1>
auto c = std::make_array("hello") ;

// array<char, 6> d = { 'h', "e", 'l', 'l', 'o', '\n' } ;
auto d = std::to_array("hello") ;

narrowing conversionは禁止されている。

// エラー、narrwoing conversion
auto a = std::make_array(2, 3U)

なかなか悪くない小粒なライブラリだ。

N4032: Comments on continuations and executors

Anthony Williams本人が自ら、現在提案中の並列処理ライブラリを実装したところ、文面に様々な不備が見つかったとのことで、その不備を列挙して、然るべき修正案などを書いている論文。

executorがabstractクラスなのは不便だ。executorコンセプトを用意して、実行時ポリモーフィックな振る舞いには、別途generic_executor_refのようなtype erasureを使ったラッパーを容易すべきだ。

Scheduled Executorがタイムアウト指定にstd::chrono::system_clockを使うのはいかにもおかしい。std::chrono::steady_cloxkを使うべきだ。そもそも、タイムアウト指定にタイムスタンプを使うというのは、利用例の一部しかカバーできないので、やはり標準のabstract基本クラスと仮想関数を用意して様々な利用例に対応できるようにすべきである。

executorの種類が少なすぎる。executor *で返されるより、generic_executor_refで返すべきだ。その記述も不十分だ。

そのほか、futureに対する拡張案へのコメントが並んでいる。設計の不備、文面の不備を指摘するものが多い。

N4033: synchronized_value<T> for associating a mutex with a value

これもあの有名なAnthony Williamsの論文。T型を自動的にmutexでロックするライブラリ、synchronized_value<T>の提案

値に排他的なロックをかけるにはmutexを使うが、mutexをそのまま使うのは、甚だ面倒である。

int value1 ;
std::mutex m1 ; // value1用のmutex
int value2 ;
std::mutex m2 ; // value2用のmutex

void f()
{
    std::lock_guard<std::mutex> guard(m2) ; // おっと
    value1 = 123 ; // おおっと
} 

上記のコードは、ロックすべきmutexオブジェクトを間違えているのだが、残念ながら、コンパイラーはこれを警告してくれない。

この問題は、ある型のオブジェクトとそのオブジェクトを守るmutexを管理するライブラリがあればいいのだ。そこで、そのようなライブラリを論文著者はすでにDr. Dobb'sの記事として執筆していたが、その設計を手直しして正式に提案している。


std::synchronized_value value ;

void f()
{
    *valule = 123 ;
    int x = *value ;
}

仕組みとしては、synchronized_valueは、T型とmutexをメンバーとして持っている。operator *は、未規定の型を返す。この型は生成時に元のsynchronized_valueのオブジェクトのmutexをロックし、破棄されるときにmutexをアンロックする。また、T型へのリファレンスを返す。その結果、安全に自動的にロック、アンロックした上に、値の読み書きもできる。

これは、値の単発の読み書きをするにはいいが、複数の操作を一括して行うには効率が悪い。そのため、オブジェクトの生存期間の間だけロックし続ける、公に公開されているインターフェースもある。

std::syncronized_value<std::string> value ;

void f()
{
    std::update_guard< std::string > guard(value) ;

    *guard += "hello" ;
    *guard += "hello" ;

// guardのデストラクターでアンロックされる
}

N4034: Destructive Move

より制約が厳しい、保証された破壊的ムーブをするためのライブラリの提案。

現在のムーブというのは、ムーブ後のオブジェクトを、未規定だが有効な状態にするように義務付けている。つまり、ムーブ後のオブジェクトの状態は定められていないが、通常使えるように使えなければならないということだ。

しかし、クラスの実装によっては、これを無例外保証のまま行うのは難しい。そこで、破壊的ムーブの提案となる。

破壊的ムーブ(destructive move)とは、ムーブ後のオブジェクトは破棄された後であり、利用も、もう一度破棄することも出気ない状態になる。こ無例外ムーブが提供できないクラスでも、無例外破壊的ムーブは提供できるクラスは多い。また、ムーブされた後のオブジェクトはどうせ破棄する場合もある(vectorがその内部バッファーを増やす場合など)

また、破壊的ムーブというのは、単にバイト列のコピーで済ませられる場合もある。そのような条件を満たす型を、trivially destructive movable typeと名付ける。

具体的な提案としては、

template <class T>
void destructive_move(T* to, T* from) noexcept( /* see below */ );

という破壊的ムーブのための関数テンプレートの追加と、is_trivially_destructive_movable<T>という、trivially destructive movableな型かどうかを調べられるtraitsを追加する。destructive_moveは、is_trivially_destructive_movableがtrueの場合、バイト列コピーを行う。また、destructive_moveが特定の型に対してオーバーロードされていた場合は、ADLによって発見され、そちらが呼ばれるようになっている。

また、この配列版、destructive_move_arrayもある。

template <class T>
void destructive_move_array(T* to, T* from, size_t sz)
noexcept(is_nothrow_destructive_movable<T>::value);

リファレンス実装はhttp://halpernwightsoftware.com/WG21/destructive_move.tgzにある。これはBloomberg LPのBDEライブラリで5年以上使われていて、vector操作を劇的に高速化させたとのことである。

[論文フォーマットは暗黙にPDF以外のまともなフォーマットが使われるべき] N4035: Implicit Evaluation of "auto" Variables and Arguments

N3748の改訂版。

以下のようなコードを考える。

matrix A, B ;

auto C = A * B ;

上記のコードで、変数Cの型がなんであるかはわからない。matrix型かもしれないし、違うかもしれない。たとえば、matrixクラスの作者は、最適化のために、計算が実際に必要になるまで遅延させるため、内部的なラッパー型や、Expression Templateの技法を使った複雑な型を返しているかもしれない。

しかし、ここでmatrix型が欲しい場合、いったいどうすればいいのだろうか。

そのため、この論文では、autoを初期化するときに暗黙に型変換される型を、ライブラリ側で指定できる機能と、ユーザー側でその指定された暗黙の型変換を無効にする機能を追加しようと提案している。そのためには、できるだけわかりやすい文法を考えなければならない。

たとえば、matrixクラスは、operator *(Matrix const &, Matrix const &)の結果として、product_exprというラッパークラスを返しているとする。

論文では、いくつかの文法案が提案されている。

using宣言

class product_expr
{
public :
    using auto = matrix ;
} ;

このように記述すれば、auto specifierでは、暗黙にproduct_exprからmatrixに型変換される。型変換は通常通り、product_exprを引数として受け取るmatrixのコンストラクターとか、product_exprからmatrixへの変換関数として実装すればよい。

operator記法


class product_expr
{
public :
    matrix operator auto() { ... }
} ;

この文法の利点は、通常のコンストラクターや変換関数とは独立して、auto specifierのためだけの型変換処理が書けることである。ただし、そういう需要はあまりないのではないかとも思うし、戻り値の型を推定させた場合、何やらわかりにくい。

auto operator auto() { ... }

他にも、decayを特殊化してそれを特別に扱おうという提案もあるが、これは筆者は気に入らないので、わざわざここで紹介しない。

ユーザー側でこの余計なお世話を無効化する方法としては、explicitキーワードを使う文法が提案されている。

explicit auto C = A * B ;

David Vandevordeは、ライブラリで無効化することも可能であるという例を提示した。

auto C = noeval(A * B) ;

noevalの実装は以下の通り


template <typename T>
struct noeval type
{
    const T& ref;
    noeval type(const T& ref) : ref(ref) {}
    operator T const&() { return ref; }
    using auto= T const&;
} ;

template <typename T>
auto noeval(const T& ref)
{
    return noeval type(ref);
}

ライブラリの無効化のために、まさに提案されているこの機能をもう一度使うとはにくいコードだ。さすがはDavid Vandevorde。

[論文からPDFフォーマットの廃止に向けて] N4036: Towards Implementation and Use of memory_order_consume

2014年2月に、Linus Torvaldsが、GCCのML上で暴れていた。この論文著者とも殺伐とやりあっていた。

gcc archive, author index for February, 2014

Linusの主張はこうだ。

C11/C++11に入ったアトミック操作は使い物にならない。規格がクソすぎるためである。よってLinuxカーネルでは使わない。

規格化されたアトミック操作は保証が弱すぎる。したがって、Linuxカーネルのアトミック操作のすべてを置き換えることはできない。また、Speculative storeのような問題ある最適化を抑制することもできない。そもそもspeculative storeなんて許されるべきではないだろ。コンパイラー屋は、規格を指さして、「でもほら、規格上許されちゃってるんだよねーん」とかほざいているが、規格がぶっ壊れてる。ぶっ壊れたコード生成を正当化するためにクソぶっ壊れた規格を使っている。現実のハードウェアに基づかない規格などクソだ。

結局、問題に対処するために、Linuxカーネルではvolatileを使っている。アトミック操作ではすべての問題ある最適化を抑制できない。すると、volatileかつアトミック型を使わければならない。じゃあ、最初っからvolatileだけでいいじゃねーか。なんでわざわざアトミック型まで使わなきゃなんねーんだよ。

アトミック操作はまだひとつかふたつのプラットフォーム向けにしか実装されていないし、まだ実装経験が浅すぎてどうせバグだらけだ。結局、Linuxカーネルの今のやり方(volatile+インラインアセンブリ)は維持しなければならない。その上で、ひとつかふたつのプラットフォーム向けに標準規格のアトミック操作もつかってみよーかなーなんてなるわけねーだろドアホ。なんでそんなに複雑にしなきゃならねーんだよボケ。

そもそもmemory_order_consumeってクソすぎるだろ。なんだよこのcarries dependencyって概念はよ。コンパイラーが依存順序を完璧に解析できるわけねーだろ。で、明示的にkill_dependency()で依存を断ち切れって? ふざけんじゃねぇぞ。

そもそも、グローバルな最適化なんて糞食らえだ。ソースコードの解析だけですべてが分かると思うな。ソースコードに記述されていない、ハードウェアやモジュールなどの外部の別言語で書かれたライブラリがメモリを操作することだってあるだろうが。ローカルな最適化だけにしておけ。

標準規格はクソ使えないので我々Linuxカーネルではゼッテー使わねー。馬鹿げた机上の空論を捨てて、規格をさっさと直しやがれ。

論文著者は、規格化されたアトミック操作が現実の需要と乖離している理由を、コンパイラーの最適化手法が、7年前の規格化していた時と比べて、格段にアグレッシブになっていること、当時より長い依存チェインが現実に使われていることなどを上げている。

論文では、Linus Torvaldsでも満足する機能を提案しようと、型ベースの制限付き依存チェインとして、value_dep_preserving型指定子を提案している。

この論文を読むのはつかれた。アカデミックバリバリの2カラムのクソレイアウトを利用したクソみたいなtexで書かれたものをPDFで出力した論文で、しかも元のtexのソースコードは公開しないときているので、最高に読みづらかった。仕方がないので紙に印刷して読んだ。そして、背景事情を調べるためにLinusのGCC MLでの発言を追う必要があったので、これまた時間がかかった。

[非PDFドキュメントが欲しい] N4037: Non-Transactional Implementation of Atomic Tree Move

トランザクショナルメモリーを使わずに、lock contentioもできるだけおこさず、要素をあるツリーから別のツリーにムーブするアルゴリズムの考察。2014年のIssaquah会議でそういう課題が出たので考察したらしい。

論文では、バランスも何もないバイナリツリーのみを考察している。

論文自体は、あまりC++らしくない。ただ、C++会議でそのような条件を満たすアルゴリズムが現在存在しないので今後の課題として設定されたので、その道の専門家であるPaul McKenneyが考察したようだ。

N4038: Proposal for Unbounded-Precision Integer Types

C++の標準ライブラリに無限精度整数型の提案。

プログラミングにおいて、C++の基本型の整数型が表現しきれないほど大きな数値を扱う需要はよくある。特に、暗号用途には必須だ。C++用のそのようなライブラリは、多数ある。Javaはそのようなライブラリを標準で持っている。C++にも無限精度整数型が必要だ。

無限精度整数型(Unbounded-Precision Integer Type)とは言うものの、現実的には、有限のコンピューターリソース上に実装される以上、当然、有限の精度を持つ。ここでいう無限精度とは、ハードコードされた上限がなく、システムのリソース以外に桁数に制限を加えるものがないことを言う。

提案では、無限精度整数型であるstd::integer型と、無限精度ビット列型であるstd::bitsが提案されている。

std::integer型は、既存の組み込みの整数型と組み合わせて使うことができ、また組み込みの整数型がサポートしている演算はすべて同じ文法でサポートする。また、その他にも無限精度整数型を必要とする分野で需要ある演算をサポートする。需要あるすべての演算を網羅することはできないので、必要な演算を実装できる基本的な演算を提供する。abs, sqr, sqrt, pow, mod, mulmod powmod, gcd, lcmあたりだ。is_zero, is_oddがあるのも興味深い

また、integer型のメンバー関数のsizeとcapacityは、内部ストレージを再確保せずに表現できる10進桁数の上限を返す。reserveも桁数を取る。

現在表現している値を表現できる規模に内部ストレージを縮小するshrink_to_fitもある。

get_data_proxyというメンバー関数もあり、これは内部ストレージを直接見たり操作したりできる。

ちなみに、ドワンゴ社内では、競技プログラミングに使えるとの意見が多かった。

N4039: Default executor

現在提案中の並列実行ライブラリには、デフォルトのexecutorがない。デフォルトのexecutorはあるべきである。しかし、何をデフォルトにすればいいのか議論は付きない。タスクごとにスレッドを作るexecutorはわかりやすいが、一般的に、スレッドプールよりコストがかかる、しかし、スレッドプールでは、タスクがブロックされてしまうことがある。

論文は結論を出さず、それぞれのexecutorの利点、欠点を挙げるだけにとどまっている。

ドワンゴ広告

最近、ドワンゴのオフィスが入っている歌舞伎座タワーの7FのコンビニがICE BOXを置くようになったので、食べ過ぎないように鋼鉄の意思を要求されてつらい。

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

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

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

妖怪ハウスのイベント予定

今月の妖怪ハウスで筆者が予定しているイベント一覧

8月7日夜、ドライフルーツヨーグルト試食会、焼き肉

8月8日夜、Monty Python鑑賞会、多分焼き肉

8月23日、ボードゲーム会、スイーツ(笑)会

2014-08-05

何でWindowsは俺のワイヤレスキーボードをトースターだと認識するんだ?

device manager - Why does Windows think that my wireless keyboard is a toaster? - Super User

俺は彼女の父親から古いPCを相続したんだが、プリンターを設定していると、ちょっとおどろきのものが現れた。

画像

さて、二つの疑問がある。

  1. なんでWindowsは俺のワイヤレスキーボードをトースターだと認識するんだ?
  2. なんでWindowsはデバイスメニューにトースターのアイコンを持っているんだ?

なぜか、質問者のワイヤレスキーボードに、トースターのアイコンが表示されている。Windowsはなぜそんなアイコンを用意しているのだろうか。

答えは。

理由1

Microsoftはトースター用ドライバーというサンプルを作っている。このサンプルでは、<DeviceIconFile>Toaster.ico</DeviceIconFile>という行があり、おそらく、お前のキーボードの製造業者は、このサンプルをそのまま使ったのだろう。

理由2

キーボードの裏に、食パンを差し込む口は見当たらないか?

どうやら、Microsoftがサンプル用に作ったトースター用のドライバーのデバイス情報のXMLファイルをそのまま使ったずぼらなキーボードメーカーがいるらしい。サンプルのアイコンがトースターなのは、Microsoftのユーモア精神なのか、そのままコピペして使うずぼらなハードウェア製造業者を防ぐためなのか。それでもコピペはされる。

それにしても、USBトースターはあまり実用になりそうにない。一般的なトースターの消費電力は1000Wはある。一方、USBは3.0でもせいぜい100W程度の電力しか供給できない。USB3.0ポートを制御用に1ポート、電力供給用に9ポート必要とするトースターだろうか。電源ユニットも相当によい物を使う必要がありそうだ。

2014-08-04

ループカウンタを64bitにしたり、 バッファのサイズを定数にしたらパフォーマンス激落ちなんだけど何で?

c++ - Replacing 32bit loop count variable with 64bit introduces crazy performance deviations - Stack Overflow

stackoverflowで、興味深い質問が行われている。

簡単にまとめるとこうだ。std::uint64_t型の配列の各要素にx86-64のpopcnt(1になっているビット数を数える命令)を適用したい。

コードの肝心の部分を書くと、以下のようになる。

for (unsigned i=0;i<size/8;i+=4) {
    count+=_mm_popcnt_u64(buffer[i]);
    count+=_mm_popcnt_u64(buffer[i+1]);
    count+=_mm_popcnt_u64(buffer[i+2]);
    count+=_mm_popcnt_u64(buffer[i+3]);
}

ループの中で、バッファーの要素を4個づつ、Compiler Intrinsicに渡して、popcntを実行している。

さて、ここで奇妙な事実がある。Sandy/Ivy BridgeやHaswellで、ループカウンター用の変数であるiの型をunsigned int型とstd::uint64_t型とでベンチマークを取ってみると、質問者の環境で、unsigned int型の処理速度は26GB/sだが、std::uint64_t型では、13GB/sと、速度が激減するのだ。

これはGCCのバグなのだろうか。しかし、Clangで実験してみても、やはりunsigned intが26GB/sに対し、uint64_t型は15GB/sと、明らかに処理速度が違う。

更に良くわからないことがある。バッファーのサイズを保持するsize変数であるが、バッファーサイズは実行時の入力により決定されるため、定数ではない。しかし、バッファーサイズを決め打ちにしてsizeを定数にしてみると、なんと性能はunsignt intもuint64_tも共に20GB/sになる。Clangでは、どちらも15GB/sになる。

size変数をstaticにしてみると、uint64_t型でも、20GB/sにまで速度が改善する。

いったい何なのだ。

最も投票数の多い回答が興味深い。

この不思議なパフォーマンスの変化の理由は、IntelのSnady/Ivy Bridge, Haswellのpopcnt命令の、不必要なデータ依存によるものだという。

popcnt src, dest

とあったとき、なぜかIntelのCPUは、結果を出力する先のレジスターにデータ依存が発生するのだという。そのため、destに指定されたレジスターの値が定まるまで、後続のpopcntを並列実行できず、スループットが落ちるのだという。しかし、destはどうせ上書きしてしまい、直前の値は何の意味も持たないので、データ依存もクソもないはずなのだが。

現時点で、GCCはこのIntelのCPUの奇妙な挙動をまだ把握していない。コンパイラーはCPUごとのこのような特性を把握して、事前にxor reg, regなどしてデータ依存をそぎ落としておくべきなのだという。

AMDのCPUにはこのような不必要なデータ依存はないそうだ。

なぜループカウンター用の変数の型を64bitにしたり、バッファーサイズを保持する変数を定数にしたりするだけで結果が劇的に変わるかというと、コンパイラーのレジスタ割り当てが変わるためだという。

一体、何故こんな不必要なデータ依存が発生するのかという疑問であるが、回答者は、IntelのCPUはオペランドを二つ取る似通った命令を共通の方法で処理していて、popcntと何らかの命令を同じ枠組みで処理しているためではないかとしている。このようにすることで、プロセッサーの設計を簡単にできるのだとか。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

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

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

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

2014-08-03

momonga.vim #6 in ドワンゴ

2014年8月2日の土曜日、momonga.vim #6が、ドワンゴのセミナールームで開催された。

momonga.vim #6 in ドワンゴ(あきらかに) - connpass

momonga.vimとは、もくもく会だ。もくもく会とは、一人では集中して作業できない人間が、皆で集まるという強力な理由付けの元に、何らかの作業をするという会だ。もくもくはいったいどこから始まったのかという疑問であるが、おそらくphaさんが始めたのではないかと思う。

今回、ドワンゴのセミナールームでVimのもくもく会が開催された理由というのは、momonga.vimの主催、@supermomongaさんが、勉強会に使える会場を探していたからだ。何でも、最初は自宅でやっていたらしいが、指数関数的に参加に人数が増えていき、このままでは年末には武道館が必要になる増加率であるという。ドワンゴのセミナールームは煩悩の数と同じ108人入り、マイク、プロジェクター、WiFi、椅子と机、ホワイトボードなどの、勉強会に必要な設備はひと通り揃っているので、勉強会に最適である。

さて、人数自体は108人入るのだが、あまりに大勢を入れ過ぎると、もくもく会の趣旨に反するとのことで、募集人数は40人に設定された。

さて、当日の筆者は、場所を提供するスタッフとして12時半頃に会場に行った。

懸念事項としては、当日にももんがさんが来るかどうかという究極の問題がある。ももんがさんというのは、極めて億劫な人であり、家から出るのが億劫で来ないということは十分に考えられるからだ。とはいっても、さすがに今回は自分で主催した勉強会であるので、十分に家から出てくる理由がある。

幸い、そういうことはなく、本人は早めに会場に到着した。しかし、8月1日の21時に起床したとのことで、若干の違和感は残った。

さて、今回の勉強会には、あの暗黒美夢王(Dark Vim Master)が来た。暗黒美夢王には、開始と締めくくりに、かの有名なエディ歌を披露していただいた。エディ歌とは、編集王(エディットキング) バトルエディターズに使われている歌である。

さて、もくもく会は、もくもく会であるから、特に特筆すべきこともなく各人が黙々と作業していた。

さて、最後に進捗のある人の発表が行われた。なにしろ、Vimに関連する作業である。Vimとはテキストエディターである。テキストエディターである以上、人目を引く見た目にわかりやすい何か動きのある発表などあろうはずもない。

ところが、なんとVimうさぎさん(C++力ないよー) (rbtnn)が、Vim上でマリオのようなものを実装しかけていた。

さて、もくもく会のあとは、妖怪ハウスで懇親会を行った。懇親会では、もう何度目かわからないピザを焼いた。ピザはだいぶ慣れてしまった。今後は別の具に挑戦したいところだ。

さて、日曜日はのんびりするとしよう。

ドワンゴ広告

この記事は日曜日ののんびりした気分で書かれた。

ところで、ドワンゴで筆者が企画して開く勉強会は、私は出勤扱いになる。そのためこの勉強会で筆者、なんと社畜らしく休日出勤してしまった。休日出勤した以上、代休を取らねばならない。

ところで、ドワンゴ社内のテキストディター利用率の統計が取られたことはないが、おそらくEmacsよりVim勢のほうが多数派ではないかと思われる。ドワンゴでは社員が各自好きなテキストエディターを使っている。なぜならば、テキストエディターはソースコードの読み書きをする極めて重要なツールであるので、プログラマーの好みが激しい分野である。テキストエディターの選択はプログラマーに委ねられていなければならないのだ。

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

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

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