2016-01-31

闘会議2016のアナログゲームのとある運営スタッフの感想

闘会議2016のアナログゲームのインスト要員として2日間運営スタッフとして入っていたので、その感想を書く。

筆者は江添亮、ドワンゴにエンジニアとして雇われている。ドワンゴのボードゲーム同好会のメンバーでもある。今年の闘会議でも、アナログゲームは設置される。もちろんカタンもある。カタンのインストをするために運営スタッフとして参加した。

今回の闘会議には不安が大きかった。なにしろ、今回のアナログゲームエリアには、30卓ほど立てるという予定である。1卓にスタッフを2人配置するとして、60人必要になる計算だ。60人ものボドゲがインストできるスタッフをどこから集めてくるというのか。あまりにも無謀すぎる。私が一切関わっていない会社の企画するイベントが失敗するのは私の知ったことではないが、カタンのインストが失敗し、カタンに悪い印象を与える事態だけは、ガチのカタンプレイヤーとして見過ごすことはできぬ。そこで、カタンのインストのために運営スタッフとして参加した。

今回の闘会議では、ドワンゴのエンジニアには動員がかからなかったので、社内からはボドゲをインストできるスタッフがせいぜい5,6人程度しか出なかった。60人ものインスト要員をどうやってまかなうつもりなのだろうか。

私「そもそも必要なだけの頭数はいるのか?」
企画「5,60人ほどいます」
私「そのうちボドゲ経験があってインストができる人は?」
企画「3分の1ぐらい・・・コネで集めました」
私「残りは?」
企画「派遣バイトです」

嫌な予感しかしない。

ボードゲームの上手なインストは、ある程度のボードゲーム経験とインスト経験を必要とする。経験が全てではないが、最低限のボードゲーム慣れ、場慣れする必要がある。派遣バイトの中にボードゲーム経験のある人間がどのくらいいるだろうか。第一、どうやって募集しているのだ。「闘会議、アナログゲームエリアでボードゲームの解説と、場合によってはプレイヤーとしても参加して場を盛り上げてもらうお仕事です!」といった具体的な告知があれば、まだボードゲーム経験のある人間や、説明慣れしている人間が集まりやすそうではある。しかし、もし単なる「闘会議イベントの運営スタッフです!」程度の告知であったならば、イベントの運営スタッフ慣れしている人は応募するだろうが、ボードゲーム経験は望めそうにもない。

「で、バイトは前日のリハーサルに来るの?」
企画「全員来ます」
「すると前日のリハーサルで我々がバイトにインストして、翌日にインストしてもらうのか」
企画「そうです」

大丈夫なのだろうか。初対面の人間に複雑なルールの説明をする作業というのは、人によっては不得意な作業である。思うに、必要なのは少しの経験による場慣れだと思うのだが、今回は、その少しの経験を詰むほどの時間もない。

そして極めて絶望的なことに、この会話が行われたのは、闘会議までもう数えるほどしか日数がない時点である。根本的にスケジュールがおかしい。

そして前日リハーサル。私は今年木場に引っ越したので、闘会議の会場である幕張メッセのある海浜幕張までは、だいぶ近くなった。東西線で西船橋まで行き、JRに乗り換えて、武蔵野線で南船橋に行き、京葉線で海浜幕張に行く。タイミングが良ければ、西船橋から直通運転で一本で海浜幕張まで行けることもある。

私は予定通り西船橋で降りて乗り換えた。そして南船橋まで着いたが、何故か電車がホームに止まったまま、なかなか発車しない。しばらく待った結果、とうとう発射したが、なんともと来た線路を戻っていくではないか。しまった。これは南船橋が終点で西船橋に折り返しているのだ。こうして、予定より遅れて会場に到着した。

さて、肝心の派遣バイトだが、やはりボドゲ経験のある人は皆無であった。ボードゲームによっては、日本語の説明書が付属していないものまである。ボドゲのインストを始めるが、やはり完全にボドゲ未経験では、マニュアル読み上げのような説明になってしまいわかりづらい。

カタンは、私の他にもう一人、カタンを5,6回ぐらいプレイしたことがある人間がインストをするようだ。カタンは最低100回ぐらいはやらないと感覚がつかめない。

さて闘会議1日目の朝、私は正しい電車の乗り換えに成功して会場に着いた。小雨が振っており極めて寒い。異様なほどに寒い。ここまで寒いと客足が遠のくのではないかと思われるぐらい寒い。幕張メッセの中は、強力な暖房に温かいのだが、外は死ぬほど寒い。

開場して人がなだれ込んでくる・・・かと思いきや、それほどの人数ではない。やはり天候が客足に影響を与えているのだろうか。それとも、今回は借りているホールが多いので、相対的に人が少なくみえるのだろうか。

去年に引き続き、麻雀は人気だ。すぐに卓が成立する。ボドゲの成立には少し待たねばならない。程なくしてボドゲ卓も埋まった。

カタンのルールをすべて口頭で説明すると30分ほどかかる。それに、口頭で説明されたことをすべて覚えられるわけがない。そこで、基本的なルールだけ教えて、後はプレイしながら教えていく方法を取った。これならば最初の何もしない状態での説明は5分か10分ほどですむ。

また、今回のカタンのインストを通じて、カタンでは初期配置が非常に重要だという認識を新たにした。ダイスの確率の説明は最初に行っているのだが、やはりカタンは数百回ほどやらないと感覚がつかめないのか、初心者は確率の極めて悪い場所に初期配置しようとする。去年の闘会議では、極端に確率が悪い場所に設置したものだけ助言をしていた。その結果、プレイヤーによって初期配置に極端な差が生まれ、トップを妨害する暇もなく30分ほどでゲームが終了してしまうことがたまに見られた。

今回は、何百回もカタンを対戦している筆者の感覚で、確率、資源バランス、港、目かぶり、他プレイヤーの影響まで含めた初期配置を強力に助言した。その結果、なんと初心者が4人集まった卓であっても、中盤から終盤にかけてのトップ阻止のどんでん返しが何度も起き、ガチ勢が経験するものと同じ極めて熱い戦いになった。

なるほど、カタンは初期配置が極めて重要なのだ。一人でも初期配置が悪いプレイヤーがいると、あるプレイヤーが極端に有利になりすぎてバランスが崩れ、張り合いのないプレイに成り下がってしまう。1番手、2番手に本来残るはずのない良い2件目の建設場所を与えてしまったり、3番手4番手に本来残るはずのない1件目の建設場所を与えてしまったりする。

ちなみに、一回だけ、カタンを所有していて家でよく遊んでいると主張する母と息子の親子2人で参加しておきながら、筆者の助言を聞かず、「置きたいところに置けばいいじゃない」とつぶやきながら、親子揃って悪い配置をした親子がいたが、親子揃って終盤まで全く伸びずに勝ち目がなかった。プレイ中の建設戦略も極めて悪く、序盤から意味もなくThe Longest Roadだけを狙って無駄な建設したりしていた。The Longest RoadとThe Largest Armyは補助的な点数であって、序盤から無理に狙いに行くものではないのだが、この辺の感覚は、強いプレイヤーと数百回の対戦をしないとわからない。カタンを所有していてよく遊んでいるとはいえ、適切な上達者の指導がなく、弱い初心者同士が狭い世界で戦っていると、こういう袋小路に陥る。大抵のゲームにありがちの問題だ。

さて、小学生ぐらいの子供が卓に着いた。子供にカタンのルールを教えるのは難しい。カタンはそれほど難しいゲームではないが、6面ダイスを2つ振った出目の合計値の確率から、戦略や得点方法まで、様々な馴染のない要素を理解させなければならない。将棋やチェスのような有名なボードゲームならば、戦略書がすでに山ほど出ていて、ある程度まで強くなるには、先人が考えぬいた結果の定石の暗記とパターンマッチだけですむのだが、カタンなどのボードゲームの場合、ルールをその場で聞いて理解し、自分で与えられたルールの範囲内で柔軟に戦略を考えなければならない。ただし、筆者の経験では、闘会議のアナログゲームの上級エリアにわざわざ来るぐらいの子供は、そういう処理も得意であるようだ。この子はどうだろう。

筆者「カタンはやったことあるかな?」
子供「はい、去年の闘会議ではじめてやりました」
筆者「何、ひょっとして私がインストした?」
子供「はい、そうです」

なんと、去年私がカタンを教えた子供が今年もやってきてカタンをするというのか。

うおおおおおおおお!!!!! 自分は今猛烈に感動している。

しかも、去年より遥かにうまくなっているし、結果として勝利したではないか。圧倒的にいい話だ。

