2016-07-23

中古PCを買った

「PCが遅い」と嫁が言う。「10万ぐらいのちゃんとしたPCがほしい」と嫁が言う。

そんな馬鹿な。今時10万円もあれば新品のゲーミングラップトップが買える。観測する限り、嫁はPCでWebブラウザーしか使っていないではないか。閲覧しているWebサイトも、TwitterやFacebookぐらいなものだ。

嫁のPCのスペックを確認するために、Windowsのコントロールパネルを開こうと試みるが、たしかに動作が遅い。型番を調べてググったほうが早そうなほどの時間分またされた挙句、ようやくCPUやメモリ量を確認できた。どうやらAllendale世代のCeleronを積んでいるようだ。

確かに動作が遅いが、不自由でプロプライエタリなソフトウェアであるMicrosoft Windowsについて調べる気にはならない。GNU/Linuxをインストールすればこれよりはマシになるだろう。しかし、聞けばこのPCは卒論を書くのにも使ったという。嫁は全て消しても構わないというが、いろいろとストレージには過去の歴史が残っているであろうから、おそらく消すべきではないのだろう。別のストレージを買ってきて取り替えてからGNU/Linuxをインストールするという手もあるが、スペックも低く、すでに外傷が激しいこの8年ぐらい前のPCをそこまでして温存するべき理由はない。

嫁はコンピューターをよく壊す人間である。スマフォやタブレットは半年も立たないうちに壊してしまうので、最近はガラケーを使っているという。ガラケーのあの形状は、比較的落下時の衝撃に強いのではないかと思う。

以上のことから考えると、数万ぐらいの中古PCを買って、GNU/Linuxをインストールして使わせるのが良いように思う。利用目的には十分な性能と、壊しても惜しくない入手価格であればいいのだ。

そこで、秋葉原にいっていろいろと見て回った結果、AcerのICONIA W700というタブレットを中古で買うことにした。選択した理由は、解像度が1920x1080であるのと、CPUがx86-64で使い勝手がよく、しかもAtomではなくSandy Bridge世代のCore i3-2365Mであることだ。値段は税込みで3万8千円だった。安い理由は、ディスプレイに気泡が入っていることだ。しかし、ディスプレイの隅なので、それほど気にならない場所だ。

タブレットを扱うの初めてだったが、Ubuntuのインストールは何事もなく完了した。

ICONIA W7のBIOSメニューの入り方は本体のWinボタンを押したまま電源ボタンを押し、ベンダーロゴが画面に現れたら両方とも離すという方法であった。ブート順序とSecure bootを無効(私はMicrosoftを信用していないため、Microsoftが署名したバイナリを信用すべき理由がない)にした後、USBストレージに入れたUbuntuをブートしてインストールする。どうやら、Ubuntuのインストーラーはキーボードとマウスを接続していない環境でもインストールが進められるように、スクリーンキーボードが出せるようになっているようだ。今回はキーボードとマウスを使ったので使わなかった。

インストール後に多少の設定をしたのち、使い心地を確かめてみた。UbuntuのUnityは有名なタッチパネルのジェスチャーに対応しているようだ。また、Chromiumはタッチパネルのジェスチャーに対応していたが、Firefoxはまだ対応していなかった。

Ubuntu 16.04には、タブレットの向きに合わせてディスプレイを自動で回転させる機能はなかった。xrandrを使ってディスプレイの回転を切り替える簡単なシェルスクリプトを書いてLauncherにでも設定しておけばとりあえずはいいが、できれば自動で回転して欲しいところだ。GNU/Linuxにはジャイロセンサーの入力を受け取る共通の方法がまだないのだろうか。

さて、今回、初めてタブレットをまともに触ったが、やはり最新のデバイスに触っていないと時代から取り残されると思い、自分用にもタブレットを入手した。Endevor TN10Eだ。これはCPUにAMD A4-1200(Temash)を積んでいて、やや遅い。しかし、ディスプレイの解像度は1920x1080なので、テキストを読むぐらいには使えるだろう。たまたま、同僚がWindowsの再インストールに失敗したものを持っていたので、4500円で買い取った。

Endevor TN10EのBIOSメニューに入るには、音量調整ボタンの+-を同時押ししながら電源ボタンを押す。また、USBキーボードを接続している場合には、F2キーを連打するでも入れる。必要な設定をしたあと、これまたUbuntuをインストールした。

インストール後に使ってみた感想としては、やはりAMD A4-1200は遅い。とはいえ、テキストを読むぐらいには使えそうだ。タブレットの経験に使い潰してみようと思う。

さて、もうひとつ中古PCを買った。dynabook T95だ。

15.6インチの3840×2160ディスプレイ、Haswell世代のCore i7 4710HQ、Radeon R9 M265X、メモリはDDR3が8GB(16GBに増設可能)、ストレージには1TBのハイブリットHDDがついて、8万5千円ほどだった。

やすい理由は、ディスプレイの真ん中よりやや左に縦一直線におそらく1ピクセル幅の常時白点灯ラインがあることだ。とても気になるが、それ以外のスペックは8万5千円で手に入るならば魅力的だ。

さて、Ubuntuをインストールして設定を終え、いろいろと使ってみたが、結論としては、ディスプレイのライン抜けを考慮しても、やはり4Kディスプレイはいいという結論に至った。

結局、我々晴眼者は、コンピューターからの入力方法に眼球を使っている。したがって、効率化のためには眼球にできるだけ多くのピクセルを叩き込むべきである。高PPIは正義だ。高PPIは未来だ。我々は高PPIだ。

また、AMDのGPUはGNU/Linuxで使うのに都合がいい。Ubuntu 16.04では、AMDの不自由でプロプライエタリなバイナリブロブのドライバーは一掃された。もはやUbuntuのレポジトリにはAMDのバイナリブロブなドライバーは存在しない。AMDのGPU用の自由なドライバー実装は十分な品質に達している。

最近、あまりPCのスペックにこだわりがなくなってしまった。いや、実際には逆で、こだわりがありすぎるために、新しくPCを購入する際には、現時点で最高のスペックのPCを入手しなければ気がすまない。30万円だして現時点で最高のスペックのPCを買ったとしても、一年後には陳腐化するし、常に破損、故障の可能性はある。ならば、最初から安い中古PCを使えばいいのではないか。中古なのでスペックには諦めが尽くし、壊れても安ければ気にならない。

2016-07-22

江添ボドゲ会@8月13日

8月13日に江添の自宅でボドゲ会を下記の要領で開催します。

江添ボドゲ会@8月13日 - connpass

気軽にご参加ください。

2016-07-21

C++標準化委員会の文書: P0290R0-P0299R0

P0290R0: apply() for synchronized_value

N4033で提案されているsynchronized_value<T>に対してapply()関数を提供する提案。第一引数に関数オブジェクトを取り、第二引数以降のsyncrhonized_valueを適用する。

synchronized_value<T> s1, s2, s3 ;