1日目の最後は、人が集まらなかったので筆者もプレイヤーとして参加した卓で、閉場までの時間が残り僅かしかない状態で猛烈なトップへの妨害と勝利争いになり、最終的に筆者が勝利した。このゲームは極めて劣悪な盤面で、鉄が固まっているが、8鉄以外は確率が悪く、麦が不作であった。筆者は8土と土港を序盤で確保して、勝利を確信したのだが、当然残りの3人の注意を惹きつけてしまい、序盤の盗賊の標的にされた上に、中盤では8が全く振られることがなかった。終盤でトップの勝ちを阻止するためにThe Longest Roadの激しい奪い合いになり、また終盤の最後で、これまでの不足分を取り返すかのように8が出始めて巻き返し、中盤は盗賊と8が振られないことにより完全に死んでいたことから妨害の注意がそれていたために勝利した。

こうして闘会議の1日目が終わり、寒さに震えながら帰路についた。帰り道が同じであった自転車好きの同僚と自転車について話をしていて、自宅から職場まで5kmなので、十分に自転車通勤が可能な距離であるが、自転車を買う決心がつかないため、いまだに自転車通勤できていないことを話すと、「ぜひ自転車通勤するべきだ。まずは安い20万ぐらいの自転車を買って始めるといい」と言われた。自転車に20万も出すとは。しかもそれが安いだと。住んでいる世界が違う。ゲームPCの値段であれば安いのだが。

帰路、海浜幕張から南船橋で乗り換えようとして、別のホームに向かってしまい、発射直前の列車にろくに確認もせず飛び乗った結果、海浜幕張に逆戻りしてしまった。また、帰りの電車で傘をなくしてしまった。

闘会議2日目

朝に木場から海浜幕張に向かう。西船橋で乗り換えをして乗った電車は、何か違和感がある。行き先を見ると市川塩浜駅となっている。これは逆方向だ。南無三、また間違えたか。市川塩浜駅で降りて、逆方向の列車に乗ることで、海浜幕張まで一本で到着した。

2日目も1日目と同様にカタンのインストをした。帰りの電車は間違えなかった。

去年の感想。

本の虫: 闘会議2015のアナログゲームエリア、とある運営スタッフの感想

ドワンゴ広告

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

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

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

2016-01-24

グラビティリサーチ銀座に行ってきた

銀座に最近できた新しいクライミングジムのグラビティリサーチ銀座に行ってきた。

GRAVITY RESEARCH 銀座 | クライミング(ボルダリング・スポーツクライミング)ジム グラビティ リサーチ GRAVITY RESEARCH

このクライミングジムの最大の特徴は、銀座にあるということだ。職場が銀座の場合、仕事上がりに登るのに最適だ。

入会金が1500円で、21時以降の料金が1600円。他のジムと比較すると深夜料金が高い気がするが、これは税込み料金であることと、銀座にあるということと、職場が銀座で平日の仕事上がりに他のジムに行く場合、交通費が別にかかるということを考えると、こんなものかもしれない。

ジムの中はそれほど広くはないが、壁の横幅と課題の数はそれなりにあった。ただし天井が低い。

課題はグレードに対して簡単であるように感じた。

職場から歩いていける距離にあるという店で、グラビティリサーチ銀座は価値のあるジムだ。もし午前中からやっているのであれば、朝ボルダリングが可能だったのだが、残念ながら、14:30から開店する。

定期会員になって、平日毎日30分ぐらいの短時間利用するのは悪くなさそうだが、残念ながら3ヶ月単位でしか定期購入できない。一ヶ月ならばその月は集中して登るのもありなのだが、さてどうしよう。

ところで、このジムにはドワンゴのボルダリング同好会として同僚3人で行ったのだが、宛名「ドワンゴ ボルダリング同好会」で領収書を書いてもらったところ、「ボルダリング同好会って何をするんですか?」と聞かれた。はて、ボルダリング同好会を名乗る団体がクライミングジムに来ている。これ以上に自明なことがあるだろうか。

第2回 ドワンゴからの挑戦状の予選に参加した

第二回、ドワンゴからの挑戦状の予選に参加した。公式Webサイトは以下の通り。

第2回 ドワンゴからの挑戦状 | 株式会社ドワンゴ

もう予選は終わってしまったが、問題は今からでも挑戦することができる。問題に挑戦するには以下のWebサイト上から行う。

Welcome to 第2回 ドワンゴからの挑戦状 予選 - 第2回 ドワンゴからの挑戦状 予選 | AtCoder

これは高橋直大さんの会社、AtCoder社の運営する競技プログラミングのWebサイトで、今回の競技プログラミングのジャッジシステムと問題作成のためにAtCoder社に協力してもらっている。

問題は、A, B, C, D, Eの5問あり、それぞれに点数が設定されている。CDE問題には、部分点も設定されてる。

回答をするには、問題とプログラミング言語を選び、ソースコードを貼り付けて提出すると、atcoderのサーバー側でコンパイルとテストケースに対する実行が行われて、結果がみられる。問題を解くためには、まずソースコードのコンパイルやパースに失敗しないこと、正しい回答を出力すること、実行時間、メモリ使用量が制限以内であることが求められる。

この予選の上位入賞者は、2月13日にドワンゴ本社で行われる本選に参加できる。本選での上位入賞者には賞金が出るほか、2017年度新卒である場合、ドワンゴへの新卒採用における一部の面接をパスできる。

去年行われた際、筆者は予選終了後に問題を解いたが、今回はリアルタイムで予選に参加した。筆者は競技プログラミングは得意ではないが、難易度が去年と同じ程度である場合、仮にもドワンゴでエンジニアという役職で雇用されている以上、B問題ぐらいまでは解けなければ沽券に関わる。

A: ニコニコ数

問題分だけ抜粋すると、以下の通り。

ニコニコ数とは、10進法で表記したときに 2 と 5 が交互にあらわれ、かつ一番上の位が 2 で一番下の位が 5 であるものです。 例えば、 25,2525,252525252525252525 などはニコニコ数であり、 467,5252,5 などはニコニコ数ではありません。

ニワンゴくんは、 N 以下の正の整数のうち、約数にニコニコ数を持つものがいくつあるかを調べようと思いました。ニワンゴくんに代わって、この問題を解くプログラムを作ってください。

筆者はこの問題を以下のように読み間違えてしまった。

ニワンゴくんは、 N 以下の正の整数それぞれについて、すべての約数のうちニコニコ数である数字の合計値がいくつあるかを調べようと思いました。

つまり、筆者の誤った解釈では、2525はニコニコ数となる約数として25と2525を持つので、出力すべき数字の合計値に2を足すべきだとなる。そのため、2525を超える値について、出力が間違ってしまった。そして、解釈間違いに気がつくまでに、実に不毛な考察が行われた。結果が32bit符号付き整数に収まらないのではないか。いや、Nは10の9乗以下であるので問題はない。どこかにコンパイルエラーにならないタイプミスがあるのか。などなど。

さて、正しい解釈でこの問題を考えると、ニコニコ数2525は約数として25を含むし、252525も約数として25を含む。したがって、約数として25が含まれるかどうかのみを考えればよい。約数の25が含まれる値は25の倍数であるし、単純にNを25で割ればよい。

#include <iostream>
 
int main()
{
    unsigned N{} ;
    std::cin >> N ;
    std::cout << N /25 << std::endl ;
}

B: 積み鉛筆

問題分はリンク先を参照してもらうとして、A問題に時間をほとんど使い尽くしてしまったので、B問題を解く時間が30分ぐらいしか残されてない。

さて、これは一体どうすればいいのだろうか。整合性を保つために手直しをすると、前の鉛筆まで手直しが発生するのではないか。するとバックトラック的な何かをする必要があるのだろうか。

ぼんやりと鉛筆を詰んでいる図の例をながめていると、ふとひらめいた。

2本の上段の鉛筆KiとKi+1に対して、下段の整合性を保つ必要のある鉛筆は、LiとLi+1とLi+2の3本だけだ。Li+2の長さをKi+1とKi+2の都合で変えても、Liを整合性を保つために変更する必要はない。そして、整合性を保つ方法というのも、KiとKi+1の長さを、Li+1とLi+2に入れて、それで整合性が取れなければ、LiとLi+1に入れるだけでいいのではないか。2つの連続した鉛筆Kに対しては、その下の3本の鉛筆Lしか考慮しなくてよい。

さっそくそのようなコードを書き、投稿。なぜかWA(Wrong Answer)とTLE(Time Limit Exceeded)の嵐。何がまずいのか。入力をすべてメモリ上に読み込んでいるからまずいのか。そんなことをせずに逐次に処理をしていくべきなのか。いや、入力は10の5乗程度でしかなく、TLEを起こすはずがない。WAはなぜだ。少なくとも問題の入力例に対しては正しい答えを出せているのだが。

そしてここで時間切れとなった。時間切れ後に気がついたのだが、どうやらB問題の回答をA問題に対して提出していたようだ。B問題に対してそのまま提出すると、普通に通った。なんということだ。

実質B問題まではできたと言えるので、最低限の沽券を守ることはできた。

C: メンテナンス明け

残念ながら、私はC問題以降を解説するだけのアルゴリズム力を持たない。しかし、C問題についてはいろいろと面白い裏話を聞いている。

この問題は、ドワンゴ社内でtayamaというハンドルネームを用いているドワンゴ社員によって作成された。

問題を作成してAtCoderに提出したところ、AtCoderの高橋直大社長から、「何のひねりもない問題」と言われたそうだ。tayamaさんはこれを、「アルハラ(=アルゴリズム・ハラスメント)である」とコメントしている。

また、この問題は、本来D問題にする予定で作ったのだが、直大社長に、「簡単じゃない?」と言われたためC問題になったのだという。

また、この問題には入力の大小に応じて、SmallとLargeというテストケースが用意されており、それぞれに点数が設定されている。テストケースが弱く、Smallには通らないのにLargeに通るコードが書けてしまうそうだ。

また、テストケースに、"small/91_tayama_killer_00"という名前のテストがあるが、これは問題作成者であるtayamaさんの当初書いた回答コードに通らない例が発見されたために追加されたテストケースだそうだ。

さて、本選は2月13日にドワンゴ本社で行われる。

ドワンゴ広告

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

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

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

2016-01-20

C++標準化委員会の文書のレビュー: P0160R0-P0172R0

P0160R0: Wording for removing defaults for unary folds

fold式からデフォルト値を削除する文面案。

P0162R0: A response to "P0055R0: On Interactions Between Coroutines and Networking Library"

「現在提案中のBoost.Asioベースのネットワークライブラリの状態保持のための動的ストレージは、コルーチンのスタック上に確保すれば高速になるので、規格でそのように規程すべきだ」という提案に対し、「そのような設計は実装の自由度を制限してしまい好ましくない。同等の高速化はBoost.Asioのカスタムアロケーターでも可能で、最近のAsioはデフォルトのアロケーターを使った場合でも最適化を自動的にするようになっているので、規格でもそのように規定しよう」という反論。

P0163R0: shared_ptr::weak_type

shared_ptr<T>に対応するweak_ptr<T>のネストされた型名、weak_typeを追加する提案

shared_ptrからweak_ptrを作るには、weak_ptrの型を具体的に記述する必要があった。これではジェネリックなコードを書きにくいので、N4537ではshared_ptrに対応するweak_ptrを返すunlockというメンバー関数を追加する提案をしたが、これは却下された。

しかし、やはりweak_ptrの型を直接書くのは嫌なので、今度はshared_ptrに対応するweak_ptr型のweak_typeというネストされた型名を追加する。

つまり、以下のようなコードを解決する。

template < typename T >
void f( T & t )
{
    auto sptr = t.get_shared_ptr() ; // 何らかのshared_ptrが返される
    std::weak_ptr<???> wptr = sptr ; // 型がわからない。
}

この問題を解決するために、従来、以下のようなコードが書かれてきた。

template < typename T >
std::weak_ptr<T> unlock( std::shared_ptr<T> const & sptr )
{
    return std::weak_ptr<T>( stpr ) ;
}

template < typename T >
void f( T & t )
{
    auto sptr = t.get_shared_ptr() ; // 何らかのshared_ptrが返される
    auto wptr = unlock(sptr) ;
}

この提案を使えば、以下のように書ける。

template < typename T >
void f( T & t )
{
    auto sptr = t.get_shared_ptr() ; // 何らかのshared_ptrが返される
    auto wptr = typename decltype(sptr)::weak_type(sptr) ;
}

unlockの方がわかりやすい気がするのだが。

P0164R0: Core Motions

Core issuesに対する解決の中で規格入りする準備ができたもの一覧

P0165C++ Standard Library Issues to be moved in Kona

Library issuesに対する解決の中で規格入りする準備ができたもの一覧

P0166R0: Three interesting questions about contracts

関数にprecondition, postcondition, invariantを記述できるcontract機能を使って、どのようにvectorのようなクラスで範囲外チェックを記述できるのかというHerb Sutterの提示した問いに答える文書。

P0167R0: Core "ready" Issues

2015 Kona会議以降に規格入りする準備が整ったcore issueの解決の一覧。

P0169R0: regex with Unicode character types

regexをUnicode(char16_t, char32_t)に対応させるために必要な設計の考察。

char32_tはUnicodeコードポイントをほぼそのまま表現できる。basic_regex<char32_t>を実現するには、<locale>をchar32_tに対応させる必要がある。具体的には、ctype<char32_t>, collate<char32_t>, collate_byname<char32_t>を実装する必要がある。

char16_tの方は、char32_tのように実装さえすれば動くわけではない。UTF-16にはサロゲートペアが存在するので、char16_tの一つのオブジェクトは1文字を表現しない場合がある。しかし.(dot atom)とか\S(predefined character class)はサロゲートペアの片割れにマッチしてしまう。サロゲートペアがあるため、char16_tは<locale>をサポートできない。

UTF-16ではなく、UCS-2(サロゲートペアのない16bit固定長Unicode符号。BMPだけをサポートしたもの)をサポートするという案は採用できない。なぜならば、UCS-2はすでにISO/IEC 10646においてdeprecated扱いされているため、今から発行する規格がUCS-2だけをサポートするというのはありえない。

ではどうするのか。basic_regex<char16_t>は提供せず、char16_tとchar32_tを相互変換するイテレーターを提供するという案がある。

他には、char16_tは、現行のbasic_regex<char>と同じく、可変長エンコードをそのまま突っ込んだものとみなし、特に何も対応せずそのまま提供するという案もある。この案を採用する場合、Unicodeに関してはchar32_tに変換した上でbasic_regex<char32_t>を使うべきだ。

筆者の意見では、UTF-8, UTF-16, UTF-32を簡単に変換できる関数と、相互に通過的に変換するイテレーターを提供した上で、正規表現を使いたければbasic_regex<char32_t>に一本化する方法がいいと思う。

P0170R0: Wording for Constexpr Lambda

constexpr lambda式の文面案。

auto f = []( int x ) { return x ; } ;
constexpr int i = f(0) ; // OK

P0171: Response To: Resumable Expressions P0114R0

Resumable Expressionに対して寄せられた懸念事項に答える文書。

言語規格がスケジューリングまで定めるべきではないという意見に対しては、提案しているのはシンタックスシュガーだけで、スケジューリングの詳細はライブラリが実装すると回答。

resumable関数を呼び出す際にawaitを書き忘れると、実に不具合箇所を特定しにくい不具合の元になるという意見に対しては、現在提案中の戻り地を無視すると警告する[[nodiscard]]のような属性を提案すればよいと回答。

P0172R0: Abominable Function Types

C++の型システムには、コンパイラー開発者とメタプログラマーしか知らない闇がある。Abominable functionと名付けられた型のことだ。

abominable functionとは、CV修飾やリファレンス修飾された関数型のことだ。

using regular = void () ;
using abominable = void () const volatile && ;

非メンバー関数はCV修飾やリファレンス修飾することはできない。しかし、関数型はCV修飾、リファレンス修飾ができてしまう。

このようなabominable function型は、ほとんど利用価値がないが、traitsを実装する際に個別に対応しなければならないため、問題になる。

このAbominable functionをどうにかしようと問題提起する文書。

ドワンゴ広告

1月23日にはドワンゴ主催のプログラミングコンテストが行われる。

第2回 ドワンゴからの挑戦状 | 株式会社ドワンゴ

前回と同じく、競技プログラミングの環境にはあのAtCoder(株)の社長であり最強最速アルゴリズマー養成講座の著者でもある高橋直大氏のAtCoder (アットコーダー)を利用している。

このコンテストの予選を突破し、2月13日の本選の上位入賞者の2017年新卒には、採用試験の一部免除などの特典があるそうだ。

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

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

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

2016-01-18

C++標準化委員会の文書のレビュー: P0144R0-P0159R0

P0144R1: Structured Bindings

多値を返す関数の戻り値を簡単に変数に束縛できるようにするための文法の提案。

現在、C++ではtupleがあるために、多値を返す関数を簡単に宣言することができ、また簡単に多値を返すことができる。

std::tuple< T1, T2, T3 > f( )
{
    T1 a{} ; T2 b {} ; T3 c { }
    return { a, b, c } ;
}

見ればわかる通り、極めて簡単だ。

呼び出し側で多値を受け取るのも、比較的簡単である。

T1 a ; T2 b ; T3 c ;
std::tie( a, b, c ) = f() ;