int main()
{
    int i = 0 ;
    apply( [&]( auto && s ) { s = ++i ; }, s1, s2, s3 ) ;

P0292R1: P0292R1: constexpr if: A slightly different syntax

この提案はC++17に採用された。以下の内容はまだ公開されていないが採択されたP0292R2の内容を元にしている。

constexpr ifの提案。この提案は条件付きコンパイルではない。実行されない方のブランチのテンプレートの実体化を防ぐif文だ。

constexpr ifの文法は、if文と条件の間にconstexprキーワードを記述する。


if constepxr ( condition )
    statement
else
    statement

conditionは文脈上boolに変換される。もしconditionがfalseならば、1番目のstatementが、trueならば2番目のstatementが、discarded statement(廃棄文)となる。

constexpr ifの実行時の挙動は、if文と同じだ。conditionの値に応じて、どちらかの文が実行される。ただし、コンパイル時の挙動が少し違う。

constexpr ifを持つテンプレートエンティティが実体化されるとき、discarded statement内のテンプレートの実体化は行われない。

template < typename T >
void f( T t )
{
    if constexpr ( std::is_same_v< std::decay_t<T>, std::string > )
    {
        t.compare( "hello" )
    }
    
}

関数fは、実引数にstd::stringが渡された場合、メンバー関数のcompareを呼び出す。もし実引数にstd::stringが渡されなかった場合は、conditionがfalseになるので、constexpr if文のsubstatementはdiscarded statementとなる。テンプレート内のdiscarded statementは、インスタンス化されないので、変数tにメンバーcompareがなかったとしても、コンパイルエラーにはならない。

constexpr ifは、以前提案されていたstatic ifと違い、条件コンパイルではない。テンプレート内で使った時に、discarded statementがインスタンス化されないだけだ。それ以外はif文と同じだ。

例えば、ill-formedなトークン列は、当然ill-formedになる。

// ill-formed
if constexpr ( false )
{
!@#$%^&*()
}

インスタンス化せずともill-formedなテンプレートのコードは当然ill-formedとなる。

template < typename T >
void f()
{
    if constexpr ( false )
    {// ill-formed
        not_found ;
    }
}

このコードでは、非依存名であるnot_foundを使っているが、not_foundは宣言されていないのでill-formedとなる。

constexpr ifを非テンプレートなコードで使うことはできるが、通常のif文と同じ意味しかない。

void f()
{
    // ifと違いはない
    if constexpr ( false )
    {
    }
}

[PDF] P0295R0: Adopt Selected Library Fundamentals V2 Components for C++17

gcdとlcmをLibrary Fundamentals V2から抜き出してC++17に入れる提案。

P0296R2: Forward progress guarantees: Base definitions

Forward Progress Guarantee(ブロックされていないスレッドはいずれは必ず実行が進む保証)を文面に入れる提案。

[PDF] P0298R1: A byte type definition

1バイトを表現するstd::byte型の提案。

std::byte型は、underlaying typeがunsigned charなscoped enum型として追加される。

ビット列を操作する演算子がオーバーロードされている。

char型は符号の有無が未規定で生のバイト列を扱うには不適切な型である。

P0299R1: Forward progress guarantees for Parallelism TS features

タイトル通り、Parallelism TSにおけるforward progress guaranteeの文面の追加。

ドワンゴ広告

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

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

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

2016-07-17

ダイヤルQ2風の電話番号でInstagramやGoogleやMicrosoftから金をむしりとれる脆弱性

セキュリティ研究者が、とても興味深い脆弱性を報告して報奨金をもらった記事が上がっている。

How I Could Steal Money from Instagram, Google and Microsoft – Arne Swinnen's Security Blog

プレミアムナンバーという電話上のサービスがある。これは一時期日本で行われていたダイヤルQ2と同等の仕組みを持つサービスで、プレミアムナンバーという電話番号にかけた電話の通話料は、通常より高い。通話料の差分は、電話サービスの提供元に支払われる。

ダイヤルQ2は電話越しに何らかのサービスを提供して、電話料金で利用料を徴収できる、手軽な仕組みだった。その利用例は、投資顧問、アダルト、占い、人生相談、義援金、ダイヤルアップISPなどに利用されていた。ダイヤルQ2自体は2014年に終わったが、海外ではまだ同等の仕組みをもつサービスが残っていて、一般に、プレミアム通話料金率電話番号と呼ばれている。

ところで、一部のWebサイトは、ユーザーが指定した電話番号に自動的に電話をかける機能を提供している。その理由は例えば以下のようなものだ。

  • ユーザーが人間であることを確かめるCAPTCHA文字列を合成音声で伝え、その文字列をWebサイトに入力させるため
  • 二段階認証のセキュリティトークンとなる文字列を合成音声で伝え、その文字列をWebサイトに入力させるため
  • アカウントに登録した電話番号が本人のものであることを確かめるため

それ以外にも理由はあるかもしれないが、ユーザーの入力した電話番号に自動的に電話をかけるという機能は、もし電話番号としてプレミアム番号が与えられた場合、課金される脆弱性がある。この研究者はこの脆弱性を研究し、それぞれ報告した。

Instagram:アカウントへの電話番号のひも付け

Instagramはアカウントに電話番号をひも付けて検索で見つけさせることができるが、その電話番号に対してSMSで文字列を飛ばし、入力させる。もし、文字列が3分以内に入力されない場合、カリフォルニアから電話をかける。通話時間は17秒だ。

電話番号のひも付けリクエストはレートリミットされているので30秒に一回しか送れない。しかし、Instagramはどんな電話番号にでも電話をかけてくれる。たとえば、eurocall24.comでは0.06ポンド/分の割合で課金できる。

したがって、計算をすると、30分で1ポンド、一日で48ポンド、1年で17280ポンド、1アカウントとプレミアム電話番号で稼げることになる。とはいえ、攻撃者は100アカウントほど用意して攻撃速度を100倍にできる。すなわち、一日で4800ポンド、一ヶ月で144000ポンド、一年で1728000ポンドだ。

また、複数のアカウントで一つのプレミアム電話番号を使いまわせる。

Facebookに連絡したところ、これは想定の範囲内であり、対策も講じてあり、許容範囲内のリスクであるとの返答を得た。手で100アカウントをつくって自動化した攻撃をするのは簡単であると重ねて連絡をとったところ、最終的に、2000ドルの報奨金と、慈善団体への4000ドルの寄付を得た。

Googleの場合: 二段階認証

Googleは二段階認証のための6桁のセキュリティトークンを、ユーザーが指定した電話番号に伝えてくる。電話番号はプレミアム番号も受け付ける。妥当なセキュリティトークンを入力しないと、数回ほど電話を試みたあとに、ブロックされる。

しかし、eurocall24.comは電話のSIPサーバーへの転送をサポートしており、SIPクライアント経由で電話内容を聞くことができる。

すると、あとは音声認識してトークンを入力してログインするまでの工程を自動化すればいいだけだ。

しかし、Googleはログインが試みられてさえいればよく、ログインが成功したかどうかまでは判定していない。電話がかかってきたあとにログイン試行さえ行われていればブロックはされない。そのため、音声認識の部分は省略でき、電話がかかってきたらそのアカウントにログインを試みるスクリプトを書いて自動化した。

電話は、一時間あたり10回に制限されているようだ。一回あたりの通話時間は約35秒だ。したがって、一日に12ポンド、一ヶ月で360ポンド、一年で4320ポンドせしめることができる。しかし、攻撃者は100アカウントを使うことで攻撃速度を100倍できる。一日で1200ポンド、一ヶ月で36000ポンド、一年で432000ポンドだ。Googleの場合は、プレミアム番号もアカウントごとにユニークなものが必要とされる。

Googleにバグ報告すると、「その脆弱性を使ってplease.break.in@gmail.comに侵入してみせよ」と返事が来た。そのような脆弱性ではないことを伝えると、「先のメールは手違いで送られたものである」と返事が来た。その後、「この問題は些細なものでセキュリティ上の懸念はほぼなく、バグ報酬に値しない」というメールが来た。問題が存在することをさらに解説すると、「先のメールは自動的に送信されたもので、送るべきではなかった。まだ調べている」と返事が来た。ただし、「金はユーザーデータに比べれば大した問題ではない。金は重要であるには違いないが、損失分を取り戻すのは、ユーザーの信頼を取り戻すことに比べれば、遥かに容易である。皮肉なことは私も認めるが、ユーザーにとっては理的だw」とのコメントもあった。

Googleの最終的な回答としては、「我々は対策を講じてあるが、電話の仕組み上、完全に防ぐことは難しい。大量の金を引き出す試みは、対策により阻止される。そのため、我々はこの報告に対して報奨金を支払わない判断をした。先に述べた如く、Googleの金銭的損失はユーザーのセキュリティほど重要ではない。とはいえ、取り上げるには値する報告なので、殿堂入りリストには載せておく」

Microsoftの場合:Office 365の体験版の登録

Office 365の体験版の登録にあたって、電話番号を入力してMicrosoftに電話をかけさせることができる。電話番号にはプレミアム番号も受け付ける。番号は7回の登録失敗でブロックされる。

しかし、このブロックをすり抜ける2つの方法が発見された。

電話番号の前にゼロを追加する。ゼロは最大18個追加することができる。また、ゼロの代わりにプレミアム番号の国番号を追加することもできる。これにより、以下の数式の回数のブロック回避が行える

\[\sum_{n=1}^{18}n=171\]

トータルの回数は171+1で172通りだ。

電話番号の末尾に最大4桁のランダムな番号を追加する。

\[\sum_{i=0}^{4}10^i=11111\]

するとトータルの回数は

\[(172 \times 11111) \times 7 = {13377644} {電話回数/プレミアム番号} \]

一回の通話には約23秒かかる。計算を簡単にするために20秒にしよう。プレミアム番号の稼ぎは1分あたり0.15ユーロだ。

\[(13377644/3) \times 0.15 = 668882 ユーロ/プレミアム番号\]

Microsoftは同一の電話番号に対する並列した電話を認めている。攻撃者はこの手法を自動化すること多額の金を掠め取れる。

Microsoftにバグ報告したところ、電話番号の前と後に桁を追加してブロックを回避できる仕様が修正された。報奨金として最小の額である500ドルが支払われた。

Microsoftの回答によれば、電話サービスはアウトソースしているため、Microsoft自体には被害はないが、報奨金を支払う判断をしたそうだ。

「これは確かに脆弱性で、しかもうまいものだ。そのため我々は報奨金を支払う判断をした。セキュリティの立場から言えば、我々は顧客を守ることに重点を置いている。この脆弱性は報奨金に値するし修正に値するが、顧客のデータに対するリスクはない。我々は常に、セキュリティ研究者に、我々がユーザーを守るための役に立つような研究をしてくれることを依頼しているが、この場合、守られたのは我々と我々の協力会社のようだ。」

この脆弱性は大したものだ。

作業が早いプログラマーと遅いプログラマーの差の比は4:1

An empirical study of working speed differences between software engineers for various kinds of task

プログラマーの作業速度には差がある。作業速度が早いことだけをもって優秀なプログラマーとは限らない。そのソフトウェアの保守性が悪いかもしれないからだ。しかし、やはり作業速度の早いプログラマーは優秀と見られがちだ。特に、転職界隈では、優秀なプログラマーは、その作業速度の速さを形容して、「ニンジャ」とか「10倍プログラマー」などというタイトルで喧伝されている。さて実際には、プログラマーの作業速度は、全体としてどの程度違うのか。

プログラマーの作業速度が早いものと遅いものの比は、従来、28:1であると言われてきた。この数字には根拠となる研究がある。1967年にGrantとSackmanが公開した論文[1]で、実験をした結果、28:1であると結論しているからだ。

[1]: "E. Eugene Grant and Harold Sackman. An exploratory investigation of programmer performance under on-line and off-line conditions. IEEE Trans. on Human Factors in Electronics, 8(1):33–48, March 1967."

問題は、その実験内容というのが、たった12人の経験豊富なプログラマーの被験者に2つの問題を解かせただけなのだ。

たったの12人の結果など、真の結果からかけ離れている可能性も大いにある。しかし、この論文はあまり批判されることがなく、28:1という数字のみが独り歩きしてしまい、今日の10倍プログラマーなどという都市伝説を生み出している。

この論文は、十分なサンプル数を集めてみたところ、28:1という比を否定した。

そもそも、プログラミングには様々な種類の作業がある。この論文では、プログラミングを以下の作業に分類した。

  • 保守(既存のコードの理解と変更と拡張)
  • 理解(例、特定の質問に応えるなど)
  • テスト/デバッグ(テストとデバッグ)
  • レビュー(誤りがないかの確認)
  • プログラミング(設計、実装、テスト、デバッグ)
  • 設計
  • コーディング(実装)

分類の判断に迷う場合は、プログラミングか保守に分類した。また、純粋なコーディング単体のみの作業はまれなので、分類上はプログラミングになっている。

結果としては、作業の種類によって、作業速度のばらつきには差が見られた。テスト/デバッグやプログラミングでは差が大きかったが、保守や理解では差が小さかった。レビュー作業の時間は差がとても小さかった。

結論として、プログラマーの作業速度の差は、4:1を超えることはめったにない。バラつきの大きい作業においても、大抵は2:1から3:1であった。

体感では、こういう短期間の実験では、乱数が相当に左右するのではないだろうかと思う。運悪くtypoをして何時間も悩むことがよくあるために。

2016-07-14

C++標準化委員会の文書: P0280R0-P0289R0

P0280R0: Initialize unspecified aggregate members with direct list initialization

以下のコードのコンパイルが通るようになる変更。


struct S
{
    explicit S(int a = 0) : _a{a} { }
    int _a;
};

int main()
{
    S s ; // OK
    S arr[2] = { S{} }; // エラー
}

現行の文面ではコンパイルが通らないが、これはコンパイルが通ることが望ましい。

P0281R0: Remove comma elision in variadic function declarations

昔ながらの可変引数関数でコンマを書かずにellipsisを書ける昔ながらの文法を除去する提案。

以下は合法なC++のコードである。


int f( ... ) ; // 1
int f( int i, ... ) ; // 2
int f( int i ... ) ; // 3

1はポータブルな方法で可変引数を取り出すことができない。どんな引数でも受け付けるので、メタプログラミングではよく使われている。

2は通常使う関数の宣言

3は歴史的経緯により今だに認められている2と同等の意味を持つ関数の宣言。iと...の間にコンマを記述していない。

この文書は、3の文法をC++から除去する提案を行っている。この文法はユーザーの混乱の元であり、Variadic Templatesとの文法の曖昧性が発見されていて、将来の言語拡張の妨げにもなるので、廃止するのが望ましいとしている。

例えば、以下のコードは極めて紛らわしい。

template <class... T> void f(T...); // パラメーターパックを引数に取る関数
template <class T> void f(T...);    // T型と可変引数を取る関数

現行のC規格ではコンマ省略が認められていないので、C言語との互換性の問題はない。

[PDF] P0283R1: Standard and non-standard attributes

実装がサポートしていないattribute namespaceは無視するように注釈を付け加える提案。

現行規格は、実装がサポートしていないattributeに対してどのように振る舞うべきか規定していない。実装がサポートしていないattributeに対してコンパイルエラーを出す場合、ユーザーはattributeをマクロで隠すようになる。これではattributeの存在意義が否定されてしまう。そのため、実装がサポートしていないattribute namespaceは無視すべきである。

[[ezoe::foobar]] int i ; // attributeは無視する

P0284R0: Unqualified enumerators in case labels

switch文のconditionがenum型の場合、そのswtich文に属するcase文では、unqualified lookupでenumeratorが見つけられるようにする提案。

以下のコードが、

enum struct E
{
    foo, bar 
} ;

void f( E e )
{
    switch( e )
    {
    case E::foo :
        break ;
    case E::bar :
        break ;
    }
}

以下のように書けるようになる。

enum struct E
{
    foo, bar 
} ;

void f( E e )
{
    switch( e )
    {
    case foo :
        break ;
    case bar :
        break ;
    }
}

これはぜひともほしい変更だ。switchのconditionがenum型であることがわかっているのだから、非修飾名でもenumeratorが見つけられるべきだ。

P0285R0: Using customization points to unify executors

ユーザー定義のexecutorを渡すcustomization pointを設ける提案。

customization pointとは、std名前空間の下に関数テンプレートがありデフォルトの実装がされている。unqualified nameで呼べばADLによって優先されるように書いておけばそちらが優先される。という仕組み。

P0286R0: A networking library extension to support co_await-based coroutines

ネットワークライブラリをco_awaitに対応させる変更の提案。

[PDF] P0287R0: Simple Contracts for C++

contractサポートの提案。preconditionとpostconditionのみに限定している。attributeで関数宣言に記述できる。

だいたい以下のような形になる。

template < typename T >
class array
{
// データメンバーなど
public :
    std::size_t size() ;

    value_type & operator []( std::size_t i )
    [[ expects: i < size() ]] ;

    void resize( std::size_t n )
    [[ ensures: size() == n ]] ;

} ;

機能的には、assert( condition )を関数の前後に挟むのと大して変わらない。プリプロセッサーマクロではなくコア言語によるサポートがあることと、コア言語でサポートされるので、コンパイラーオプションなので有効無効を切り替えられる実装にできるぐらいか。

文書はもうひとつ、[[ assert : condition]]を提案している。

[PDF] P0288R0:A polymorphic wrapper for all Callable objects

unique_functionの提案。std::functionとほぼ同じだが、コピーコンストラクター、コピー代入演算子がない。そのため、コピーできないcallable型を格納できる。

[PDF] P0289R0: Forward declarations of nested classes

ネストされたクラスの前方宣言を認める制限緩和をする提案。以下のように書けるようになる。

class X::A ;
X::A ptr = nullptr ;

現状では、以下のように書かなければならない。

class X
{
    class A ;
} ;

ネストされたクラスの宣言を囲む直前のクラスの定義の中で宣言しなければならない。不完全型のX::Aしか必要ない場面でも、完全型のXが必要になる。つまり、Xの定義が必要になる。

この制限緩和をする提案。

ドワンゴ広告

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

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

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

2016-07-13

C++14でCommodore 64の開発をする

x86から6502への変換ソフトウェアをつくった上で、GCCでC++14のコードをx86にコンパイルして、そこから6502に変換して、Commodore 64エミュレーターで実行している。

Hacker Newsのツッコミによると、6502ではスタックを表現するのが難しいので、コードもスタックを使うような変数を意図的に避けているのだそうだ。

C++14 for the Commodore 64 [video] | Hacker News

ドワンゴ広告

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

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

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

2016-07-12

C++標準化委員会の文書: P0270R0-P0279R0

P0270R0: P0270R0: Removing C dependencies from signal handler wording

シグナルハンドラーの文面を書き直す提案。

現在のC++規格におけるシグナルハンドラーは、C規格との共通項のみ使えると定義している。

しかし、現在のC++規格が参照しているC規格はC11である。ということは、thread_localが共通項になるが、thread_localは使えるべきではない。

C言事の共通項というと、Cリンケージを指定するextern "C"も使えないということになるが、それではシグナルハンドラー自体が宣言できない。

nullptrを使えない技術的な理由は何もない。

など、様々な問題があるので、この機会に文面を新たに書きなおす提案。

一般に、シグナルハンドラーのなかでは、ロックを発生させない操作のみ許されるべきだとしていて、POF(Plain-Old-Function)や、 signal-safe functionという用語を定義している。

現実的には、シグナルハンドラーの挙動は何も変わらない。既存のコードは動くのもの動かないものも含めてそのままだ。

この文面の変更により、例えばlambda式がシグナルハンドラーの中で使えるようになった。というのも、lambda式の実装にはメモリの動的確保が必要ないから、禁止する理由がないためだ。

P0271R0:

T型を特定の型リストで構築した時に、実際に呼ばれるコンストラクターの実引数の型を知るために、実際に呼ばれるコンストラクターの型を返すtype traits、std::direct_init<T>の提案。

std::variantで例外を投げる変換関数を持った型が渡された場合でも例外安全にvariantを実装するために必要。

P0272R1:Give 'std::string' a non-const '.data()' member function

あまりにもひどいクソ提案なので解説する価値なし。CreateProcessの第二引数は書き換えを行うし第一引数にNULLを渡すべきではないしCreateProcessAを使うべきでもない。

[PDF] P0273R0: Proposed modules changes from implementation and deployment experience

Clangでモジュールを実装して使用した経験から、モジュール規格の改良提案。

モジュール宣言はソースファイルのどこにでも書いていいことになっていたが、その仕様だと、ツールはソースファイル全体をスキャンしてモジュール宣言があるかどうか確認しなければならない。モジュール宣言はソースファイルの最初の宣言に書く制約をつける。

異なるモジュールで名前が衝突した場合、異なるエンティティが与えられるが、名前の衝突に対する対策としては名前空間がすでにある。モジュールでも名前衝突を回避する機能を別に提供すると、名前空間が使われなくなる。名前の衝突を回避する方法としては名前空間のみが提供すべきだ。モジュールの外にでるエンティティは名前が衝突した場合エラー。モジュールの外に出ないエンティティは別々になるようにする。

モジュールの循環参照は禁止されているし、その理由は妥当なものだが、それだと全モジュールに同じ宣言を書かなければならず、結局一つのテキストファイルに書いて#includeする方法から逃れることができない。これではモジュールの意味がない。

これを解決するために、モジュールパーティションという機能を提供する。

[PDF] P0274R0:Clump - A Vector-like Sequence Container with Embedded Storage

組み込みのストレージをオブジェクトに持った連続したメモリー上に確保されるコンテナー、clumpの提案。

clumpはvectorに似ているが、stringのsmall string optimizationに似た実装をしている。Boostのsmall_vectorと発想は同じだ。

例えば、以下のようなレイアウトで実装される。

template < typename T, std::size_t N, typename Allocator = std::allocator >
class clump
{
    char buf[ sizeof(t) * N ] ;
    T * ptr ;
} ;

要素数がN以下の場合はオブジェクト自体に埋め込みのストレージを使い、Nより多い要素数を扱うときには、アロケーターによるストレージの動的確保を行う。

vectorとは互換性がないので、置き換えて使うこともできない。例えば、insertはpair<iterator, bool>を返す。boolは、組み込みのストレージに収まったかどうかを返す。

P0275R0: A Proposal to add Classes and Functions Required for Dynamic Library Load

Boost.DLLに似たshared library(DLLや.so)を扱うためのライブラリの提案。純粋にライブラリだけの提案となっている。

std::shared_libraryは、ファイルパスを指定してshared libraryを読み込み、シンボル名が存在するか調べたり、型とシンボル名をしていしてshared library内の参照を得たりできる。

int main()
{
    std::shared_library mylib(std::filesystem::path{"mylib.so"}) ;

    if ( mylib.has("myfunc") )
    {
        auto && myfunc = mylib.get< void(void) >( "myfunc" ) ;
        myfunc() ;
    }

}

shared libraryの遅延読み込みをしたりすることもできる。また、デフォルトではシステムディレクトリーは探さず、オプションを指定する必要がある設計になっている。この理由としてシステムディレクトリを探すのは遅く、ユーザーはたいてい読み込むファイルの場所をわかっているからとしているが、理解できない。

提案ではDLL(Dynamic Load Library)という用語を使っていて、ライブラリにもdllという名称が出てくるが、インターフェース的にはdlopenのラッパーのように感じる。

例えばロード時のオプションのscoped enumは以下の通り。

    // shared library file load modes
    enum class dll_mode {
        default_mode = 0,
        dont_resolve_dll_references,    // DONT_RESOLVE_DLL_REFERENCES
        load_ignore_code_authz_level,   // LOAD_IGNORE_CODE_AUTHZ_LEVEL
        rtld_lazy,                      // RTLD_LAZY
        rtld_now,                       // RTLD_NOW
        rtld_global,                    // RTLD_GLOBAL
        rtld_local,                     // RTLD_LOCAL
        rtld_deepbind,                  // RTLD_DEEPBIND
        append_decorations,             // See [dll.dll_mode]
        search_system_directories       // See [dll.dll_mode]
    };

DONT_RESOLVE_DLL_REFERENCESとLOAD_IGNORE_CODE_AUTHZ_LEVELについてはWin32 APIのLoadLibraryEx由来、RTLDについては、dlopen由来のオプションだ。

append_decorationsというのは、ロードするファイルパスに、実装のデフォルトのプレフィクスやポストフィクスを付けてロードするオプションだ。例えば、"foobar"という共有ライブラリをロードしようとすると、Windowsならば"foobar.dll"を、GNU/Linuxならば"foobar.so"を読み込もうとする。ロードが失敗した場合、元の文字列でロードを試みる。

search_system_directoriesは、システムディレクトリからもファイルを探すオプションだ。

果たして入るだろうか。

P0276R0: A Proposal to add Attribute [[visible]]

共有ライブラリでシンボル名としてexportしたいエンティティに指定できる[[visible]]の追加

現状では、MSVCはエスクポートするシンボル名に__declspec(dllexport)を書かなければならない。GCCでは__attribute__((visibility("default"))を書かなければならない。また、シンボル名を使う際には、MSVCでは__declspec(dllimport)を書かなければならない。GCCでは書く必要がない。

この結果、以下のような汚らしいプリプロセッサーマクロが書かれることになる。

#if EXPORTING
    #   if MSVC
    #       define API __declspec(dllexport)
    #   else
    #       define API __attribute__((visibility("default")))
    #   endif
    #else
    #   if MSVC
    #       define API __declspec(dllimport)
    #   else
    #       define API
    #   endif
    #endif

この提案は既存の拡張機能を追認するものだ。

[PDF] P0277R1: const Inheritance

const派生の追加提案。const派生すると、基本クラスを改変できなくなる。派生クラスは、基本クラスのコンストラクターは呼べるが、非staticデータメンバーは変更できず、const修飾された非staticメンバー関数しか呼べなくなる。

P0278R0: P0278r0 - volatile solutions

volatileの定義を見直す提案。以下の変更をする。

  1. 未初期化のvolatileオブジェクトからの読み込みは未定義
  2. 実装はvolatileオブジェクトの値を推定してはいけない
  3. const volatileオブジェクトには初期化が必要ない

volatileの利用例として、memory mapped I/Oに使うというものがある。このとき、特定のメモリアドレスに対する読み書きアクセスは、必ず副作用が発生してほしい。

int volatile * p = ... ;

*p = 42 ;
*p = 42 ;
int x = *p ;

例えば、上のコードについて考える。同じアドレスに連続して2回同じ値を書き込んでいる。通常ならば、実装は最適化の際に、書き込みは一回で十分だと考えるかもしれない。書き込んだあとにすぐ読みだしている。実装は最適化の際に、xの値をコンパイル時に決定できる。したがって、実装は最適化の結果、以下のようなコードに変換するかもしれない。

*p = 42 ;
int x = 42 ;

volatileでは、読み書きに伴う副作用を無視しないという意味を持つ。したがって、このような最適化を禁止する。

ということを規定する。

P0279R0: Read-Copy Update (RCU) for C++

RCUライブラリの追加提案。

RCUは、ロックフリーなreader-writerを実現する。

ドワンゴ広告

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

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

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

2016-07-05

C++標準化委員会の文書: P0260R0-P0269R0

P0260R0: C++ Concurrent Queues

concurrent queueライブラリの提案。

使い方は、普通にpushやpopがあり、空きがなかったり空だったりするqueueに待ちなしで操作を試みるtry_pushやtry_popがあり、ブロックせずに操作を試みるnonblocking_pushとnonblocking_popがある。

P0261R0: C++ Distributed Counters

プログラムでは、様々な情報をカウンターで扱う。マルチスレッド環境では、このカウンターはatomic操作しなければならない。問題は、リードの頻度が低く、ライトの頻度が高いカウンターでは、atomic操作を用いるのはコストが高くつく。

このライブラリは、頻度の低いリードのコスト増と引き換えに、頻度の高いライトのコストが低いカウンターを提供する。

使い方としては、基本的にはgeneral_counterを使う。

general_counter<int> counter ;

void f()
{
    // インクリメントとデクリメント
    ++counter ;
    --counter ;

    // +=, -=
    counter += 10 ;
    counter -= 10 ;

    int result = counter.load() ;
}

カウンターへのライトは、戻り値がvoidで、カウンターの結果は返さない。カウンターの値を読むためには、メンバー関数loadを読む必要がある。

カウンターへの操作が集中する場所では、general_counterの代わりに、counter_brokerを使うことができる。counter_brokerはgeneral_counterへの参照を保持し、カウンターへのライトを一旦受け取り、counter_brokerに反映する。


general_counter<int> counter ;
thread_local counter_broker<int> broker( counter ) ;

void high_contention_thread()
{
    while ( true )
    {
        ++broker ;
    }
}

void f()
{
    int value = counter.load() ;
}

うまくライトの集中する部分を切り分けてcounter_brokerを効果的に使わなければパフォーマンスが出ない設計のようだ。

P0262R0: A Class for Status and Optional Value

P0260で提案されているConcurrent queueでは、状態と値を返す。値は返せることもあるし、返せないこともある。現在の設計は、値を返すか例外を投げるメンバー関数と、状態を返し、値はリファレンスで受け取った引数に書き込むメンバー関数とがある。

concurrent queueでは、値を返せないというのは通常起こりうることなので、例外のようなエラー処理を使うのは好ましくない。また、リファレンスで受け取ったオブジェクトに書き込むというのは、値はデフォルト構築可能であることを要求する。

理想的なインターフェースは、状態と値を同時に返すものだ。値は存在しないこともあるので、optionalが使える。しかし、状態と値のtupleを使うのは面倒だ。

すでに書いたように、concurrent queueでは、値を返せないというのは通常起こりうることなので、例外のようなエラー処理を使うのは好ましくない。したがって、状態と値を同時に返す、status_value< Status, Value >というライブラリを新たに提案する。

この提案は筋が悪い。素直にtupleを使ったほうがいい。tupleの使い勝手が悪いのならば、tupleを改良すべきだ。

P0263R1: Core "tentatively ready" Issues

コア言語で既知の問題で解決案が規格入りする準備のできたもの。

P0264R0: auto operator= considered dangerous

auto operator =有害論。

=defaultで定義される特別なメンバー関数でもautoによる戻り値の型推定をさせる作法を定着させようと言う動きに対し、auto operator =は有害であるとする文書。

autoはリファレンスにならない。

struct A
{
    // 戻り値の型はA
    auto operator = ( A & ) { /*...*/ return *this ; }
} ;

クラスAの代入演算子は、、lvalueリファレンスではなく、コピーを返す。

autoの代わりに、auto &やdecltype(auto)が使える。しかし、それでも問題は残る。

struct A { decltype(auto) operator = ( A && a ) { reteurn swap(a) ; } // 100行のコード decltype(auto) swap( A && a ) { /*...*/ return *this ; } } ;

このクラスでは、もしswapの戻り値の型を変更すると、その他の多くの特別なメンバーの戻り値の型まで影響を受けてしまう。保守しにくいコードになる。

文書の上げる最後の点の問題点はよくわからない。swapのような他のメンバーからも使われている基本的なメンバーの変更をしたら、結果がクラス全体に及ぶのは当然だと思うのだが。

[PDF] P0265R0: SG5 is NOT proposing Transactional Memory for C++17

SG5はトランザクショナルメモリーをC++17に提案しない決定をした。時期尚早である。

現行のトランザクショナルメモリーはあまりにも柔軟すぎて、現実のアーキテクチャがサポートするネイティブのトランザクショナルメモリーに落とし込めず、ジャイアントロックによる実装になるだろうから、さもありなん。

[PDF] P0266R0: Removing Restrictions on requires-Expressions

コンセプトのrequiresをbool値を期待する文脈ならどこにでも書けるように制限を緩和する提案。

筆者は現状のコンセプトは入るべきではないと考えている。

[PDF] P0267R1: A Proposal to Add 2D Graphics Rendering and Display to C++,

2Dグラフィックライブラリの提案。

205ページもある、とても長大なドキュメントだ。

内容は普通のグラフィックライブラリだ。

[PDF] P0268R0: up-to expression

シマンテックによるup-to expressionの提案。以下のような式が、

[a..b)

half-openレンジ、a, a+1, a+2, ... , b-2, b-1を生成する。

以下のように使う。

// {0,1,2,3,4}
std::vector<int> v = [0..5) ;
// "01234"
for ( int i : [0..5) )
    std::cout << i ;

up-to式は、慣習的な数学記法に従った文法を持ち、レンジコンセプトを満たす型のオブジェクトを返す。これは遅延評価されるため、メモリ使用量はmake_pair(a, b)程度であり、サイズb-aの配列ではない。up-to式はレンジオブジェクトを期待するところで使うことができる。

有名なLinuxディストリビューションのC++で書かれている11,423件のパッケージのソースコードをパースしてトークンをカウントしたACTCD16によれば、識別子iは20,224,291回使われている。識別子iは最も多く使われている識別子である。平均して、一つのソースファイルあたり8個の識別子iが使われている。

よく書かれるコード例としては、以下のようなものだ。

for ( int i = 0 ; i < 10 ; ++i ) { /*...*/ }

searchcode.comで検索したところ、for文の90%はこのような形をとっている。

このようなfor文のうち、半分はrange-based forに置き換えることができる。残りの半分はup-to式に置き換えることができる。

for ( int i : [0..10) ) { /* ... */ }

up-to式はイテレーターやポインターに使うこともできる。

for ( auto iter : [ v.begin()..v.end() ) )
{
    std::cout << *iter ;
}

up-to式はレンジとしてcomposableである。


using namespace ranges::view;
for (int i : [0..5) | reverse)
    print(i); // outputs 4 3 2 1 0
for (int i : [0..10) | stride(3))
    print(i); // outputs 0 3 6 9
for (int i : [0..10) | stride(3) | reverse)
    print(i); // outputs 9 6 3 0

up-to式は式である。その結果は変数に格納できる。

void f()
{
    auto one_to_five = [0..5) ;

    for ( int i : one_to_five ) ;

    auto f = []( auto x ) { return x ; } ;
    auto a = f( one_to_five ) ;
}

文書では、将来的には[)だけではなく、(), [], (]にも対応できるとしている。

趣旨はわかるのだが、ライブラリでいいのではないのかという気もする。

そして、この提案はコンセプトと、コンセプトによるレンジライブラリが存在することを前提にしているので、使えるとしてもまだまだ先の話だ。

[PDF] P0269R0: Allocator-aware regular expressions

これもシマンテックの提案。std::regexをアロケーターに対応させる提案。

設計上の都合、今更他のライブラリのようにテンプレート実引数でアロケーターを取るわけには行かないので、uses_allocatorを使ってアロケーターを指定する。

シマンテックでは、regexを共有メモリ上に構築するためにregexのあロケーター対応が必須だという。

ドワンゴ広告

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

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

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

2016-06-29

C++標準化委員会の文書: P0250R0-P0259R0

P0250R1: Wording improvements for initialization and thread ids (CWG 2046)

実行単位と実行順序についての文面や言葉の使い分けを微妙に変える提案。

標準化委員会の中では、C++は将来的に、プログラムが明示的にスレッドを使っていなかったとしても、実装が自動的に初期化を並列実行することを許可したいコンセンサスがあるが、現行の文面も既存のコードもそのまま対応できるようにはなっていない。とりあえず文面を整理する。

[PDF] P0251R0: Unified Call Syntax Wording

統一関数呼び出し記法の提案。

f( x, y )という式を書いた時に、f( x, y )を満たすfが存在しない場合、x.f( y )を呼び出す機能のことだ。

template < typename T >
void f( T x, T y )
{
    compare( x, y ) ;
}

この例では、もしcompare( x, y )を満たすcompareが存在しない場合、x.compare( y )の呼び出しが試みられる。

当初の提案に上がっていた、x.f(y)をf(x,y)として扱う機能は省かれた。

これにより、ジェネリックコードは渡された型について気にする必要がなくなる。

[PDF] P0252R1: Operator Dot Wording

operator .をオーバーロード可能にする提案。operator .をオーバーロードした型に対するメンバーアクセスは、原則としてoperator .の戻り値に対して適用される。型に同じ名前のメンバー名がある場合を除く。

template < typename T >
class vector_ref
{
    std::vector<T> v ;
public :
    std::vector<T> & operator .() { return v ;}
    std::vector<T> const  & operator .() const { return v ;}

    std::size_t size() const { return 1 ; }
} ;

int main()
{
    vector_ref r ;

    // operator .を経由した
    // vector_ref.vに対する操作
    r.push_back(0) ;

    // vector_ref.sizeを呼ぶ
    r.size() ;
}

これにより、スマートリファレンスを実装することが可能になる。

operator .はクラス型から、リファレンス型を返すことができる。operator .をオーバーロードした型を、リファレンスサロゲート型と呼ぶ。複数のoperator .が記述されている場合、メンバー名によるオーバーロード解決が行われる。

struct A
{
    int x ;
} ;

struct B
{
    int y ;
}

struct C 
{
    A a ;
    B b ;
    A & operator .() { return a ; } 
    B & operator .() { return b ; }
} ;

int main()
{
    C c{ } ;

    c.x = 0 ; // c.a.x
    c.y = 0 ; // c.b.y
}

名前が衝突した場合は曖昧なためill-formedになる。

struct A
{
    int x ;
} ;

struct B
{
    int x ;
}

struct C 
{
    A a ;
    B b ;
    A & operator .() { return a ; } 
    B & operator .() { return b ; }
} ;

int main()
{
    C c{ } ;

    c.x = 0 ; // エラー、曖昧
}

リファレンスサロゲート型によって初期化した場合、オーバーロード解決により最適なoperator .が選ばれる

struct X
{
    int i ;
    double d ;

    int & operator . () { return i ; }
    double & operator . () { return d ; }
} ;

このようなリファレンスサロゲート型に対して、

以下はwell-formedとなる。

X x{ 0, 0.0 } ;
int i = x ; // well-formed
double d = x ; // well-formed

リファレンスサロゲート型に対する代入には、そのようなオーバーロード解決が働かず、operator .も使っていないため、以下はill-formedとなるのか、それともコピー代入演算子の呼び出しもoperator .を使うからwell-formedなのか、よくわからない。


X x{ 0, 0.0 } ;
x = 0 ; // 謎
x = 0.0 ; // 謎

また、operator .と変換関数がある場合、変換関数が優先されるのではないかと思われる。

struct X
{
    int i ;
    int & operator . () { return i ; }
    operator int & () { return i ; }
} ;

X x{0} ;
int i = x ; // call conversion function

と、このようないろいろな疑問を論文著者に聞いてみたところ、実際オーバーロード解決についてあやふやなため、いま開催中のOulu会議で文書自体が却下されたらしい。

[PDF] P0253R1: Fixing a design mistake in the searchers interface in Library Fundamentals

BMサーチなどを実装するために設計されたsearcherの戻り値を、パターンにマッチした先頭へのイテレーターを返すのではなく、マッチしたパターンの先頭と末尾のイテレーターのpairを返すようにする提案。

次のパターンを探したいことがよくあるから最適化のため。

[PDF] P0254R1: Integrating std::string_view and std::string

stringからstring_viewへの変換は、stringの変換関数が担うべきであるため、そのように設計を変更する提案。

[PDF] P0255R0: C++ Static Reflection via template pack expansion

テンプレートのパック展開を用いた静的リフレクション。使いたくないほど文法が汚い。1

[PDF] P0256R0: C++ Reflection Light

現在提案されているC++の静的リフレクション機能を比較して考察している。

T型のデータメンバーのポインターを取得する

N4428提案

int size = std::class_traits<T>::class_members::size;
// テンプレートメタプログラミングにより0からsize-1までのそれぞれのIについて取得する
auto pointer = std::class_traits<C>::class_members::ge<I>::pointer;
// その後、pointerの型をそれぞれ調べて、データメンバーのみを抽出する処理が必要

P0194R0提案

typedef reflexpr(T) meta_T;
typedef std::meta::get_all_data_members_t<meta_T> meta_DMs;
int size = std::meta::get_size_v<meta_DMs>; // get number of data members
// apply template meta to recurrently pick each I, from 0 to size - 1
// テンプレートメタプログラミングにより、0からsize-1までのそれぞれのIについて取得する
typedef std::meta::get_element_t<meta_DMs, I> meta_F;
gauto pointer = std::meta::get_pointer_v<meta_F>;

P0255R0提案

auto pointers = std::make_tuple( typedef< T, is_member_object_pointer >... );

お題:プライベート・ライアンは救えるか?

struct X {
private:
    int ryan;
};

Urbana会議の結論として、privateメンバーにはうっかりアクセスできないようにしたい。既存のコードにアクセス性を与えるような変更ができないようにしたい。reflection_unsafeのように明示的な操作を必要とするようにすべきか。

[PDF] P0257R1: A byte type for increased type safety

1バイトを表現するstd::byte型を標準ライブラリに追加する提案。

現在、1バイトを表現するには、char, signed char, unsigned charが使われているが、char型には、バイト単位のアクセス、数値型、文字型という3つの目的が割り当てられており、混同しやすい。そのため、バイト単位のアクセス専門の型が必要だ。

std::byteは以下のようにscoped enumで定義されている。


namespace std {
    enum class byte : unsigned char { } ;
}

わざわざコア言語に新しいキーワードとして組み込むまでもなく、既存の言語機能だけで実現できるとしている。

また、コア言語に手を加えて、今までのvoid *からchar *に加えて、std::byte *にstatic_castした場合でもバイト単位でのアクセスが保証される。

P0258R1: is_contiguous_layout

is_contiguous_layout traitsの追加。型がstandard layout typeかつ、すべてのビットがオブジェクトの値の表現に使われる場合にtrueを返す。

例えば、パディングビットが存在するような型に対してはfalseが返される。

目的は、バイト列からハッシュ計算をする最適化ができる型かどうか判定するためだ。

[PDF] P0259R0: fixed_string: a compile-time string

constexpr実装できるbasic_fixed_stringの提案。

basic_stringはconstexpr実装できないので、コンパイル時に文字列処理をするためにconstexpr実装できる設計の文字列クラスを追加する。

constexpr auto hello = std::make_fixed_string("hello") ;
constexpr auto world = std::make_fixed_string("world") ;

constexpr auto helloworld = hello + ", " + world ;

static_assert( helloworld == "hello, world" ) ;

ドワンゴ広告

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

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

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

2016-06-28

C++標準化委員会の文書: P0240R0-P0249R0

P0240R0: Why I want Concepts, but why they should come later rather than sooner

P0225R0「何故私はコンセプトをすぐに欲しているのか」に対する反論、「何故私はコンセプトを欲しているが、時期尚早であるのか」と題された文書。

現在のコンセプトの問題は、標準ライブラリがまだコンセプトに対応していないことと、constrained templateのチェックができないことだ。

標準ライブラリはプログラマーの模範となるべきライブラリで、標準ライブラリですら対応できていない言語機能は、有用性が十分に検証されているとは言えない。また、コンセプトの標準ライブラリがないということは、ユーザーごとに基本的なコンセプトですら、非互換な車輪の再発明が行われるので好ましくない。

現在のコンセプトは、contrained templateに対するチェック機能がない。つまり、テンプレートが、指定したコンセプトの要件のみを使っているコードかどうかを、実体化せずにチェックできない。C++0x時代のコンセプトを実装したConceptGCCの経験から言えば、エキスパートであっても、手で要件を全て網羅することはできず、コンパイラーに指摘されるまで気が付かない要件漏れが発生している。この機能なしではコンセプトは使い物にならない。また、現在提案中のコンセプトは、将来の拡張性の低い設計で、将来的にチェック機能を追加する際は、未チェックのコンセプトとチェックされるコンセプトという、2種類の言語機能を入れなければならず、言語機能が分断してしまう。

P0241R0: Remove Future-Related Explicit Specializations for Void

futureとpromiseのvoid型への特殊化を除去する提案。

P0146R1で、void型は完全型になるので、特殊化は不要になる。

P0242R0: Standard Library Support for Void

標準ライブラリ、特にiostreamをvoid型に対応させる提案。

P0146R0でvoidが完全型になるので、void型に対応する必要がある。

例えば、テンプレート引数でvoid型が渡された場合、テンプレートコードはコンパイル時分岐が必要なくなる。

template < typename F >
void call_print( F f )
{
    std::cout << f() << std::endl ;
}

int main()
{
    call_print( []{} ) ;
}

提案では、void型に対する入出力は、何もしないとしている。"void"を読み書きしたりするのは、既存のコードから考えても、余計なお世話であろうとしている。

P0244R1: Text_view: A C++ concepts and range based character encoding and code point enumeration library

UTF-8/UTF-16をコードポイント単位で読めるイテレーターを提供するtext_viewライブラリの提案

using CT = utf8_encoding::character_type;
auto tv = make_text_view<utf8_encoding>(u8"J\u00F8erg");
auto it = tv.begin();
assert(*it++ == CT{0x004A}); // 'J'
assert(*it++ == CT{0x00F8}); // 'ø'
assert(*it++ == CT{0x0065}); // 'e'

U+00F8は、UTF-8でエンコードすると、0xc30'b8になってしまうのだが、コードポイント単位でのイテレーター経由で見ることができる。

tahonermann/text_view: A C++ concepts and range based character encoding and code point enumeration library

実装はGitHubで公開されているが、コンセプト機能を多用しているため、最新の開発版GCCでしか動かない。

P0245R1: Hexadecimal floating literals for C++

16進数浮動小数点数リテラルの提案

int main()
{

    double d = 0xffp0 ;

    // 255.0000, 0x1.fep7
    printf("%f, %a\n", d, d ) ;
}

文法は、プレフィクス0x/0Xに続いて、16進数桁を記述して、.で区切って小数点以下を記述し、その後に2進数指数部を記述する。

16進数浮動小数点数リテラルには2進数exponentの指定が必須だ。2進数exponentはeではなくpで記述する

\[0xApB = A \times 2^B\]

たとえば、0xabcp3は、\(\texttt{0xabc} \times 2^3\)となる。

すでにC言語には入っている。

これを執筆途中に、トークン列のパースの問題に苦しんだ。

本の虫: C/C++で0xf+1は合法なのに0xe+1はコンパイルエラーになるのはなんで?

[PDF] P0246R0:Contract Assert Support Merged Proposal

フォントにLinux Libertineを使っているためstやctやらがligatureで表示されてやや戸惑う。

それはともかく、内容はcontract機能の議論の結果の改定案。contract機能とは、関数にpreconditionやpostconditionとして期待される状態をチェックするコードを記述できる機能で、コンパイル時や実行時にチェックできる。いわば高級なコア言語でサポートされたassertと言える。

[PDF] P0247R0: Criteria for Contract Support

これもLinux Libertineフォントによるligatureがうっとおしい。

内容は、実行時contractチェックが満たすべき要件について。

[PDF] P0248R0: Concepts in C++17

C++17にConceptが入るべきだと主張する文書。

筆者は現行のコンセプトに満足していない。

[PDF] P0249R0: Input Devices For 2D Graphics

イベント処理と割り込み処理のためのライブラリの提案。

BoostのSignalを参照している。また、QtやAllegro(ゲームフレームワーク)も引き合いに出しているほか、jQueryの手軽さに最も影響を受けた設計だとしている。

確かに、ある程度のGUIライブラリでは常に再発明されている機能ではあるが、皆が納得する設計にするのは難しい気がする。

ドワンゴ広告

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

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

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

2016-06-27

C/C++で0xf+1は合法なのに0xe+1はコンパイルエラーになるのはなんで?

以下のコードはwell-formedである。

int x = 0xf+1 ;

以下のコードはill-formedである。

int x = 0xe+1 ;

何故か。

理由は、0xf+1は、0xf, +, 1という3つのトークンとして認識されるが、0xe+1は、一つのpp-numberトークンとして認識されるためだ。これには、浮動小数点数リテラルの指数の文法が影響している。

具体的には、"数値 e 符号"という組み合わせの文字列が引っかかる。

そのため、16進数のeに1を加えた数を表現する場合は、eと+の間に空白を開けなければならない

int x = 0xe +1 ;

-の場合も同様。

ドワンゴ広告

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

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

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

2016-06-24

C++標準化委員会の文書: P0230R0-P0239R0

[PDF] P0230R0: SG14 Games Dev/Low Latency/Financial Meeting Minutes 2015/10/14-2015/02/10

ゲーム開発、低レイテンシー、Financialな業界からC++に提案をする会議の議事録。

[PDF] P0231R0: Extending the Transactional Memory Technical Specification to Support Commit Actions

トランザクショナルメモリーにコミット操作を追加する提案。コミット処理の結果が反映されるのはは、処理が終わったあとまで遅延される。

[PDF] P0232R0: A Concurrency ToolKit for Structured Deferral/Optimistic Speculation

RCUとハザードポインターの特性を面白い喩え話を用いて説明している。

シュレディンガーは動物園を持っている。動物園には様々な動物がいる。シュレディンガーは動物たちを管理するメモリ内データベースを持っている。動物の出産や購入はデータベースへの追加、死亡や売却はデータベースへの削除として操作される。動物にはネズミや昆虫も含まれるので、このデータベースは頻繁に更新される。このデータベースに対して、動物の状態を調べるためにクエリーすることができる。ただし、猫に対するクエリーが常に多い状態になっている。これは、おそらく動物園内のネズミが点滴の情報を調べるためにクエリーしているのだろうとシュレディンガーは推測している。

この頻繁に更新され、一部の情報に高頻度のクエリーが飛ぶ利用特性を持つデータベースの実装として、グローバルロック、バケットごとのロック、RCU、ハザードポインターによる実装を比較して、それぞれの特性を示している。

また、文書ではリファレンスカウントもRCUやハザードポインターと似たような特性を持つだろうとしている。

将来的には、C++にRCUやハザードポインターのライブラリを追加したいそうだ。

[PDF] P0233R0: Hazard Pointers: Safe Reclamation for Optimistic Concurrency

ハザードポインターの解説文書。

[PDF] P0234R0: Towards Massive Parallelism(aka Heterogeneous Devices/Accelerators/GPGPU) support in C++

GPGPUのような大規模な並列処理を行う異なるアーキテクチャが混在したコンピューター環境をC++でサポートするにあたって、現状の提案の一覧や、既存の実装を解説している。

[PDF] P0235R0: A Packaging System for C++

ブリザードエンタテイメント社によるC++のためのパッケージシステムの提案。

パッケージシステムというのは、Pythonにおけるpipや、Rubyにおけるgemや、node.jsにおけるnpmのような仕組みのことだ。ソフトウェアのライブラリを手軽に配布、入手、インストール、使用できるためのシステムのことだ。

提案はディレクトリ構造にまで言及するなど、かなり具体的なものになっている。

筆者の意見では、パッケージシステムはOSが提供するものであって、言語がそれぞれ提供するのは間違っている気がする。しかし、成功しているプログラミング言語はだいたいパッケージシステムを持っているのも事実だ。

文書の提案する仕組みに基づいたパッケージシステムをClangに実装したものが、ブリザードのGitHubで公開されている

https://github.com/Blizzard/clang

[PDF] P0236R0: Khronos's OpenCL SYCL to support Heterogeneous Devices for C++

OpenCL SYCLとしてC++によるOpenCLのラッパー規格の紹介。

[PDF] P0237R0: On the standardization of fundamental bit manipulation utilities

unsigned charの配列をビット列のコンテナーとして扱えるラッパークラスの提案。ビットに対するリファレンス、ポインター、イテレーターを提供している。

これにより、例えば立っているビットの数を数えたいときは、イテレーターをstd::countに渡せばよい。std::countは、std::bit_iteratorに対して、アーキテクチャがサポートしていれば、popcntを呼び出すなどの最適化も行える。

P0238R0: Return type deduction and SFINAE

戻り値の型の型推定に失敗した場合、ハードエラーではなくSFINAEにする提案。

[PDF] P0239R0: valueless_by_exception

variantのcorrupted_by_exceptionをvalueless_by_exceptionに改名する提案。

ドワンゴ広告

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

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

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

2016-06-21

Chrome 51のV8の興味深いバグ

以下のコードを実行した結果を予想してみてほしい。


function foo()
{
    return typeof null === "undefined" ;
}

for ( var i = 0 ; i < 1000 ; ++i )
{
    console.log( foo() ) ;
}

typeof nullの結果は"object"なので、"undefined"と===で比較するとfalseになる。したがって、関数fooは必ずfalseを返すはずである。1000回実行しようと常にfalseを返す関数は常にfalseを返すはずである。

では実際に実行して確かめてみよう。

コンソールにコピペするのとは挙動が違うが、何度もクリックすると、なぜかtrueを返すようになる。おそらく、コンソールにコピペすると毎回JITが走るので、挙動が違うのだろう。

ちなみに、workaroundとしては、typeof null === undefinedとかtypeof null === "undefined" + ""などがあるらしい。

参考

Javascript developers, be warned about this crazy JIT bug in V8!

Issue 604033 - chromium - JIT compiler not preserving method behavior - Monorail

ドワンゴ広告

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

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

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

C++標準化委員会の文書: P0220R0-P0229R0

P0220R1: Adopt Library Fundamentals V1 TS Components for C++17 (R1)

Library Fundamentals V1の規格への取り込み

Library Fundamentasについては、先日の勉強会資料でまとめている。

EzoeRyou/cpp17lib-slide: 勉強会のスライド資料

P0221R1: Proposed wording for default comparisons, revision 3

デフォルトの比較演算子を生成する提案。

デフォルトの比較演算子の挙動については、すでに解説してきた。現在問題になっているのは、スライシングだ。例えば以下の例をみてみよう。


struct B { int state ; } ;
struct D : B { int state ; }

void f()
{
    B b ;
    D d ;
    b < d ; // well-formed
}

b<dはwell-formedになる。なぜならば、オブジェクトdは、基本クラスBのサブオブジェクトへのリファレンスに、暗黙に変換されるからだ。つまり、上のコードは以下の意味を持つ。


b < static_cast< const B & >( d ) ;

この提案では、このスライシングを禁止する破壊的変更を伴う提案をしている。

提案はこうだ。コピーコンストラクター、operator =、比較関数、不等号関数に、クラスBから派生しているクラスDにたいして、const B &への暗黙の変換を禁止する。

その結果、上記のような合法なC++14コードはill-formedになる。

P0222R0: Allowing Anonymous Structs as Return Values

無名クラス型を関数の戻り値の型として返せる提案。

struct { int x, int y }
f()
{ return { 1, 2 } ; }

void g()
{
    auto [x, y] = f() ;
}

P0223R0: Class Namespace

クラスのメンバーをクラスの定義外で定義するときに、名前空間のようにクラススコープの中に入れることで、冗長な記述を省略できる文法の提案。

以下のようなクラスがあるとする

template < typename CharType, typename Traits, typename Allocator >
class MyString
{
    MyString( ) ;
} ;

クラスのメンバーをクラスの定義内で定義すると、クラス定義が極めて長くなってしまい。クラスの概要がつかみにくくなる。そのため、メンバーはクラスの定義外で定義したい。問題は、現状では極めて冗長な記述をしなければならない。

template < typename CharType, typename Traits, typename Allocator >
MyString< CharType, Traits, Allocator >::MyString( )
{
// ...
}

まずテンプレートを書いた上で、クラス名をテンプレートまで指定したスコープ解決演算子を書かなければならない。しかも、これをメンバー全てに対して記述する必要がある。

このような記述をするのは面倒だし、間違いのもとであるし、リファクタリングの妨げにもなる。そこで、以下のようにクラススコープを名前空間スコープのように使える文法を提案している。


template < typename CharType, typename Traits, typename Allocator >
namespace class MyString
{
    // 何も書かなくていい
    MyString()
    {
    // ...
    }
}

virtualやstaticはクラス名前空間では書けない。これは、現状でもクラス定義外では書けないからだ。

struct S
{
    virtual void f() ;
} ;

// virtualは書けない
void S::f() { }

P0224: Implicit Return Type

関数の戻り値の型を、先行する宣言から保管する提案。

int f() ;

// well-formed
// 戻り値の型はint
auto f() ;

C++14では、宣言が異なるためオーバーロード関数であるとみなされるが、引数が同じなのでill-formedになる。関数の戻り値の型でだけの違いではオーバーロード可能ではないので、不必要な制限である。

この制限緩和は、無名クラスを関数の戻り値の型として使えるようにするためである。


struct { int x, int y ; } f() ;

// OK
auto f()
{
    return { 1, 2 } ;
}

P0225R0: Why I want Concepts, and why I want them sooner rather than later

Ville Voutilainenによる、なぜConceptをC++17に入れるべきかという文書。

筆者の意見では、現状のConceptはC++17に入れるべきではない。

[PDF] P0226R1: Mathematical Special Functions for C++17, v5

数学関数をC++に追加する提案。

[PDF] P0227R0: Weakening the iterator categories of some standard algorithms

std::sort, std::inplace_merge, std::stable_sortのイテレーター要求をRandom Access IteratorからForward Iteratorに変更する提案。

アルゴリズムの研究の発展により、Forward Iteratorでもこれらのアルゴリズムをそれなりの効率で実装できるため。

[PDF] P0228R0: A Proposal to Add Safe Integer Types to the Standard Library Technical Report

数学的に正しく振る舞うかチェックできるライブラリ、std::safe<T>の提案

C++の組み込みの整数型は、数学の法則に正しく従わない。コンピューターの内部の挙動に従うように設計されている。これにより、以下のような数学的に正しいコードは、実際には正しく動作しない。

int f( int x )
{
    return x*x ;
}

なぜならば、x*xの結果は、int型で表現できる範囲を超えてしまうかもしれないからだ。

std::safe<T>は、整数型Tを置き換えて使うことができる。もし、結果が数学的に正しくない場合、例外が投げられる。

int f( std::safe<int> x )
{
    return x*x ;
}

ライブラリ実装により、実行時のオーバーヘッドを極力発生させないことができる。例えば以下のコード

int f( std::safe<std::int8_t> x )
{
    reutrn x*x ;
}

Integral promotionにより、x*xの結果の型はint型であり、それをint型として返す。int型はint8_t型同士の乗算結果を必ず数学的に正しく表現できるので、この場合に実行時チェックは必要ない。ライブラリの実装によって、そのような実行時チェックは省ける。

また、アーキテクチャによっては、演算結果が数学的に正しいかどうかをチェックできる機能を提供していることがあり、ライブラリがそのような機能を活用すれば、実行時のパフォーマンス向上に繋がる。

このライブラリは、整数型のDrop-in replacementとして使えることを目的としているため、追加のオプションなどはない。既存の似たようなライブラリには、オーバーフローやアンダーフローを検出するかどうかや、整数の範囲などを細かく指定できるものがある。このライブラリでは、単純性のため、そのような設計はしない。

なかなかいいライブラリだ。

[PDF] P0229R0: SG5 Transactional Memory Meeting minutes 2015/11/02-2016/02/08

トランザクショナルメモリの会議の議事録。短い。

ドワンゴ広告

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

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

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

2016-06-17

GNU/Linux版Steamがクソすぎる件

正確に言うと、Steam Runtimeがクソすぎる。

GNU/Linux版のSteamは、ユーザースペースをごっそり自前で用意している。これはSteam Runtimeと呼ばれている。steamランチャーとsteamのゲームは、Steam Runtimeでchrootされた環境で実行される。

GNU/Linuxにおいてプログラムをバイナリブロブで配布して実行する場合、ユーザースペースをすべて同梱した上でchrootして実行するのは手っ取り早く互換性を保てる方法だ。問題は、バイナリはそのまま時代に遅れていくということだ。

現在のSteam Runtimeは、Ubuntu 12.04のユーザースペースのものである。実に4年も前のバイナリを使っているわけだ。

事の発端は、GNU/Linuxで動かすfactorioのFPSが安定しないということだった。私が構築したゲーム用のGNU/Linux環境は、GeForce GTX670MXを使っている。数世代前のGPUとはいえ、factorioを動かすのに問題があるとは思えない。

STEAM_RUNTIME=0することで、steamランタイムを無効化できるのだが、依存するi386版のshared libraryを全部インストールしても、まだvgui2_s.soが見つからず起動できない。これはsteam側のshared libraryのようだ。

ところで、factorioの公式サイトのアカウントは、factorioを購入済みのSteamアカウントと紐付けることで、factorioのSteam DRMのかかっていないバイナリを落とすことができる。このバイナリで試してみたところ、なんとFPSが60で安定した。

結論、GNU/Linux版Steamはクソ

2016-06-16

C++標準化委員会の文書: P0210R0-P0219R0

P0210R0: A light-weight, compact dynamic array

軽量な動的配列ライブラリの提案。

vectorはamortized constant timeを実現するために、実際に必要なサイズよりも大きなサイズのメモリを確保している。これはメモリ消費量を必要以上に上げる。

そのため、そのような挙動を行わない軽量な動的配列ライブラリの追加をする提案。

このライブラリは、insert, push_back, emplace{,_back}, eraseといったメンバー関数は提供しない。これらのメンバー関数は、汎用的なコンテナーの要件には定められておらず、特定のコンテナーがそのような操作を効率的に実装できる場合にのみ提供されるものだからだ。

reserveやcapacityもない。

resize(n)は要素数をnにする再確保を常に行う(ただしn == size()の場合を除く)

このライブラリの名前をどうするかは自転車小屋の議論が続いている。

このライブラリはスタックからメモリを確保するdynarrayとは別の目的なので、別のライブラリとなる。

用途は、new T[n]の置き換えだ。

new T[n]にくらべて何が優れているのか。

  • サイズも一緒に管理される
  • クラスによってメモリが所有されるので、オブジェクトの破棄時にメモリも解放される
  • 要素に対して個々に破棄することができる

P0211R0: Allocator-aware library wrappers for dynamic allocation

アロケーターを使ってnew/make_unique/make_shared相当のことができるライブラリ。

new/make_unique/make_sharedは、operator newから生のストレージを確保して、オブジェクトを構築する。アロケーターでやるには、placement newでオブジェクトの構築を自前でやらなければならない。

auto p = new T( args ... ) ;
delete p ;

相当のことを、メモリ確保をアロケーターに変えて行いたい。このライブラリを使えば、以下のように書くことができる。

auto p = std::allocate_new<T>( alloc, args ... ) ;
std::allocate_delete( alloc, p ) ;

同様に、make_uniqueやmake_sharedも、以下のように書ける。

auto p = std::allocate_unique<T>( args ... ) ;
auto p = std::allocate_shared<T>( args ... ) ;

便利なライブラリだ。

P0212R0: Wording for [[maybe_unused]] attribute.

名前が使われないことをヒントとして示す[[maybe_unused]]の文面案。以前は[[unused]]だったが、改名された。

たとえば、以下の例でassertはプリプロセッサーマクロで、リリースビルドで消えてしまうので、コンパイラーからはresultが使用されていないように見え、変数が使われていないという警告を出すかもしれない。これをmaybe_unusedで解消できる。

int f(int x, int y)
{
    [[maybe_unused]] int result = error_check( x, y ) ;

    assert( result ) ;

    return do_something( x, y ) ;
}

P0213R0: Reexamining the Performance of Memory-Allocation Strategies

Fundamentals TSで追加される新しいメモリアロケーターのベンチマークテスト。

monotonicとは、メモリ解放をしないアロケーター。アロケーターオブジェクトの破棄時に、一括してメモリを解放する。メモリの中にリスト構造か何かをつくって開いている領域を探してといったメモリ管理が不要になるシンプルなデザイン

multipoolとは、確保する小さなサイズごとにテーブルを作りそこから確保する戦略のアロケーター

P0214R0: Data-Parallel Vector Types & Operations

1

ベクトル型を利用したSIMDプログラミングの説明

これ事態は提案ではなく、このような機能をC++に入れるにあたって委2員に基礎的な知識を説明するための文書とみえるが、それでも結構詳しく定義されている。

P0215R0: A Civil-Time Library

時間を、absolute time/civil time/time zoneに分類した上で、civil timeをサポートするライブラリの提案。

absolute timeとは、起点時間からのカウント数で、例えばtime_tなどが相当する。C++ではすでにchronoライブラリがある。

civil timeとは、グレゴリオ暦による年月日時分秒のことだ。これをサポートするライブラリを提案している。

タイムゾーンやサマータイムは含まれない。うるう秒も予測不可能なので含まれない。オブジェクトは時間は常に妥当な日付を指す。妥当でない日付を指定すると、正規化が行われる。例えば、1月32日を指定すると、2月1日になる。これにより、呼び出し側は範囲チェックを行わずに単純に日付同士の演算ができる。秒以下の時間は管理しない。

月同士の演算をする場合には、日を1日に合わせなければならない。

P0126R0:A Time-Zone Library

タイムゾーンライブラリの提案。

P0217R0: Wording for structured bindings

P0217R1: Wording for structured bindings

P0217R2: Wording for structured bindings

構造化束縛(structured bindings)の文面案。

多値を受け取る文法の提案。

publicな直接の非staticデータメンバーを持つクラス、配列、std::tuple, std::pairから、それぞれの要素の型と値で変数を初期化できる文法となっている。

int a[3] = { 1, 2, 3 } ;
// int, int, int
auto [ a1, a2, a3 ] = a ;

struct B { int x, double y, std::string z} ;
B b = { 1, 2.0, "3"} ;
// int, double, std::string
auto [ b1, b2, b3 ] = b ;

std::tuple< int, int, int > c{ 1, 2, 3 } ;
// int, int, int
auto [ c1, c2, c3 ] = c ;

これにより、多値を返す関数を使うのが楽になる。

std::tuple< int, double, std::string > f()
{
    return { 1, 2.0, "3" } ;
}

int g()
{
    auto [ x, y, z] = f() ;
}

文法は以下の通り。

auto [ identifier-list ] brace-or-equal-initializer

identifier-listのそれぞれの変数名は、リファレンス型になる。初期化子の要素数と同じだけの識別子がなければならない。

int a[3] = { 1, 2, 3 } ;
// ill-formed.
auto [ a1, a2 ] = a ;

変数は必ずリファレンス型になる。これについては注意が必要だ。構造化束縛にリファレンス修飾子がない場合、初期化子がコピーされて、変数名はそのコピーへのリファレンスになる。リファレンス就職しがある場合、コピーされず、初期化子へのリファレンスになる。

例えば、以下のコードは

int a[3] = { 1, 2, 3 } ;
auto [a1, a2, a3 ] = a ;

以下のコードと同じ意味になる。

int a[3] = { 1, 2, 3 } ;
auto _a[3] = { a[0], a[1], a[2] } ;

auto & a1 = _a[0] ;
auto & a2 = _a[1] ;
auto & a3 = _a[2] ;

初期化子のコピーが作られ、変数はそのコピーへのリファレンスとなる。

以下のように書いた場合は、

int a[3] = { 1, 2, 3 } ;
auto & [a1, a2, a3 ] = a ;

以下のようになる。

int a[3] = { 1, 2, 3 } ;

auto & a1 = a[0] ;
auto & a2 = a[1] ;
auto & a3 = a[2] ;

初期化子はコピーされず、変数は初期化子のオブジェクトへのリファレンスとなる。

P0218R1: C++17 Filesystem

filesystemライブラリ。

P0219R0: Relative Paths

Filesystemライブラリに相対パスを計算する機能を追加する提案。

基本的には、。あるパスに対して、別のパスからの相対パスを計算する方法で、例えば/a/b/cに対して/a/bからの相対パスを計算するとcになる。

これだけならば、現行のFilesystemにも存在するのだが、Symlinkを追ってほしい、追わないでほしい。冗長な.や..を除去してほしい。ほしくないと言った衝突するユーザーの要望があり、かつどのように振る舞うか意見が別れるコーナーケースも多数あるため、Boostでは10年にわたって紛糾している問題。これに対する解決案。

ドワンゴ広告

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

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

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

2016-06-13

rootが無効化された環境でsshfsで権限の必要なファイルを変更する方法

sshfsは便利だ。リモートのファイルシステムをローカルのファイルシステムにマウントして扱うことができる。これにより、ローカル側のGUIのファイルシステムビューワーやエディターを使った温かみののある手作業による管理ができる。

ところで、Ubuntu Serverではrootが無効化されている。そのため、sshfs root@host:remote_path local_path はできない。代わりに、root権限が必要な操作はsudoを使って行う。しかし、sshfsではsudoができない。一体どうすればいいのだろうか。

試していないが、調べたところ、sftp_serverを指定する方法で行けるようだ。

まず、sudoのcredentialをキャッシュさせるために、ssh軽油でsudo -vを実行しておく。

そして、以下のようにsshfsでsftp-serverを指定する。

sshfs user@host:remote_path local_path -o sftp_server="/usr/bin/sudo /path-to/sftp-server"

おそらく行けるはずである。

2016-06-11

プログラミング言語基礎勉強会で発表した

プログラミング言語基礎勉強会 sponsored by @wantedly UIターン - connpass

上記の勉強会で発表してきた。今回の発表は、C++17の標準ライブラリを軽く説明した。

発表に使用したスライド資料はGitHubに上げてある。

EzoeRyou/cpp17lib-slide: 勉強会のスライド資料

発表者が全員濃かったので、他人の発表はあまりよくついていけなかった。

最後に地方と都内で仕事をすることについてというテーマでパネルディスカッションがあったが、いかんせんテーマが技術的なものではないので、極めて一般なこと、例えば地方は地代が安いだの、都内は通勤ラッシュがひどいだのといった、誰でも言えるようなことしか話せず、せっかくあの人間を前に集めて議論する内容としては非常にもったいなく感じた。一体何を考えて、あの人選に対してあんな陳腐なテーマを設定したのか謎だ。

その後懇親会に参加していろいろと雑談をしていた。結局、勉強会の価値はこういう雑談にあると思うので、懇親会がメインで発表は1,2時間だけという勉強会を開いてもいい気がした。

さて、勉強会の会場の近くには、そらよんというクライミングジムがあるので、勉強会のあとに行こうと思っていたが、どうしたことか、休日は21時までしかやっていないそうだ。ジムに行った時には、すでに閉まっていた。

ドワンゴ広告

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

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

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

2016-06-09

GNU/Linuxでゲーム

数年前のゲーミングラップトップを手に入れたので、Ubuntuを入れてゲーム用のGNU/Linux環境を構築しようとした。

GNU/Linuxにおけるゲーミング環境は年々良くなってきている。ValveのSteamOSのおかげでGNU/Linuxに対応する理由があるからだ。今回の目的は、持ち運べるfactorioゲーム環境を作ることだ。

もらったゲーミングラップトップは、G-tunes Nextgear i770というものだ。Core i5 3230MにGeForce GTX670MX、メモリが12GB、ストレージが1TBのSSHDだ。メモリが12GBなのは、4GBメモリを4枚さして16GB積むと何故か起動しないので1枚抜いたとのことだ。メモリが悪いのかマザボが悪いのかはわからない。

もらった時点でWindows 10が入っていたが、容赦なくUbuntu 16.04をインストールする。Windows殺すべし。慈悲はない。

さて、インストールが終わったら。apt-get install steamしてsteamをインストールする。そしてfactorioをインストール。

いざ起動してみると、何事もなく動いた。しかしFPSが30程度しかでない。

GPUドライバーが自由なソフトウェア実装であるNouveauになっていた。涙をのんで不自由なnvidia-driverをインストールする。

nvidiaの不自由なドライバーをインストールするとまともなFPSが出るようになった。しかし、画面にティアリングが発生する。どうやっても直らない。また、xrandrを使ったディスプレイの回転ができない。また、外部ディスプレイをつなぐと、nvidia-settingsからは、一枚の巨大なディスプレイを使っているかのように扱われる。解像度が高すぎるため、一度外部ディスプレイをつなぐと、ゲームのFPSが落ちる。

調べたところ、これはnVidiaのドライバー上の問題で、今のところどうしようもないということ。nVidia Fuck You!

nVidia Optimusは、IntelのGPUとハードウェア的にもかなり密に結合していて、nVidiaのGPUがIntelのフレームバッファに直接書き込むような形になっているそうだ。

また、factorioのゲームサーバーを立てるために、適当な安い中古のラップトップを買ってきた。Ubuntu Serverをいれて、とりあえずfactorioサーバーを建てられるようにはした。中古だが、ある程度こだわって選んでしまったので、果たしてサーバーにしていいものかどうか迷う。ただし、メモリが2GBしかないため、デスクトップ用途に使うとしてもUnityはいれられないだろうから、XbuntuとかLubuntuなどを入れることになると思う。

2016-05-26

近況

しばらくブログを書いていなかったが、いろいろと忙しかった。

今、とある仕事をしているのだが、その仕事内容がまことに精神的に辛い。その仕事内容はまだ公開できないのだが、とりあえず言えることとしては、1文字は1バイトではないということと、C++の標準ライブラリの正規表現ライブラリはUnicodeに対応していないということと、C++のlocaleは多言語対応の足かせになっているということだ。

さて、もうひとつ憂うべき事態としては、メインで使っているラップトップが故障した。去年の冬頃からどうにも挙動がおかしいと思っていたが、いよいよ使い物にならないほど挙動がおかしくなってきたので、memtest86+にかけてみたところ、なんと4900件ほどのエラーを出したところで動作を停止した。完全にメモリが壊れている。

さて、メモリが壊れているだけなら新しくメモリを買ってくればいいのだろうが、このラップトップはその他のスペックが2016年の基準から見て劣っている。ディスプレイの解像度は低いし、WiFiも5GHz帯が使えない。そろそろ新しいPCを買わねばなるまい。

とりあえず今は、別件で手に入った中古のゲーミングラップトップを使っている。これは、GNU/Linuxでゲーム環境を構築するために試しに買ってみたもので、nVidiaの不自由なGPUであるGTX 670Mが積んである。とりあえずUbuntu 16.04をインストールしてみたところ、自由なドライバーであるNouveauで動作した。そのままゲームも遊ぶことができたが、FPSが絶望的に出ない。仕方がなく、nvidia-driverを入れてみると、まともなFPSが出た。しかし、なぜかXrandRが動かないため、ディスプレイの回転ができない。

ところでゲームだが、最近は、ゲームといえばfactorioしかしていない。factorioは極めて高い中毒性のある自動化ゲームで、プログラマーならば必ずハマるだろう。先週は職場の同僚5人でマルチプレイをして、10時間かかってロケットを打ち上げた。

ところで、factorioには列車があるのだが、つながった同一の線路上に複数の列車を走らせる場合、信号を使わなければならない。この信号の使い方は、ゲーム内になんの説明もない。factorioの公式wikiには、OpenTTDと同じだからそちらを参照しろとしか書いてない。

これについて列車好きの同僚に教えを請うたところ、排他処理の起源は鉄道だったという事実を知らされた。なるほど、信号で区切られた区間に存在できる列車は一つだけという排他処理なのだ。これを理解したので、ようやく列車ネットワークが組めるようになった。なんのことはない。単なる排他処理である。ただし、一つの区間のアンロックと同時に、別の区間をロックするという特性がある。

現実の鉄道の排他制御では、区間の独占通行権を示すタブレットと呼ばれる物理的なトークンをやり取りしていたようだ。また、駅間が電信でつながっていて、タブレットを入れると、数値が駅間でインクリメント、デクリメントされ、釣り合いが取れた時だけタブレットが排出される装置まで存在したそうだ。それはセマフォではないか。

自宅でのfactorioのLANパーティもいつか開きたい。

2016-05-11

BitKeeperがオープンソース化された付記DVCSの歴史

BitKeeper

BitKeeperは最初の分散ソース管理システムである。今後はオープンソースのApache 2.0ライセンスとして提供される。

BitKeeperは高速で、エンタープライズレディな、分散ソースコード管理であり、大きなプロジェクトから小さなプロジェクトまでスケールする。

「最初の」という主張には語弊があるが、DVCSの歴史を考えると、あながち間違いでもない。

DVCS(分散バージョン管理システム)を最初に実装したのは、Sun WorkShop TeamWareである。

Sun WorkShop TeamWare - Wikipedia, the free encyclopedia

これは名前通り、Sun Microsystemsによって開発されたDVCSで、その主要な開発者として、Larry McVoyがいる。

Larry McVoy - Wikipedia, the free encyclopedia

Larry McVoyはその後独立してBitMover社を立ち上げ、BitKeeperというDVCSを開発する。BitKeeperは2005年までLinuxカーネルの開発に使われていた。

BitKeeper - Wikipedia, the free encyclopedia

Larry McVoyは、1990年台前半に、Sun MicrosystemsはSunOSをオープンソース化すべきであると主張していた。

The Sourceware Operating System Proposal

もし実現していれば、Linuxは今日の興隆を見なかったかもしれず、我々はSunOSを使っていたかもしれない。

そもそも、Sunの生い立ちたるや、IBMのメインフレームのような巨大で高価なコンピューターに対して、より小型で安価なUnixワークステーションを販売して成功したというものだ。とすれば、次は個人でも所有できる、さらに小型で、さらに安価なPCが流行するというのは、自身の成功体験から予測できたはずである。PC用のハードウェアを売る商売でSunは儲けられたはずであり、OSは自由にできたはずだ。我々はIBM PC互換機ではなく、Sun PC互換機を使っていて、WindowsやGNU/LinuxのかわりにSunOSを実行していたかもしれない(Sunのユーザースペースツールは使いづらいのでGNUはまだ残っていたかもしれない)。

にもかかわらず、Sunは近視眼的な判断によってSunOSをオープンソースにはしなかった。

さて、1993年にSunOSのオープンソース化を主張したLarry McVoyは、残念ながらその主張を実践しなかった。BitMover社を立ち上げてBitKeeperを開発したが、BitKeeperはプロプライエタリであった。2005年まで、Linuxカーネルの開発に使われていたが、無償版で提供されていない機能を実装したLinuxカーネル開発者であるAndrew Tridgellの行為に激怒してコミュニティへのBitKeeperの提供を辞めた。

そして、gitが生まれることになった。gitは圧倒的な速度でDVCS市場を独占していき、BitKeeperなど誰も相手にしなくなった。そして今日に至る。

残念ながら、BitKeeperのオープンソース化は、10年遅かった。もう手遅れだ。

2016-05-08

xkcd 463: 投票機

xkcd: Voting Machines

Premier Election Solutions社(以前はDiebold社)は、自社で開発したオハイオ州の選挙用の投票機が、McAfee アンチウイルスソフトウェアの問題によって障害を起こし責任を追求されている

「ちょっとまて」
「投票機にアンチウイルスソフトウェアだと? 間違ってるだろ」

「なんで? セキュリティは重量だろ?」
「もちろんそうだが、なんと言うべきか」

「保護者の教師の説明会で、教師が授業中は常にコンドームをつけているので安全だと保護者の説明したとしよう」

「なるほど、厳密に考えると、付けていないよりは安全・・・」
「間違ったやり方のヤツがいかに多いことか」

titleテキスト:これで俺はまた暗号会議を追い出された。いいだろ。いい例えなんだからさ。

2016-05-05

MITがSICPを教えなくなった理由

Programming by poking: why MIT stopped teaching SICP | posterior science

このNYC Lisp meetupの動画で、Gerry Sussmanに対する質問として、SussmanとAbelsonの古典、The Structure and Interpretation of Computer Programs(SICP)に基づく、伝説的な6.001講義をなぜMITはやめたのかと聞かれている。

Sussmanの回答としては、SussmanとHal Abelsonは1980年代から延々と教え続けるに嫌気が差し、1997年に、学部長の事務所に行って、「俺らはやめる。後どうするからは勝手に考えろ」と宣言した。より重要なこととしては、SICPのカリキュラムは、今日のエンジニアリングに求められるエンジニアを育てることができないからである。1980年代と1990年代には、エンジニアは複雑なシステムを組むのに、単純で十分に理解されている部品を組み合わせた。SICPの目的は、そのようなシステムを理解するための抽象的な言語を提供することだ。

今日では、状況が変わっている。今のエンジニアは、自分が完全に理解していない複雑なハードウェアのためのコードを日常的に書いている(そして、大抵の場合、企業秘密により完全に理解するのは不可能である)。ソフトウェアでも状況は同じだ。プログラミング環境は、多大な機能を提供する巨大なライブラリ群の集合として存在している。Sussmanの今日の生徒は、その時間の大半を、ライブラリのマニュアルを読み、どのように組み合わせれば目的が達成できるのかを把握することに費やしている。Sussman曰く、今日のプログラミングは、「より科学に近い。ライブラリを持ち寄って、つっつき回すのだ。プログラムを書くには、突っつき回して、どのように動作するかを観察する。そして、「目的を達成するために改造できるか」と考えるのだ」。SICPの「合成による解析」という物の見方である、小さな、単純な部品を組み合わせて大きなシステムを作るということは、もはや今日の状況にそぐわなくなった。今や、我々のプログラミングはつっつき回すことで行われている。

なぜPythonを選んだかということについて、Sussmanは、"late binding"に決定したと冗談を飛ばした。Pythonには大量のライブラリがあり、教育者の様々な実習に使いやすい(たとえば、ロボットを制御するソフトウェアを書くなど)

Sussmanは、SICPカリキュラムは現在のカリキュラムより洗練されていると考えているものの、正しいカリキュラムのあり方についてはまだ答えが出ていないという。

たしかに、今のプログラマーは、ハードウェアの仕様書を元にを直接操作するコードは書かないし、OSを実装していないし、コンパイラーも実装していないし、古典的なアルゴリズムやデータ構造さえ自分の手で書く必要がなくなっている。ライブラリが発達してその必要がなくなったためでもあり、また個々の機能があまりにも高度になりすぎて、到底一個の人間の手に負える作業量ではなくなったということもある。

不自由なハードウェア、ソフトウェアが蔓延してその詳細がわからなくなり、また自由なソフトウェアであっても、その内容が複雑になりすぎ、一つ一つ完全に理解するには時間が足りなすぎる。

何にせよ、平均的なプログラマーが実現できる機能は昔よりはるかに複雑になっていることは確かだ。

2016-05-02

Craig WrightがSatoshi Nakamotoだとする証明はない

WiredとGizmodeにより、Craig Wrightなる人物がbitcoinのオリジナルの設計者にして最初の実装者、Satoshi Nakamotoであると報じている。

Bitcoin’s Creator Satoshi Nakamoto Is Probably This Unknown Australian Genius | WIRED

This Australian Says He and His Dead Friend Invented Bitcoin

bitcoinのオリジナルの設計者にして最初の実装者は、当時Satoshi Nakamotoと名乗っていた。一見、日本人のような名前であるが、彼は自らのことを多く語らず、またできるだけ身元の特定に繋がる痕跡は隠していた。当然、国籍はおろか、個人かどうかすらもわからない。彼の書いたコードのコメントはすべて英語で、非英語ネイティブにありがちな文法ミスはみあたらない。また、彼の当時のフォーラムへの500件ほどの投稿を調べると、GMTで5時から11時にかけてほとんど投稿がみられないので、Satoshi Nakamotoはこの間には睡眠をとっていたのではないかと推測されている。この時間帯はJSTに生きる人間の一般的な睡眠の時間帯とは異なっている。

Satoshi Nakamotoの正体には様々な憶測が飛び交い、日本人である京大の望月新一教授や、たまたまSatoshi Nakamotoという名前であるアメリカ人がメディアによって正体であると噂されたりもした。

さて、このCraig Wrightなる人物は、自ら作者であると名乗りでた人物であるが、発言がまるででたらめであると言われている。Bathurst大学で博士号を取ったと自称しているが、大学は否定した。また、彼のブログによる証明というのも、bitcoinの公になっているハッシュ値にすぎず、ブログで提示しているbase64っぽい文字列に至っては、デコードすると単なる平文のブログ本文にもある文字列になるというお粗末さである。

Jean-Paul Sartre, Signing and Significance - Dr. Craig Wright BlogDr. Craig Wright Blog

そもそも最も簡単な照明である、当時Satoshi Nakamotoが使っていた最初のPGP秘密鍵で署名した、「Craig WrightはSatoshi Nakamotoである」というメッセージを出して、少なくともSatoshi Nakamotoが当時使っていたPGP秘密鍵は所有しているという証明をしていない。

よくある詐欺師であり、目的はSatoshi Nakamotoを自称して出資を引き出すためではないかと言われている。

と、ここまでならよくある話だが、何故か今回の話にはおまけがある。bitcoin開発者であり、当時のSatoshi Nakamotoとも対話していて、暗号理論も理解していて証明の真贋も見分けることができるであろうGavinが、Craig Wrightは確かにSatoshi Nakamotoであるとコメントしているのだ。

このため、bitcoin開発者はGavinのコンピューターがハックされた可能性を考慮して、Gavinからコミット権限を取り消す措置をしている。まだGavinからハックされたというコメントはない。

Peter Todd on Twitter: "FYI, @gavinandresen's commit access just got removed - Core team members are concerned that he may have been hacked. https://t.co/7re7z16TeR"

ちなみに、Gavinがハックされたとか詐欺に加担しているのであれば、最も簡単に利益を上げる方法が取られていない。すなわち、「私はサトシだ。今から私の持っているbitcoinを全部売る」と宣言して、bitcoin取引市場に多大な売り注文がやってくると誤認させ、市場を混乱に陥れてbitcoinの価値を下げたところでbitcoinを買い占め、市場が落ち着いた後で売り払うという方法だ。

Satoshi Nakamotoが今更名乗りでても、それ自体は別に興味深くはない。本人の名声とか政治的迫害を別にすれば、Satoshi Nakamotoでなければできない設計や実装は今さら存在しない。ただし、Satoshi Nakamotoのウォレットに入っているbitcoinは莫大であり、市場に放出された場合、大混乱に陥るだろう。

こういった事情により、一概に詐欺とも言いがたい事態となっている。

コンピューター科学のアカデミック業界の残念な現状

mhoye on Twitter: "Extremely angry with the state of academic CS research right now. (1/n)"

MozillaでFirefoxのエンジニアリングコミュニティマネージャーであるMike Hoyeが、コンピューター科学におけるアカデミック研究の残念な現状に激怒している。

コンピューター科学のアカデミック研究の現状に激怒している。

MozillaがBugzillaを始めとした多数の情報を公開した結果として、多くの研究論文が書かれている。

我々はそのような研究には注目している。論文はじっくり読んでいるし、研究結果にしたがって今後の方向性も決めている。

しかし、我々は常に変化する世界に生きている。そのため、我々はデータをもとに結果を再検証して、仮定が正しいことを確認する。

ここで我々が行いたいことは、我々はある意思決定をある論文Xの結果をもとに行いたいのだが、その結果は最新のデータでも妥当であろうか? と言えることだ。

まともな世界では、そのような検証は以下の3ステップで行えるはずだ。

  • 論文著者のバージョン管理システムのレポジトリをクローン
  • 論文著者のプログラムを最新のデータに対して適用
  • 新しく生成されたグラフを見る

データはまだ仮説を支持するものであるか? 素晴らしい。この方向で進めよう。結果が変わった? 何故なのか考えてみよう。いずれにせよ。全員が満足する結果となる。

しかし、これは実現しない。なぜならば、コンピューター科学の研究者はコードもデータも公開しないからだ。奴らはLatexで整形したWordドキュメントをペイウォールに阻まれたPDFとして公開する。

奴らときたら、科学の原則である、「妥当性」とか「再現性」とか、中でも最も基本的な原則、「現実に即しているか」などは、クソ喰らえの姿勢だ。

人間が知識や学習結果を共有しないことの時代遅れがいかに時代遅れであるかを見てみようか。

ギリシア火薬の製法は失われた。ダマスカス鋼の製法は失われた。アンティキティラ島の機械は紀元前200年に失われ、同等の精度を持つ時計を再び作るには1500年代まで待たねばならなかった。

いいか。よく聞け。お前の未公開のコードと、お前の未検証のデータと、お前のペイウォールに阻まれた博士論文は、この輝かしい因習の一部であるのだぞ。

お前の目的とやらが、学士を得て卒業することなら、まあいいだろうよ。大抵の人間が望むことだ。だが、院にまで来てやることか?

お前の業績により世界をよりよい方向にインクリメントするためには、世界はお前の業績を読めなければならないのだぞ。

俺は結果の報告書など読みたくない。そんなのは、ワインを注文しているのに、赤っぽい色の液体について報じた新聞記事の切り抜きをFAXしてよこされるのと同じだ。

検証可能なデータと動くコードなしには、お前のコンピューター科学の博士論文の命題とやらは命題ではない。それは単に命題が存在するかもしれないという未検証の主張に過ぎない。

まとめると、俺は大変に失望している。もっと言うべきことはあるが、俺はこれから長年の研究とツールを再現するためのコードを書かねばならないのだ。

2016-05-01

超会議2016でドワンゴの運営スタッフとして焼きそばを焼いた感想

「江添さん、超会議で焼きそばを焼きませんか?」

恰幅のいい同僚が話しかけてきた。この男はドワンゴの料理研究部の部長である。

ドワンゴには福利厚生として同好会の設立を会社に申請でき、受理された同好会には部費も支給される。最も、会社が経費として出す金なので、いろいろと制約がある。例えば、飲食費用には使えない。料理研究部は調理器具や職場近くのキッチンのレンタルなどに部費を使っている。

「焼きそば? 少し前に話題になったアレをネタにするつもりですかな。しかし、もう旬は過ぎてしまったのではありませんかな」

「アレ」というのは他でもない。一時期、ドワンゴから退職が相次いだ時期があり、その時のある退職者に対して、退職理由がよくわからないとドワンゴの川上宣夫会長と伊藤直也氏がスシをつまみながらのインタビュー記事で書かれたことを受けて、元ドワンゴ社員のkuzuhaが、言及されている退職者というのは自分であろうと名乗りでて書いたブログ記事が発端で、一時期ドワンゴと焼きそばが炎上したアレだ。

僕は初回のニコニコ超会議が開催される前にこの話を聞いてさっさと退職したので実際には経験をしていませんが、ニコニコ超会議には社員が強制的に動員され、列の整理や焼きそば屋台などに従事させられました。正直に言ってソフトウェアエンジニアとして雇用した人間に焼きそばを焼かせるのは雇用契約違反なのではないかと思います。立て付けとしてはお客様の文化に触れる、本気で楽しんでくださっているお客様の姿をじかに見られる他にない機会であるという言い分です。

ドワンゴは大量退職に関する印象操作をやめろ - hiroki-uemuraのブログ

この炎上以降、Googleの検索欄に"ドワンゴ"と入力すると"焼きそば"がサジェストされ、"ドワンゴ 焼きそば"でググると、ドワンゴに対してネガティブな情報ばかりヒットするようになってしまった。

私がドワンゴに入社する以前の話であり、この当時の状況は人からの伝聞と風のうわさでしかわからないのだが、kuzuhaの記事には間違いがある。初代超会議のフードコートでドワンゴのエンジニアに課された料理は「焼きそば」ではなく「あんかけチャーハン」だったとのことだ。当時、ドワンゴの社内チャットでは、「会社は焼きそばではなくチャーハンですと訂正するIRを出すべきでは」などと無責任な冗談が飛んでいた。

ところでチャーハンといえば、ドワンゴには非公認のチャーハン部があり、名にし負うハンドルネームがチャーハンである部長の強力なリーダーシップのもと、毎週一度近所の中華料理屋に臨んで大盛りのチャーハンを食らうという活動をしているが、同部ではチャーハンの重量以外に、食べづらさ、胃もたれなどの完食への困難性への度合いを総合的に評価した、実質係数なる独自用語が飛び交っていて、1.2kgのチャーハンを完食した部員は尊敬され、中でも、1.2kg完食者の「来る者拒まず、去る者追う」という発言は名言としてSlack上で永久にPinされている。

それはさておき、同僚の話によれば、転んでも無料では起きない精神をもってこの炎上ネタを利用し、来る超会議2016ではエンジニアがフードコートで焼きそばを焼く企画があるという。そのために、フードコートに自らの意思でアサインされるエンジニアを募集しているという。

「いえね、やはりこういう企画は表に名前の出ている有名なエンジニアさんを採用したほうがいいと思いますし、焼きそばを焼くのも悪くありませんよ。なんでしたらフルタイムではなくパートタイムでも構いません。基本的には他のブースにアサインして途中2時間ほど抜けて焼きそばを焼くという形でのエンジニアの参加も予定しています。焼きそばを焼いていただけるのでしたら、特別に企画にかけあって希望のブースに優先的にアサインされるような配慮も致します。そしてですね。あまり言いふらしてほしくないのですが、休憩時間を通常1時間のところ、焼きそばブースは特別に2時間取ろうと予定しております」

「ほう、2時間?」

「はい、といいますのも。焼きそばを焼く鉄板はすごく熱くなるので、普段から仕事で焼きそばを焼く本職の人でも3,4時間ぐらいが限界だとか。我々素人に換算しますと、もっと短い時間で限界になるでしょうし、休憩時間も多めに必要なわけです」

巨体に似合わぬ高い声で、同僚は官僚的な勧誘文句を連発する。実際、この同僚はこの手の官僚的な細かい規則主義、手続き主義を極めて得意とする男であり、その才能は公私において遺憾なく発揮されている。今回のこの企画に割り当てられたのもその才能故だろう。この企画を遂行するにあたっては、外部のイベント設営会社、イベント運営会社、ましてや食品を扱うので消防、保健所などとの極めて役所的で煩雑なやり取りをしなければならないだろう。

そういえば、この同僚はドワンゴ社員の労働組合の代表でもあった。以前、ドワンゴとドワンゴ社員の労働組合との労使協定が危うく締結できなくなる危機があった。理由は会社と労働者との待遇の提示と要求の不一致という高尚なものではなく、単にドワンゴ社員がズボラで会社と労使協定を締結する際の労働組合の代表を選ぶ投票に参加しないというだけであった。

雇用者と被雇用者の労働組合との間で労使協定が締結できない場合、何が起こるのかというと、労働基準法の定める範囲外の労働が違法になる。読者の中には、労働基準法の範囲内の労働しか認められないのはとても良いことではないかと考える人もいるだろう。もしそう考えているとしたら、読者は労働基準法を読んだことがないだろうから、今すぐ読むべきである。

労使協定を結ばない労働基準法の定める範囲内の労働というのは、極めて制限が強い。一日の労働時間は最大8時間であり、1週に最大40時間である。残業や休日労働は認められない。緊急時のみ認められるが、事後に行政に残業や休日労働を行った旨を届け出なければならない。これは、夜中や休日にドワンゴの提供するサービスに障害が発生した場合でも、次の営業日の営業時間になるまで修正作業が行えないことを意味する。例えばニコニコ動画でそのような障害対応を行った場合、ユーザーが可及的速やかに離れてしまうだろう。Webサービスにおいては、障害はいつ何時でも即座に対応しなければ死活問題なのだ。また、定時が存在するようになり、全労働者は同じ時間に一斉に出社し、同じ時間帯に一斉に休憩を取り、同じ時間に一斉に退社することになる。

もちろん裁量労働制はなくなる。ドワンゴの特に極端な社員の一日では、早朝に就寝し、昼過ぎに起床して出社し、出社で疲れたので仕事など当然できるわけもなく、まず飯を食いに行き、16時から朝会と称する部署のメンバーが集まっての活動報告を行い、18時頃からそろそろ昼になってきたから本腰を入れて仕事するかなどとうそぶきながらようやく活動を始める。そんな裁量労働制もなくなってしまう。

事態は極めて逼迫しているというのにドワンゴの労働者はどこ吹く風といった時、この同僚は各個撃破の地道な根回しを行って何とか票を集めて従業員代表になり、労使協定を締結させ、ドワンゴの裁量労働制を維持し、ひいてはドワンゴの労働基準法違反を回避させた男である。

かかる経緯で、筆者は超会議2016で焼きそばを焼くことになった。

なお、フタを開けてみると、この同僚の根回しが強力すぎたためか、はた、祭り好きの人間が多かったのか、焼きそばブースへのアサインを希望するドワンゴのエンジニアが応募多数に付きお断りする事態が発生していたらしい。

そもそも元をたどればこの焼きそば企画の発端は、kuzuhaの上記のブログ記事なのだが、この企画を社内で提案した人間は、実は途中で退職しているそうだ。そのため、同僚は退職者の企画を引き継いだ形になる。この規格の経緯を考えるとなんとも形容しがたい何らかの違和感のある不思議な気分になる。

今回の焼きそば企画では、"ドワンゴ 焼きそば"でググった時の検索結果のネガティブな結果を変えようという意図もあるらしい。なんと、物理SEOというわけか。当日はよりシュールなネタとして、特別なスタッフカードを装着することとなった。そのスタッフカードには、顔写真と名前と「私は自らの希望で焼きそば担当になりました。」とそらぞらしく書いてある。

この同僚の苦労はともかく、焼きそばブースにアサインされただけのエンジニアは、前日のリハーサルと当日の本番に労働するだけであった。

リハーサルの日は雨が降っていた。焼きそばブースの現地でのリハーサル開始として指定された時刻は16時であった。これは朝が弱いエンジニアへの配慮である。というのは方便で、実際には、消防と保健所の検査が終わるまで鉄板に火を入れることができず、調理実習ができないためである。筆者は大幅に寝坊をして現地の到着が15時半になったが、ブースには誰もいなかった。しかたがないのでニコニコ頂神社の壁を登っていた。グレードは8級以下に感じた。今回のビレイは派遣されたプロが担当するそうだ。焼きそばもプロが派遣されているという。今回、ボードゲームがないのは残念だ。

16時になったのでフードコートに戻ってみるが、やはり誰もいない。まさかと思って控室に行くと、全員いた。エンジニア達は爪楊枝にN高のシールをはって食べ物に指す旗を作るという地味な単調作業をしていた。エンジニアの人件費を考えると、この旗は相当高いに違いない。しかし不思議だ。プログラマーにとって単調作業は忌避すべきものであるが、我々はたまに単調作業の魅力に惹かれてしまう。これは一体どういうわけだろう。

その後、待てども待てども検査はなかなか始まらず、とうとう前日は調理実習が何もできないまま解散した。筆者はその足で、海浜幕張にあるPEKIPEKIというクライミングジムに行った。去年登れなかった課題がまだ残っており、しかも登ることができた。また、クライミングマシーンのExtream設定をクリアした。一年たって上達を感じる。

さて、当日、ようやく鉄板に火を入れることができるようになったので、焼きそばのプロの実演を見た。出来上がったカレー焼きそばを食べたが・・・とても粉っぽくて食べられたものではない。どうやらレシピに記載されたカレー粉の分量が多すぎるようだ。調整してまともな味にした。

今回の焼きそばには、ドワンゴのニコニコ動画チームのK1の設計したカレー焼きそば、ニコニコ静画チームのまさらっきが設計したイカスミ焼きそば、ニコニコ生放送チームのK2が設計したピザ焼きそばの3種類の焼きそばが用意されている。カレー焼きそばには、カレー粉が入っている。イカスミ焼きそばにはイカスミが入っている。ピザ焼きそばは肉の代わりにシーフードミックスで、トマトピューレを入れた上で、上からパルメザンチーズをかける。

今回、焼きそばブースにアサインされたレシピの設計者はまさらっきだけだ。試食の結果、イカスミ焼きそばが一番美味しかった。まさらっきはグルメをわかっているものと見える。問題は、唇と歯が黒くなってしまうバグが発覚したことだ。

ピザ焼きそばは、極めて粘着質で、パック詰めが難しいという運用上の問題が発覚した。

今回は、オープンソース焼きそばということで、レシピがGitHubで公開されている。ライセンスは煮るなり焼くなり好きにしろライセンスだそうだ。

dwango/yakisoba-sauce: Open source recipe of yakisoba-sauces for chokaigi 2016

とはいえ、物理的存在に自由不自由の区別は存在しないのだが。

売上としては、カレー焼きそばが最も多く売れ、イカスミ焼きそばが最も売れないという結果になった。

価格は800円だそうだ。焼きそばにしてはやたらと高い。イベント価格とは言えあまりの高い値段に罪悪感がある。とはいえ、後述する焼きそばを焼く労働の辛さを考えると、妥当ではないかと思えるようになってしまった。

さて、肝心の焼きそばを焼く労働の苛酷さについて述べる前に、もうひとつ書いておくことがある。エンジニアとしての演出だ。

せっかくだから鉄板の温度を計測して表示してはどうか、また、焼きそばのそれぞれの売上もカウントして表示してはどうかと、アサインされたエンジニアの一人が主張し、実際にそのための装置を作った。その実装が笑えるほど面白かったので紹介する。

まず、鉄板の温度の計測方法であるが、接触するセンサーは衛生上問題があるので使えない。赤外線のセンサーを用いることにした。そのため、中国製の赤外線センサーを買った。さて、赤外線センサーを分解して、中身だけ取り出し、温度情報を取得すればいい。ドワンゴの電子工作部の部員に依頼したが、温度情報を取り出す方法がわからない。唯一測定できる信号線は、液晶ディスプレイにつながっているもので、0.1mm以下の線が何本もあり、これを手作業でハンダ付けして一本一本取り出すのは極めて困難なのでやりたくないとのことであった。したがって、計測装置は極めて笑える富豪的な実装になった。

すなわち、温度を表示する液晶ディスプレイをカメラで撮影し、画像認識して温度を得るというものである。このためにカメラとRaspberry Piが用いられた。

しかし、残念ながら、当日の表示に温度表示がつくことはなかった。理由はわからないが、鉄板の温度が60度ぐらいに表示されてしまうのだ。表面に塗った油が煙を出すような状態でその温度は明らかにおかしい。この赤外線温度計は確か500度まで対応していたはずだがなぜだろうか。あるエンジニアは、鉄板の温度が高すぎてオーバーフローしたのではないかという説を出した。温度が9bitで管理されていた場合、512度以上になるとオーバーフローする。しかし、9bitなどという中途半端なビット数で管理するだろうか。「いや、ビット数を増やすと信号線が増えて回路も増えて手間もコストもかかる。組み込みならよくあること」と言っていた。

しかし、冷静に考えると鉄が600度になると赤黒く発光するはずであり、そんなに高温になるだろうか。

「もう乱数でも表示しておけばいいんじゃないか。どうせ気づかないぜ」などという冗談まで飛び出した。

さて、焼きそばの種類ごとの売上をカウントするボタンはうまく機能した。ボタンは、100円ショップで売っていそうな透明なタッパーを容器として、ボタンを3つ取り付けてある。容器の中には基板が入っていた。

「それはArduinoかな」
「ラズパイですよ」
「ラズパイ? たかがボタン3つの制御のために?」
「何言ってるんですか。富豪的プログラミングって言葉知ってます?」
「たかがボタン3つの制御のために一昔前のスマフォに組み込まれていたARM CPUと100MB以上のメモリが必要なのか?」
「今はIoTの時代ですよ。ボタンもsshdぐらいお話できないと不便ですよ」
「ボタンの制御にH.264をデコードできるGPUを詰んだMinecraftもインストールされているコンピューターが必要なのか?」
「それは・・・うーむ」

3つボタンの容器は2つあったので、このシステムにはRaspberry Piが3つも使用されていることになる。なお、実際にはログの集計に使うソフトウェアにメモリが16GB以上必要でMacBookまで持ち出すはめになった。富豪的プログラミングにもほどがある。

さて、そろそろ肝心の焼きそばを焼く労働について述べねばなるまい。当日のフードコート奥には、3枚の鉄板が設置された。一枚の鉄板で一度に40人前(パック詰めのときには、目分量でかなり多めに詰めたので、実際にはもっと少ないだろうが)の焼きそばを焼くことができる。カレー焼きそばの材料は、豚こま切れ肉1.5kg、キャベツ1.5kg、カレー粉適量、もやし2kg、麺4kgである。

このスケールでは、焼きそばの調理は重労働である。まず鉄板が熱い。読者は何をいまさらと思われるかもしれないが、やはり鉄板は熱い。焼きそばをかき混ぜるには、ヘラを動かすために鉄板の上に手をかざさなければならず、極めてあつい。そして材料は重い。まさか焼きそばの麺があれほど重たいものだとは思わなかった。

そして、焼きそばは休みなしにひっきりなしに焼かねばならなかった。というのも、作ったそばから売れて在庫がなくなっていくからだ。会場には焼きそばの保温器が設営されていたが、果たして必要だったのだろうか。

フードコートのボトルネックは注文を聞いて現金をやり取りする部分で、そのためにスループットが上がらず行列ができてしまうのだが、たとえそのボトルネックが解消されたところで、焼きそばの生産速度はすでに追いつくのも難しいほど限界であった。

また、パック詰めも焼きそばの調理ほど肉体疲労はないが、地味に手間のかかる面倒な作業であった。トングで焼きそばを掴んで、パックにいれ、輪ゴムをかけなければならないのだ。カレー焼きそばとイカスミ焼きそばは普通の焼きそばなのだが、ピザ焼きそばはトマトピューレを入れるため、やたらと粘着質な出来上がりになってしまい、トングで掴んでパックに移す作業が極めて面倒であった。

興味深いのは、フードコートの売れ行きである。行列ができていると呼び込まずともどんどん人が来るのだが、行列ができていないとまるで人が来ない。これは一体どういう群集心理が働いているのだろうか。先の単調作業への魅力といい、なんだかやたらと心理学への興味が深まる。

こうして、過酷な2日間に渡る焼きそばブースでの業務が終わった。始まる前は、「ところで、ちゃんと焼きそばは食べられるんでしょうね? 目の前で焼いているのに食べられないとか拷問ですよ」などと言っていたエンジニアは、あまり焼きそばを食べていた気配がない。

超会議のフードコートで焼きそばを売って実感したこととしては、希望せずに強制的にフードコートに配属されたら、少なくとも転職を考えるぐらい疲労するということだ。

だいぶ披露したとはいえ、ボルダリングは別腹だ。超会議2日目の後は海浜幕張のクライミングジム、PEKIPKEIには行った。前日リハーサルで落とせなかった課題が落とせたので満足して帰った。来年もまたこよう。

写真

ドワンゴ広告

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

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

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

2016-04-23

Ubuntu 16.04にアップグレードした

Ubuntu 16.04にアップグレードした。今回はかなり問題だらけだった。

まず、アップグレードが正常に終わらなかった。古いパッケージを削除する途中で、hicolor-icon-themeのインストールスクリプトが正常に終了しなかったとのメッセージが表示された。同じ問題が報告されているようだ。

Bug #1571139 “package hicolor-icon-theme 0.15-0ubuntu1 failed to...” : Bugs : hicolor-icon-theme package : Ubuntu

他にも、Localesのインストールスクリプトが正常に終了しなかったというメッセージも表示された。

そして、"E: mkinitramfs failure cpio 141 gzip 1"と表示されたまま画面が完全に固まってしまった。入力を一切受け付けず、端末に切り替えることもできなければ、SysRqもきかない。30分ほど待っても何も起こらないので、仕方なく電源を落とすことにした。

さて、ブートしてみるとGrub2のシェルが表示される。さて、ブートローダーの問題なのだろうか。

とりあえずUbuntu 16.04のISOを落としてブータブルUSBメモリを作成してブートしてみる。あっさりとブートできた。どうやらLive USBではなくて普通にUSBメモリのブートローダーだけを使ってラップトップにインストールしたUbuntuが起動したらしい。ログイン画面は16.04になっている。

それではgrubのインストールだけやり直せばいいのだろうか。とりあえず様子を見てみようとGUIのアップデートプログラムを起動してみたところ、何やら大量のパッケージを確認もなくアップデートして再起動を促された。はて、まだ終わっていなかったのだろうか。

再起動してみるとこれまた普通に使える。古いパッケージ、特に古いLinuxカーネルのパッケージが大量に残っているので、apt-get autoremoveで除去した。

さて、今回はGCCが5.3、Clangが3.8になっている。ただし、libc++とlibc++abiに問題があるようだ。まず、libc++abi-devは、/usr/include/libcxxabi/下にインストールされるのだが、このディレクトリへのパスが通っていない。しかし、なぜか/usr/include/c++/v1/cxxabi.hだけは存在するので/usr/include/libcxxabi/__cxxabi_config.hを#includeしようとして見つからないエラーが出る。

仕方がないので、Clangのオプションに-I/usr/include/libcxxabi/を渡してパスを追加した。

もうひとつ、どうもlibc++ 3.7のstd::stringの実装に問題があるようだ。/usr/include/c++/v1/stringの1326-1331行は以下のようになっているが、

    _LIBCPP_INLINE_VISIBILITY explicit basic_string(const allocator_type& __a)
#if _LIBCPP_STD_VER <= 14
        _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value);
#else
        _NOEXCEPT;
#endif

1936-1940行は以下のようになっており


template <class _CharT, class _Traits, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
basic_string<_CharT, _Traits, _Allocator>::basic_string(const allocator_type& __a)
    : __r_(__a)
{

basic_stringのコンストラクターの宣言にあったnoexceptが定義には存在せず、宣言の不一致でエラーになる。

パッケージ管理されているファイルを弄りたくはないので、とりあえず別のディレクトリにコピーしてコードを修正し、そのディレクトリへのパスを追加することで修正した。次のUbuntuのアップグレードの時まで忘れずに覚えていなければならない。

変更自体は、noexceptを付けるだけだ。


~/Documents/src/cpp/include$ diff -u /usr/include/c++/v1/string ./string 
--- /usr/include/c++/v1/string 2015-09-06 19:01:16.000000000 +0900
+++ ./string 2016-04-23 18:59:51.848010811 +0900
@@ -1936,6 +1936,11 @@
 template <class _CharT, class _Traits, class _Allocator>
 inline _LIBCPP_INLINE_VISIBILITY
 basic_string<_CharT, _Traits, _Allocator>::basic_string(const allocator_type& __a)
+#if _LIBCPP_STD_VER <= 14
+        _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value)
+#else
+        _NOEXCEPT
+#endif
     : __r_(__a)
 {
 #if _LIBCPP_DEBUG_LEVEL >= 2

2016-04-20

江添ボドゲ会@5月

以下の通り5月7日に自宅でボドゲ会を開催するのでお集まりください。

江添ボドゲ会@5月 - connpass

今回もカレーでも作ろうと思う。

curl | bashをサーバーサイドで判定する方法

Detecting the use of "curl | bash" server side | Application Security

ソフトウェアをインストールするとき、シェルスクリプトを実行するのはよくあることだ。しかし、そのシェルスクリプトが他人のリモートサーバーでホストされていた場合、curl | bashするのは危険だ。まともなユーザーは、curl | bashする前に、まず中身を確認して、悪意がないことを確かめるものだ。

しかしもし、サーバー側がwgetやcurlといったツールとブラウザーを判定して、それぞれ別のコードを返した場合どうか。ユーザーが見るのは囮のシェルスクリプトだ。

しかし、それではcurlやwgetを利用してシェルスクリプトをダウンロードするユーザーは騙せない。しかしもし、curlとcurl | bashを判定することができたらどうか。実は、できるのだ。

curlとcurl | bashを判定する方法は、bashの処理にある。bashはコードを順次実行していく。コードの実行中はパイプからの読み出しが滞る。ネットワークとパイプのバッファーが全て埋まってしまえば、サーバーからのデータのダウンロードは中断する。したがって、コードの冒頭にsleepを置いて、バッファーを埋めるために無害で表示されないnull文字を送りつける。ダウンロードが途中で中断すれば、curlはパイプを経由してbashに出力しているのだと判断できる。

curl | bashを判定するサーバーを判定する方法

では、ユーザーはどうやってcurl | bashを判定するサーバーを判定すればいいのだろうか。判定が上に述べたような簡単なディレイで行われているならば、そういうディレイを発生させてやればよい。

curl https://example.com/setup.bash | (sleep 3; cat)

しかし、curl | bashを判定する方法はその他にもあるし、ディレイを複数使って判定することもできるので、確実に判定はできない。信頼できないデータはまずローカルのファイルに落として、中身を検証してから、ローカルのファイルをbashで実行すべきだ。