確かに、比較的簡単ではあるが、このコードはいろいろと問題がある。

変数をあらかじめ宣言しなければならない。もし、型がPOD型ならば、未初期化の状態となり、お作法上あまりよろしくない。

型がクラス型の場合、デフォルト構築が行われる。その直後にコピー/ムーブして上書きするのに、デフォルト構築するのは無駄だ。

そこで、複数の変数の宣言と、その変数群を多値のそれぞれの値で初期化する新しい文法を追加する提案。

現在、以下の文法が提案されている。

auto { a, b, c } = f() ;

この宣言文は、変数、a, b, cを宣言する。変数の型はそれぞれ独立して初期化子から推定される。

この提案では、以下の2つの新しい文法を提案している。

auto { list-of-comma-separated-variable-names } { expression };
auto { list-of-comma-separated-variable-names } = expression;

expressionは、tupleとpairの場合、それぞれの要素で型推定され、初期化される。式は変数と同じ個数の要素をもたなければならない。

std::pair< T1, T2 > p{ ... } ;
auto { a, b } = p ;

std::tuple< T1, T2, T3 > t{ ... } ;
auto { x, y, z } = t ;

式には、クラス型を指定することもできる。このときクラス型のすべての非staticデータメンバーはアクセス可能で、ひとつの基本型(そのクラス型も含む)で宣言されていなければならない。

struct X { int a ; double b ; } ;

X x{ 0, 0.0 } ;

// aはint、bはdouble
auto { a, b } = x ;

クラスが無名unionメンバーを含む場合、最初のメンバーが選ばれる。


struct X
{
    union { int i ; double d ;} ;
    int data ;
} ;

X x{ {0}, 0 } ;

// xはint型で値は0
auto { x, y } = x ;

autoにCV修飾子やlvalueリファレンス修飾子を使うこともできる。

auto const { x, y, z } = f() ;
auto const & { x, y, z } = f() ;

rvalueリファレンス修飾子のサポートについては、議論中。

この文法は、initializer_listからの初期化はサポートしない。

braced-init-listからの初期化もサポートしない。

// サポートしない
auto { x, y, z } = { 1, 2, 3 } ;

理由は、この文法を追加するのは簡単であるが、この文法の有益な利用例がいまのところないため。

再帰的な構造化解除はサポートしない。

std::tuple< T1, std::tuple< T2, T3>, T4 > f() ;

// サポートしない
auto { a, {b, c}, d } = f() ; 

将来の拡張案としては興味深い。

[PDF] P0145R0: Expression Order of Evaluation

式のオペランドの評価順序を固定する提案。

たとえば、f( a, b, c )という式があるとき、オペランドf, a, b, cがどの順番で評価されるのかは、規格上は未規定(unspecified)とされていた。そのため、シークエンスポイントを隔てることなく、2つのオペランド中の式が同じオブジェクトを変更するとき、挙動は未定義となる。例えば、f( i++, i )のような式で、iが整数型の変数の場合、挙動は未定義となる。v[i] = i++も同じだ。

f( i++, i )やv[i] = i++のような昔からよく知られた問題ばかりではない。例えば、以下のようなコードの挙動も未規定だ。


#include <map>

int main()
{
    std::map< int, int > m ;
    m[0] = m.size() ; // #1
}

#1が評価された後のmapの中身はどうなっているだろうか。{{0,0}}だろうか、{{0,1}}だろうか。規格上は未規定だ。

式の評価順序が未規定という問題は、単にプログラマーの娯楽とか、採用試験とか、学術的な興味にとどまる問題ではない。現在の規格の制約は、現実の日常的なプログラミングに問題を引き起こしている。例えば以下のコードだ。

void f()
{
    std::string s = “but I have heard it works even if you don’t believe in it”
    s.replace(0, 4, “”).replace(s.find(“even”), 4, “only”).replace(s.find(“ don’t”), 6, “”);
    assert(s == “I have heard it works only if you believe in it”);
}

s.replace(...).replace(...).replace(...)と、いわゆる"method chaining"的なメンバー関数呼び出しの仕方をしている。これらがすべて、ひとつの式の中のサブ式であるので、その評価順序は未規定である。評価順序が未定義な以上、assertは引っかかる可能性がある。findの後に、そのfindを含まない別のreplaceが評価されるとassertに引っかかる。

このコードの問題点は、極最近になってツールで検証した結果明らかになった。

このコードは、Bjarne StroustrupのThe Programming Langauge 4thに載っているコードであり、この本は世界屈指のC++専門家達によって査読されていた。そのような環境ですらこの問題が発覚しなかったということは、現在の規程に問題がある。

このようなメソッドチェイニングが問題なのだとする批判は当たらない。なぜならば、std::cout << e1 << e2 << e3のような式も影響を受けるし、std::future<T>はメソッドチェイニングを前提としたthen()メンバー関数を追加する予定である。問題はメソッドチェイニングではない。

しかし、評価順序の未規定ルールは、何十年も存在する。なぜ今変えるのか。当時の制約ある環境では、この規程は理由があるものであった。時代と環境が変わった今、当時は適切だった規程が適切ではなくなっている。そのために変える必要がある。

この文書が提案する評価順序は以下の通り。

  • 後置式は左から右に評価される。これには関数呼び出し式とメンバー選択式も含まれる。
  • 代入式は右から左に評価される。これには複合代入も含まれる
  • シフト演算子のオペランドは左から右に評価される。

結果として、以下の式はすべて、a, b, c, dの順に評価される。

a.b
a->b
a( b, c, d )
b @= a
{ a, b, c, d }
a[b]
a << b
a >> b

オーバーロードされた演算子を使った式の評価順序は、組み込み演算子の評価順序と同じになる。関数呼び出しと同じ順序ではない。

P0147R0: The Use and Implementation of Contracts

現在提案されているcontracts案と似たような文法を使ってどのようなコードが書けるかという例示のための文書。contractsは、関数が満たすべきpreconditions, invariants and postconditionsを記述できる。

[PDF] P0148R0: memory_resource_ptr: A Limited Smart Pointer for memory_resource Correctness

memory_resourceをラップするスマートポインター、memory_resource_ptrの提案。

memory_resourceとは、ライブラリに追加される各種ヒープメモリーを実装したクラスのポリモーフィックな基本クラスだが、生のポインターを使うのはいろいろと不便なので、memory_resourceに特化したスマートポインターを追加する。

[PDF] P0151R0: Proposal of Multi-Declarators

多値を個々の変数で受け取る宣言文法として、以下のようなものがP0144で提案されている。

std::tuple< T1, T2, T3 > f() ;
auto { a, b, c } = f() ;

この文書は、以下のような別の文法を提案している。

tuple<T1, T2, T3><T1 x, T2 y, T3 z> = f(); // 多値のクラスと変数型の明示的な指定
tuple<T1, T2, T3><x, y, z> = f(); // 多値のクラスの明示的な指定
<T1 x, T2 y, T3 z> = f(); // 変数型の明示的な指定
<x,y,z> = f(); // 明示的な指定なし

また、使わない変数の省略を認めている。

// 2番めの変数は無視される
<a, c> = f() ; 

個人的には、autoキーワードを使う文法のほうがわかりやすいし、この文法が提案している柔軟な機能にどの程度の需要があるのか疑問だ。

P0152R0: constexpr atomic<T>::is_always_lock_free

コンパイル時にatomic<T>が常にロックフリーかどうかを確認できるconstexprメンバー関数is_always_lock_freeを追加する提案。

P0153R0: std::atomic_object_fence(mo, T&&...)

atomic_thread_fence(memory_order)に似ているが、指定したオブジェクトのみsequenced before関係を発生させるatomic_object_fence( memory_order, T && ... )の提案。

P0154R0: constexpr std::hardware_{constructive,destructive}_interference_size

std::hardware_constructive_interference_sizeとstd::hardware_destructive_interference_sizeの提案。

この2つのconstexpr関数は、一般にキャッシュラインサイズと呼ばれている値を取得するためのもの。

2つのオブジェクトがあり、ランタイムアクセスパターンがそれぞれ異なる(例えばあるオブジェクトは頻繁に変更するのに、もう一方のオブジェクトはほとんど変更しない)とする。CPUのキャッシュはキャッシュラインサイズと呼ばれる単位で行われており、この2つのアクセスパターンの異なるオブジェクトが同じキャッシュライン上に載っている場合、一方のアクセスパターンに引きづられて、本来必要のないキャッシュからメモリへの書き込みが行われてしまう。これをfalse-sharingと呼ぶ。

false-sharingを避けるには、異なるキャッシュライン上にオブジェクトが配置されるために、オブジェクトの配置されるメモリアドレスに十分なオフセットを儲けなければならない。hardware_destructive_interference_sizeは、false-sharingを避けるために必要な最小限のオフセットサイズを教えてくれる。

逆に、2つのオブジェクトのランタイムアクセスパターンが似通っていて、同じキャッシュライン上に載っている場合を、true-sharingと呼ぶ。true-sharingが行われるためには、2つのオブジェクトの合計サイズがキャッシュラインサイズに収まらなければならない。

hardware_constructive_interference_sizeはtrue-sharingされるための上限のサイズを教えてくれる。

この2つの値の定義は、実質の同じ意味なので、同じ値になるのではないかと思うのだが、2つに分けたのは、単にコードの意図をわかりやすくするためだろうか。それともこの値の異なる環境が実際に存在するのだろうか。

[PDF] P0155R0: Task Block R5

fork-join並列コードを書くためのライブラリ、task_blockの提案。

例えば、ツリー構造を並列実行でたどるときに

template<typename Func>
int traverse(node *n, Func&& compute)
{
    int left = 0, right = 0;
    define_task_block([&](task_block& tb) {
        if (n->left)
            tb.run([&] { left = traverse(n->left, compute); });
        if (n->right)
            tb.run([&] { right = traverse(n->right, compute); });
    });
    return compute(n) + left + right;
}

このように、define_task_blockに関数オブジェクトを渡すと、task_blockが実引数に渡される。あとはそのメンバー関数のrunを実行するたびに並列に実行が分岐する。

P0156R0: Variadic lock_guard (Rev. 2)

lock_guardをVariadic Templatesにする提案。

std::mutex m1, m2 ;

void f()
{
    // m1, m2に対してlock()が呼び出される
    std::lock_guard<std::mutex, std::mutex> lock( m1, m2 ) ;
    // 処理

    // lockのデストラクターでm1, m2にunlock()が呼び出される。
}

なお、make関数はない。lock_guardはコピーもムーブもできないからだ。

P0157R0: Handling Disappointment in C++

ある関数が呼び出し元の期待する処理を完了できなかった場合、呼び出し元は失望(disappointment)する。関数はその失望をどのようにして呼び出し元に伝えるのか。

この文書は、慣習的に使われている通知方法を列挙して考察している。

戻り値

戻り値は、最も一般的なC言語的手法であり、大抵はintかenumが使われる。失敗時には成功時特別可能な特別な値が使われる。この通知方法には問題がある。

エラー処理と通常の処理とが混ざってしまう。エラー処理が面倒なため、プログラマーはエラーを無視したがる。エラー処理に戻り値を使うと、通常の結果の値を戻り値ではなく実引数を経由した上書きで渡す必要が出てくる。呼び出し元がエラー通知に反応するには、事前に通知される値について知っていなければならない。

特別な戻り値

C言語で慣習的に用いられている方法で、戻り値を通常の結果通知に使うと同時に、特別な値を使って、エラー通知にも使う方法だ。特別な値には、nullポインターやゼロや-1などが用いられる。

この方法で通知できるのは、たいていはエラーの有無だけであり、エラーの具体的な内容については、別の方法で通知しなければならない。別の方法には、errnoのようなグローバルなオブジェクトが使われる。これは並列化を阻害する。

実引数を経由したエラー通知

これもC言語で慣習的に行われている方法で、実引数にエラー通知を受け取るためのオブジェクトへのポインターを取る方法だ。

これは、ループ文の条件式の中で使えないとか。エラー通知を完全に無視まではできないものの、結局無視されやすいという問題はある。

多値

関数の結果と、エラー通知の両方を多値で返す。これは古典的なC言語では行われていない方法だが、Goのような最近の言語では組み込みの多値を返す機能があるために使われている。

long jump

エラー通知にlong jumpを使う例が存在する。long jumpは関数内で起こった状態を解消するための手段を持たず、関数内で状態を持たないか、エラー発生時に状態を無視していい、極めて制限された環境でしか使えない。

例外

例外は上記のエラー処理の問題をいくつも解決している。通常のコードとエラー処理コードを分離できる。戻り値の型にデフォルトコンストラクターが要らない。補足されなかった例外はコールスタックを上がっていく。例外は未知のエラー通知にも使える。スタックフレームを遡る例外通知は、ローカルのオブジェクトを破棄していくため、エラー専用の破棄処理がいらない。

例外には欠点もある。まず例外は重い処理であるということ。頻繁に発生する「失望」を例外で伝えるには重すぎる。例外の存在は関数呼び出しにオーバーヘッドを発生させるので、極めて資源制約の強い環境では使えない。

エラーに対処してもう一度試行する処理を書けない。

論文では、様々なエラー処理を比較した結果、今後の規格は、現在提案中のexpectedやstatus_valueのような多値を返すエラー処理を推奨している。

P0158R0: Coroutines belong in a TS

コルーチンには様々な問題が山積みで、C++17に直接追加するのは時期尚早であるので、TSとして出すべきだと主張する文書。

現在コルーチンに持ち上がっている様々な問題が列挙されている。

Technical Specification for C++ Extensions for Concurrency, DTS

複数のfutureがready状態になるまで待つwhen_all、複数のfutureのうちどれかひとつがready状態になるまで待つwhen_any、futureのmethod chaining的に使えるメンバー関数then、wait_for, wait_untillatchとbarrier、atomic_shared_ptrといった並列同期に関するライブラリのTS。

ドワンゴ広告

今日は雪だったが、いつもどおりドワンゴ標準時で出社した結果、特に影響はなかった。

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

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

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

2016-01-12

C++標準化委員会の文書のレビュー: P0100R1-P0136R1

今回も改訂版文書のレビューをしていく。改定前の文書のレビューは同番号の過去の記事を参照。

本の虫: C++標準化委員会の文書のレビュー: P0100R0-P0109R0

P0100R1: Comparison in C++

比較の強さ別にカスタマイゼーションポイントとなる関数を用意する提案。

比較には、同一比較と、順序比較がある。順序比較には、partial orderとweak orderとtotal orderがある。これまで、順序比較にはoperator <などの演算子が、その種類を問わず使われてきた。どの比較を提供できるかは、型により異なるので、比較ごとの方法を提供できる関数テンプレートを追加する。カスタマイズするには、これをオーバーロードすればよい。

template<typename T> bool partial_less(const T&,const T&);
template<typename T> bool weak_less(const T&,const T&);
template<typename T> bool total_less(const T&,const T&);

template<typename T> bool partial_unordered(const T&,const T&);
template<typename T> bool weak_equivalence(const T&,const T&);
template<typename T> bool total_equal(const T&,const T&);

P0112R1: Networking Library (Revision 7)

Boost.Asioをベースにしたネットワークライブラリの提案。

変更点は、io_serviceをio_contextに、wrap()をbind_executor()に改名。package()をuse_futureの関数呼び出し演算子に、const_buffers_1とmutable_buffers_1クラスの廃止。const_bufferとmutable_bufferが直接要件を満たすことに鳴った。const_bufferとmutable_bufferにdate()とsize()メンバー関数を追加。buffer_cast<>とbuffer_size()の代替案。

P0136R1: Rewording inheriting constructors (core issue 1941 et al)

継承コンストラクターの文面を書き直す提案。これにより継承コンストラクターの挙動が僅かに変わるそうだが、影響はほぼない。

ドワンゴ広告

そういえば、今年からドワンゴで勉強会にも使っているセミナールームは歌舞伎座タワーではなく、ADK松竹スクエアの13階に変更になっている。

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

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

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

2016-01-11

GCC 6にインデントミスの警告機能が追加

GCC 6にインデントミス警告の機能が追加された。ドキュメントのコミットログは以下の通り。

gcc.gnu.or g Git - gcc.git/blobdiff - gcc/doc/invoke.texi

このインデントミスの警告機能は、-Wmisleading-indentationオプションで有効にできる。

if ( condition )
    foo() ;
    bar() ; // 警告

具体的に説明すると、この機能は、if, else, while, forの中の文がブロック文ではなく、かつ、文に続いて同じインデントのif, else, while, forではない文が続く場合に警告する。

例えば、以下のようなコードは、for文のオペランドとしての文に続いて同じインデントレベルの文が続くが、for文なので警告は出ない。


const std::size_t I = 10, J = 10, K=10 ;
int a[I][J][K]

for ( std::size_t i = 0 ; i != I ; ++i )
for ( std::size_t j = 0 ; j != J ; ++j )
for ( std::size_t k = 0 ; k != K ; ++k ) // 警告なし
{
    a[i][j][k] = 0 ;   
}

この警告は、プリプロセッサーにより生成された結果のコードには適用されない。

if ( condition )
    foo() ;
#if CONDITION
    bar() ; // 警告なし
#endif

理由は、プリプロセッサーによって生成された結果のコードは機械的に生成されたもので、人間が読むものではないから、人間向けのインデントは意味をなさないからだ。

ドワンゴ広告

今日は祝日で休みだ。

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

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

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

2016-01-09

Linus Torvalds、Microsoftが「ジャンプしてみろよ」と言えばIntelとAMDはジャンプする

LKML: Linus Torvalds: Re: [PATCH 0/3] TLB flush multiple pages per IPI v5

IntelのCPUのTLBの挙動に、頻出するパターンにおける最適化らしきものが施されていることが観測できることに対して議論した後で、

前にも言ったように、Transmetaで働いていた時期、俺はNT以前のWindowsがどういう世界だったかということを垣間見た。GDI protection traversalはGDIがカーネル側に入るたびにTLBを全部フラッシュするらしく、また当時の一部のグラフィックベンチマーク(これはまだハードウェア支援されたVGAグラフィックが一般的ではなかった時代のことだ)は、5千から1万命令以内にTLBを全部ふっとばす実装になっていた。そのため、IntelとAMDはTLB fillを高速にするために多大な労力を割くだけの理由があった。なぜならば、GDIベンチマークは当時重要だったからだ。当時のグラフィックベンチマークというのは、基本的な2Dウインドウ処理やフォント描画のベンチマークのことだ。

RISCベンダーは全く気にしなかった。奴らと来たら完全にクソなハードウェアで、ソフトウェアパートナー(大方はデータベース)に、ソフトウェアを変更して、large-pageを使うようにしたり、TLBミスを回避すべく努力させた。奴らのコンパイラーはロードを早期に行い、ストアを遅延させた。というのも、メモリサブシステムは完全にオモチャだったからだ。TLBミスはパイプライン全体をぶっ壊すなどしていた。本当にクソなハードウェアで、まだ期待している奴もいる。残念なことだ。

Windowsの業界では、そんなことは望みようがなかった。Microsoftが、「おう、ジャンプしてみろよ」と言ったならば、IntelとAMDはどちらも「どれだけ高く飛べばいいのでしょうか?」と言ったものだ。結果として、x86はどのRISCよりも柔軟だった。なぜならば、IntelとAMDはどんなクソなソフトウェアでも実行しなければならなかったからだ。ソフトウェア開発者に最適化をさせる代わりに。

ドワンゴ広告

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

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

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

x.orgドメインが消失の危機

X.Org Might Lose Its Domain Name - Phoronix

x.orgドメインが消失の危機に陥っているそうだ。

もともと、x.orgドメインは、X.Org Foundation LLCという団体によって登録管理されてきたが、x.orgがアメリカ合衆国における非課税の非営利団体 501(c)(3)に認定されるにあたって、この団体は解体された。

現在のX.org団体はx.orgドメインを管理しておらず、ドメインは1月19日に失効する。

x.orgドメインの登録時の連絡先は、Leon Shimanなる人物になっており、これはかつてアクティブに活動していた昔のX.org団体のメンバーの一人らしいが、連絡がつかないらしい。

2016-01-07

C++標準化委員会の文書のレビュー: P0022R1-P0092R1

今回も改訂版の文書のレビュー。改定前の文書は、以下の記事でレビューしている。

本の虫: C++標準化委員会の文書のレビュー: P0021R0-P0029R0

本の虫: C++標準化委員会の文書のレビュー: P0030R0-P0039R0

本の虫: C++標準化委員会の文書: P0050R0-P0059R0

本の虫: C++標準化委員会の文書のレビュー: P0060R0-P0069R0

本の虫: C++標準化委員会の文書のレビュー: P0080R0-P0089R0

本の虫: C++標準化委員会の文書のレビュー: P0090R0-P0099R0

P0022R1: Proxy Iterators for the Ranges Extensions

vector::iteratorのようなプロクシーイテレーターとそれ以外のイテレーターがうまく汎用的に扱えない問題を解決するライブラリの変更の提案。

これまで存在はするもののあまり活用されていなかったiter_swapを、例外的な挙動をするイテレーターのためのカスタマイゼーションポイントとして定義しなおすほか、iter_moveを追加する。イテレーターを操作する際は、このライブラリを使えば、プロクシーイテレーターを意識せずに扱うことができる。

また、共通の束縛できるリファレンス型を得る。common_reference traitsを追加する。2つのイテレーターのvalue_typeをcommmon_referenceに渡すことでコードを汎用的にできる。common_referenceはcommon_typeに似ているが、トップレベルのCV修飾子とリファレンス修飾子を削らない。

P0025R1: clamp: An algorithm to 'clamp' a value between a pair of boundary values -

clamp( value, min, max )で、valueがminより小さければminが、maxより大きければmaxが、それ以外の場合はvalueが返る関数clampの提案。

[PDF] P0030R1: Proposal to Introduce a 3-Argument Overload to std::hypot

3引数版std::hypotの提案。

[PDF] P0032R1: Homogeneous interface for variant, any and optional (Revision 1)

variant, any, optionalというtype erasure機能を提供する用途の異なるライブラリが提案されている。type erasureという機能では共通しているこれらのライブラリは、インターフェースがバラバラだ。インターフェースをある程度統一する提案。

[PDF] P0051R1: C++ generic overload function (Revision 1)

非情に面白いoverloadライブラリの提案。関数オブジェクトを突っ込んで、突っ込んだ関数オブジェクトの中でオーバーロード解決が最適なものを呼び出してくれる。

std::f = std::overload(
    []( int x ) { }, // #1
    []( double d ) { }, // #2
    []( auto x ) { } ) ; // #3

f( 0 ) ; // #1
f( 0.0 ) ; // #2
f( "hello" ) ; // #3

実装例は前回の記事で解説したが極めて単純で興味深い。

前回からの変更点としては、最適関数を選ぶoverload, 呼び出し可能な最初の関数を選ぶfirst_overload, 格納した関数オブジェクトにアクセスする機能の3種類に提案を分割し、それぞれ独立して提案することにしたらしい。

[PDF] P0057R1: Wording for Coroutines

コルーチンの文面案。変更点は、とうとうキーワードが決定されたこと。co_await, co_yield, co_returnになった。なんだか泥臭い名前だ。しかし、await, yieldなど使えるわけがない。

future<void> g()
{
   std::cout << "processing f" << std::endl ;
   co_await f() ;
   std::cout << "resumed" << std::endl ;
}

うーむ。バイク小屋バイク小屋。

P0061R1: Feature-testing preprocessor predicates for C++17

プリプロセッサーでのみ使える__has_includeの追加。ヘッダーファイルが存在するかどうかを調べられる。

#if __has_include(<any>)
#include <any>
using lib = std ; 
#elif __has_include(<boost/any.hpp>)
#include <boost/any.hpp>
using lib = boost ;
#endif

lib::any a ;

[PDF] P0083R1: Splicing Maps and Sets (Revision 3)

listにあるsplice機能をmapにも提供する提案。前回からの変更点は、node_ptrがnode_handleになったこと。operator *, operator ->が廃止され、かわりにmappedとvalueというアクセッサー関数が追加されたこと。空の状態を調べられるempty関数が追加された。機能テストマクロが追加されたなど。

mapが管理する内部の動的に確保されたメモリ上に構築されたノードの所有権をmapから切り離すことができる機能。

extractでmapからノードの所有権を切り離す。切り離されたノードはnode_handleクラスを経由して扱う。node_handleはアロケーターのコピーも持っているので、破棄された時にはノードも破棄される。キーを変更することもできる。mergeでnode_handleの所有するノードをmapにマージできる。

キーを変更して差し戻すことにより、余計なメモリの破棄、確保を省略することができる。

P0092R1: Polishing chrono

chronoライブラリに対する機能追加。丸めモードの設定、符号付きduration型にabsを追加する。

ドワンゴ広告

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

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

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

C++標準化委員会の文書のレビュー: P0001R1-P0018R1

内容は以下で解説しているものの改訂版。

本の虫: C++標準化委員会の文書2015-09 pre-Konaのレビュー: P0001R0-P0009R0

本の虫: C++標準化委員会の文書 2015-09 pre-Kona: P0011R0-P0020R0

P0001R1: Remove Deprecated Use of the register Keyword

registerキーワードの廃止。変更点はCとの互換性の一覧に追加。ドラフト入りした。

P0002R1: Remove Deprecated operator++(bool)

operator ++(bool)の廃止。変更は文面上の些細な間違いの修正にとどまる。ドラフト入りした。

P0004R1: Remove Deprecated iostreams aliases

iostreamのdeprecatedされていたライブラリを廃止。変更は互換性の一覧に追加。ドラフト入りした。

P0005R1: Adopt 'not_fn' from Library Fundamentals 2 for C++17

汎用的なnot_fnの追加。変更点はネストされた型名result_typeの削除。理由はISO/IEC JTC1/SC22/WG21 p0090r0による。

P0005R2: Adopt 'not_fn' from Library Fundamentals 2 for C++17

not_fnのさらなる改訂版。result_typeの復活。

P0007R1: Constant View: A proposal for a 'std::as_const' helper function template

実引数をconstなlvalueリファレンスとして返すstd::add_const。変更点は文面案の追加。

以下のような実装になる。

namespace std
{
    template< typename T >
    inline typename std::add_const< T >::type &
    as_const( T &t ) noexcept
    {
        return t;
    }

}

P0012R1: Make exception-specifications be part of the type system, version 5

P0012R1: Make exception-specifications be part of the type system, version 5

関数の無例外指定を型システムに含める提案、関数ポインターに無例外指定を型として含めることができる。変更は些細。

P0013R1: Logical Operator Type Traits (revision 1)

boost MPLにあるand_, or_, not_の提案。ただし、生枝がことなる。

前回の提案は、and_, or_, not_だったが、今回の提案では、議論の結果、名前が変わっている。それぞれ、conjunction, disjunction, negationとなっている。

template < typename T, typename U >
void f()
{
    // std::is_same_v<T, U> && std::is_integral_v<T> && std::is_signed_v<T>
    // と同じ
    constexpr bool b = std::conjunction_v< std::is_same<T, U>, std::is_integral<T>, std::is_signed<T> > ;
}

negationはともかく、conjunctionとdisjunctionは英語を母語とせず、数学の素養もなく、コンピューターサイエンスのアカデミックの経歴もない筆者にはわかりにくい気がするのだが、いいのだろうか。

とはいえ、この関数は初心者が使うものでもないし、これでいいのかもしれない。

P0014R1: Proposal to add the multiline option to std::regex for its ECMAScript engine

regexにECMAScriptにあるmutlilineオプションを追加する提案。multilineオプションを使うと、^と&の挙動が変わり、文字列の戦闘と末尾ではなく、文字列の各業の戦闘と末尾にまっ地するようになる。

P0017R1: Extension to aggregate initialization

基本クラスを持つクラス型をアグリゲート初期化できるようにする提案。

基本クラスを持つクラスはアグリゲート初期化できない。

struct base { int x ; } ;
struct derived : base
{
    int y ;
} ;

// エラー、derivedは基本クラスを持つ
derived d{ 1, 2 } ;

これに対し、直接の基本クラスを宣言順で初期化できるようにしようという提案。

// {1}はbase::xの初期化,
derived d{ {1}, 2 } ;

P0018r1 : Lambda Capture of *this by Value

lambda式で*thisをコピーキャプチャする機能の提案。

lambda式では、thisでキャプチャするのはポインターである。メンバー名を使った場合、キャプチャしたthisポインターを経由したアクセスが行われる。

struct X
{
    int member ;

    auto f()
    {
        // this->memberと同じ
        return [=]() { return member ; }
    }
} ;

クラスのオブジェクトの寿命が尽きた後もクロージャーオブジェクトを使いたい場合に、問題になる。

int main()
{
    std::function< int () > f ;

    {
        X x ;
        f = x.f() ;
    }// xの寿命、ここまで

    f() ; // エラー、xはすでに破棄されている。
}

C++14では、明示的なキャプチャー機能が追加された。

struct X
{
    int member ;

    auto f()
    {
        // memberはコピーされる
        return [ =,  member = member ]() { return member ; }
    }
} ;

問題は、データメンバーが複数ある時、これをいちいち書くのは面倒だ。明示的なキャプチャーで、クラスのオブジェクト自体をキャプチャーすることはできる。

struct X
{
    int member ;

    auto f()
    {
        // memberはコピーされる
        return [ =, self = *this ]() { return self.member ; }
    }
} ;

しかし、この例では、thisポインターは依然としてキャプチャーされてしまう。もし、self.memberのかわりにmemberと書いてしまうと、this->memberとして扱われる。極めて危険で間違いの元だ。

そこで、新しいラムダキャプチャーに、*thisを追加する。ラムダキャプチャーに*thisと書くと、クラスのオブジェクトをコピーする。


struct X
{
    int member ;

    void f()
    {
        // this->memberと同じ
        [this]{ member ; }

        // *thisを値でコピーする。
        // memberはクロージャーオブジェクトにコピーされたオブジェクトを参照する。
        [*this]{ member ; }
    }
} ;

つまり、以下のようなクロージャーオブジェクトが生成されると考えればよい。


struct closure_object
{
    // *thisをコピーする
    X unnamed_copy ;

    void operator () ()
    {
        unnamed_copy.member ;
    }
} ;

これは欲しい機能だ。

ドワンゴ広告

正月明けで昼夜逆転してしまった睡眠サイクルを強引に修正しようとした結果、DST(ドワンゴ標準時)から-4時間ほどずれてしまった。午前中に出社し、夕方過ぎに帰宅するようになってしまった。

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

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

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

yumetodoが本当に欲しかったもの

template引数として与えられた2つの型TypeFromとTypeToがあるとき、TypeFromからTypeToへの変換がintegral promotionsであるか判別し、そうであるならstd::true_type、違うならstd::false_typeを継承するクラスを作るにはどうかけばいいのか?

https://ask.fm/EzoeRyou/answers/134026904663

integral promotionsとは、C++規格の§4.5で規定されている。問題は、integral promotionsの内容を忠実に判定すると、charからshortへの変換はintegral promotionではないし、intからlongへの変換もintegral promotionではない。

それでもいいのであれば、実装は以下のようになる。

// check if from can be converted and represented to to.
template < typename from, typename to >
constexpr bool can_represent()
{
    return std::is_same< typename std::common_type< from, to >::type, to >{} ; 
}

template < typename from, typename to >
constexpr bool is_integral_promotion_impl()
{
// as per section 5.4 paragraph 6
    if ( std::is_same<from, bool>{} )
    {
        return std::is_same<to, int>{} ;
    }

// as per section 5.4 paragraph 3
    if (
            std::is_same<from, char16_t>{} ||
            std::is_same<from, char32_t>{} ||
            std::is_same<from, wchar_t>{} )
    {
        if ( can_represent<from, int>() )
            return std::is_same<to, int>{} ;
        else if ( can_represent< from, unsigned int>() )
            return std::is_same< to, unsigned int >{} ;
        else if ( can_represent< from, long int>() )
            return std::is_same< to, long int>{} ;
        else if ( can_represent< from, unsigned long int>() )
            return std::is_same< to, unsigned long int >{} ;
        else if ( can_represent< from, long long int >() )
            return std::is_same< to, long long int>{} ;
        else if ( can_represent< from, unsigned long long int>() )
            return std::is_same< to, unsigned long long int>{} ;
    }

// as per section 4.5 paragraph 3-4
    if ( std::is_enum<from>{} &&
        // requires lazy intantiation because underlying_type<T>::type is ill-formed for non enum T.
        std::is_same< to, typename std::conditional< std::is_enum<from>{}, std::underlying_type<from>, std::decay<void> >::type::type >{} ) 
    {
        return true ;
    }

// as per section 4.5 paragraph 1
    if ( !std::is_integral<from>{} ||
        !std::is_integral<to>{} )
    {
        return false ;
    }

    if ( std::is_same< to, int >{} &&
         can_represent<from, int>() )
    {
        return true ;
    }

    if ( std::is_same< to, unsigned int >{} &&
         can_represent<from, unsigned int>() )
    {
        return true ;
    }
         

    return false ;
}


template < typename from, typename to >
struct is_integral_promotion
    : std::integral_constant<bool, is_integral_promotion_impl<from, to>() >
{ } ;

ただ、fromがtoに変換できてかつ精度を落とすことなく完全に表現できる程度であれば、

template < typename from, typename to >
struct is_representable
    : std::integral_constant<bool, can_represent<from, to>() >
{ } ;

これぐらいでもいいのではないか。

しかし、constexprがあるのでこの手の処理は書きやすくなったと思ったら、std::underlying_typeのような問題がある。これを簡単に賭けるようにするため、constexpr_ifの導入が必要だ。

ドワンゴ広告

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

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

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

2016-01-05

C++標準化員会の文書のレビュー: N4553-N4567

[PDF] N4553: Working Draft, C++ extensions for Concepts

コンセプトTSのドラフト

[PDF] N4554: Editor's report for the Concepts TS

コンセプトTSのドラフト編集者の報告書

[PDF] N4555: February 2016 WG21 Meeting

2016年1月に開催される会議の告知

N4556: WG21 telecon minutes

2015年10月9日に行われた電話会議の議事録。

N4557: WG21 2015-07-20 Telecon Minutes

2015年7月0日に行われた電話会議の議事録

N4558:WG21 2015-11 Kona Minutes (Draft)

2015年10月19日から24日にかけて行われたKona会議の議事録。

[PDF] N4559: Kona WG21 Minutes

2015年のKona会議の議事録のドラフト。

会議の参加者とその所属一覧が興味深い。

また、Appleが投票権を失ったとも書いてある。最近人を出していないからなのだろう。

[PDF] N4560: Working Draft, C++ Extensions for Ranges

軽量コンセプトを用いたRangeコンセプトライブラリの提案、Range TSのドラフト。Rrangeコンセプトを定義することにより、コンテナーを直接アルゴリズムに渡すことができる。

std::vector<int> v ;

// 従来のイテレーター
std::sort( begin(v), end(v) ) ;
// Rangeコンセプト
std::sort( v ) ;

[PDF] N4561: Ranges Editor's Report

Range TSの編集者の報告書。N4560はRange TSの最初のドラフトであり、その内容はP0021R0に由来する。

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

標準ライブラリの提案。

意味的なconst性をメンバー関数を経由しても伝えるpropagate_const、汎用的なBM検索、ある型のオブジェクトを格納しているか、格納していない状態を表すoptional, どんな型でも格納できるany, 異なる文字列表現をラップしてくれるstring_view, 新しいメモリ確保のインターフェースと、新しいヒープメモリ確保の実装、所有しないバカポインターobserver_ptr, コンテナーに対するフリー関数版のerase, araryの拡張, 汎用的なサンプリングアルゴリズムの実装、GCDとLCMの実装、簡単に使える乱数ライブラリrandint, ソースファイルの情報を取得できるsource_location。などなど

[PDF] N4564: C++ Extensions for Library Fundamentals, Version 2 PDTS

N4562と内容は同じ。

[PDF] N4565: Record of Response: National Body Comments ISO/IEC PDTS 19571 Technical Specification: C++ Extensions for Concurrency

Concurrency TSに対するNBコメントに対する返答。日本からは文面の誤りと、コンテナーをvectorに限定せずに汎用的にするコメントが出された。文面の誤りは採用された。コンテナーを汎用にする提案は拒絶された。

n4566: Editor's Report -- Working Draft, Standard for Programming Language C++

C++ドラフトの編集者の報告書。

興味深いcore issuesの解決は以下の通り。

Core issue 1722が解決された。

lambda式のクロージャーオブジェクトの関数ポインターへの変換関数は無例外保証(noexcept(true))を持つようになった。

// 無例外保証
void (*ptr)() = []{} ; 

Core issue 1949が解決された。

規格は、AがBより先に処理されるという意味の、"A sequenced before B"という用語の意味を定義していて、この意味を表現する時はこの用語を統一して使うべきなのにもかかわらず、"B sequenced after A"なる表現がいくつかの箇所で見られるので、sequenced beforeに修正する。

core issue 2004の解決。

規格の文面を解釈すると、mutableなvariant memberがある場合に、constexprで型システムを壊して実行時書きかえができてしまう場合を修正。

  union U { int a; mutable int b; };
  constexpr U u1 = {1};
  int k = (u1.b = 2); // OK, bはmutable
  constexpr U u2 = u1; // おおっと?

core issue 2024を解決。pack expansionがテンプレートに依存するように規定する文面がないことを修正。こんな例が見逃されていたとは以外。

core issue 2026を解決。定数初期化にもゼロ初期化が行われるように解釈できる文面を修正。定数初期化ではゼロ初期化は行われず、明示的に初期化されない定数初期化は違法になるという従来の挙動を維持。これも考えると以外な見逃しだ。

core issue 2031の解決。C++11ではリファレンス修飾子として&&を追加したが、これは従来の演算子の&&と互換性の問題を発生させるコード例が見つかったので、それを互換性の項目に付け加える修正。

以下のようなコードが該当する。

  struct Struct { template <typename T> operator T(); };
  bool example_1 = new int && false;               // #1
  bool example_2 = &Struct::operator int && false; // #2

C++03では、#1,2ともに、&&は演算子であり、合法である。C++11では、リファレンス修飾子となるため、違法である。

まあ、まず書かないようなコードだ。

core issue 2052の解決。特殊化の結果生成されたシグネチャが組み込み演算子と同じだった場合を違法にする。

その他のドラフト入りしたコア言語の変更点。

registerキーワードの廃止, operator++(bool)の廃止。例外指定を型システムに取り込む変更、__has_includeの追加、非staticデータメンバーの宣言箇所における初期化に対して、default member initializerという名前をつける文面の変更、継承コンストラクターの文面の書き直し。

ライブラリの変更点としては、Adopt Type Traits Variable Templates from Library Fundamentals TS for C++17を追加したのがとても大きい。また、Variadic lock_guard (Rev. 2)も入った。

[PDF] N4567: Working Draft, Standard for Programming Language C++

現時点で最新のC++ドラフト規格。

ドワンゴ広告

新年初出社。

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

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

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

v

2016-01-03

江添ボドゲ会1月の調整さん

例のごとく、1月も木場にある筆者の自宅でボドゲ会をしようと思うのだが、人数と都合のいい日の把握のために、調整さんを作った。

江添ボドゲ会1月 | 調整さん

4人以上集まれば開催する。

2016-01-02

教えてgooのリコメンドが怪しい

正月の朝には雑煮を食べる慣習がこの2016年に存在する。筆者も慣習に従い、雑煮の調理を開始した。正月の雑煮の作り方は様々だが、共通項としては、具材に餅が含まれることと、だし汁で具材を煮込むことがある。今年の雑煮は、人参、しいたけ、たけのこ、鶏肉をゆでた上で、三つ葉も入れた。

鶏肉は、もも肉を使った。鶏肉は部位によって特性が違うが、特にもも肉と胸肉がよく使われる。もも肉は胸肉より高い。なぜだろうか。もも肉は胸肉よりも取れる量が少ないから高いのであろうか。しかし、せせり(首)は少量しか取れないが、それほど高くはない。もも肉は油が多く美味だから高く、胸肉は油が少なく調理方法を工夫しないとパサパサして美味しくないから安いのだろうか。すると、レバーはそのままでは臭みがあり、調理が面倒なのでとても安くなっているべきなのだが、そうでもない。

この疑問を解決するため、筆者はGoogleにお伺いを立てることにした。Googleは以下のURLを返した。

鶏肉はなぜもも肉より胸肉の方が安いのですか?- 素材・食材 | 教えて!goo

教えてgooは利用者同士の質問と回答をするためのWebサービスである。回答をみてみると、もも肉のほうが需要が高いからであるという意見が多かった。

出典がないが、需要の差というのは常識に照らし合わせて納得できる説ではある。

さて、右側に目を転ずると、「このQ&Aを見た人がよく見るQ&A」という項目があり、他の質問へのハイパーリンクが貼られている。これは、リコメンドと呼ばれる機能である。この質問の見た人に共通して特によく見られている別の質問は、この質問を見た人にとって見る価値がある可能性が高い。

しかし、そのリコメンドされた質問の候補が不思議だ。

  1. 鶏の胸肉はなぜ安い?- 素材・食材 | 教えて!goo
  2. 行為のときに、女性の体を見て幻滅したことある男性- 性の悩み | 教えて!goo
  3. 夫婦でのセックス頻度ってどれくらいですか?- 性の悩み | 教えて!goo
  4. セックス挿入について- 性の悩み | 教えて!goo

1はいいとして、2から4までの候補は、本当にこの質問を見た人がよく見ているのであろうか。まるで「胸」という単語に単純に反応したかのように思える。

第一、この質問から2-4の質問に直接飛ぶ方法が、このリンクしかないではないか。下のほうに、「胸」で質問を検索するURLが生成されているが、この検索結果は、女性が胸の悩みが多く、2-4の質問は出てこない。

他の質問を見ても、このように極端なリコメンドは存在しない・・・と思っていたが、ランキング上位のカテゴリー素材、食材の質問のリコメンドみると、やはり2-4が上位にきている。「胸」のような性の悩みに関連する単語すら出てこない質問だ。

なるほど、どうやら、「これを見た人がよく見るもの」という意味でのリコメンドは正しいようだ。2-4の質問はあまりにも閲覧者が多いため、食材に関する質問と言った、全く関係ない質問に対しても共通する閲覧者が多いためにリコメンドされてしまうらしい。一度リコメンドに乗るとハイパーリンクが貼られるため、その興味深い質問文につられてさらに閲覧者が増える効果も予想できる。そうなると、リコメンドが固定されてしまう。

リコメンドはもう少し工夫してほしいものだ。