2019-07-19

キャディ株式会社のテクニカルアドバイザーになった

C++勉強会 #1 - connpass

Ta-da:ドワンゴは辞めていない。キャディでテクニカルアドバイザーとしてC++教育もすることになった。7月30日に最初の勉強会をする。

周りで転職が頻発しているので、私もにわかに転職熱をだし、自分の転職市場における価値を確かめるためにも、いくつか企業に話を聞いてみた。その結果としては、私を給料据え置きで雇いC++の仕事をさせたいという企業はあった。しかし、教育一辺倒というわけでもないし年収も現状維持、そしてドワンゴでまだやりたい仕事も残っているときている。転職も興味ぶかい人生の選択ではあるが、しばらくはドワンゴにとどまろうという判断を今回はした。

その話を聞いた企業の一つがキャディ株式会社だ。奇しくもちょうど1年前、もうC++17を現場で使っている企業があるというので話を聞きに行ってブログに書いたことがある。

C++17をすでに現場で使っているというキャディ株式会社に話を聞いてきた

キャディは板金加工の受注生産をしている会社だ。板金加工というのは一枚の金属板を切断したり曲げたり削ったりと様々な加工をして、顧客の望む形状の金属製品を作り上げる仕事だ。従来、板金加工の依頼をするとなると、板金加工業者と話をして、望む形状を伝え、熟練の職人が長年の経験と勘で必要な加工を決定していた。これは時間のかかる作業であり、価格の見積もりだけでも長時間かかるものであった。

キャディは見積もりを自動化することを目指している。顧客がキャディのWebサイトに生産したい形状の3Dモデルデータをアップロードすると、それをソフトウェアで処理し、あたかも折り紙を展開するように、1枚の板から目的の形状を作り出すための加工を決定する。必要な板金加工が自動的に決定できるのであれば、価格の見積もりもできる。従来の板金加工業者が、何日や何週間もかけてようやく見積もりを出していた作業が、自動化をすることによって一瞬で見積価格を顧客に提示することができる。これがキャディの目標だそうだ。

板金加工の見積もりを自動化する。こう書いてしまうととても簡単に読める。しかし似たような計算である折り紙の展開というのは数学的に難しい問題である。自動化の実現には、数学力とアルゴリズム力とコーディング力が必要だ。キャディは自動化の要となる処理の実装について、最新のC++を選択した。古いC++で生産性を下げている余裕はない。問題は、最新のC++を知っているプログラマーは労働者市場では希少だということだ。

しかも、単にC++を知っているだけではダメで、数学力とアルゴリズム力も問われる。しかし、これについては朗報がある。AtCoderのような競技プログラミングコンテストを定期的に開催しているWebサイトの興隆で、今や競技プログラミングは盛んに行われている。高レートを維持している優秀な競技プログラマーは本物の数学力とアルゴリズム力を持っているので、数学力とアルゴリズム力を持ったプログラマーは労働者市場に多数供給されている。

AtCoder:競技プログラミングコンテストを開催する国内最大のサイト

好都合なことに、競技プログラミングではC++が使われることが多い。問題は、単に競技プログラミングで好成績を出すためだけであれば、C++を理解する必要はないということだ。一部の突出した競技プログラマーは、プログラマーというよりはもはや数学者に近く、彼らにとってC++というのはプログラミング言語ではなく、都合のいい計算機でしかない。そのため、必要な数学力とアルゴリズム力とC++力を兼ね備えた人材は労働者市場に極めて稀にしか存在しない。労働者市場に即戦力がいないのであれば教育するしかないが、最新のC++がわかるプログラマーに数学を教えるのと、競技プログラマーに最新のC++を教えるのはどちらがマシかというと、確実に後者だ。そもそも、労働者の人口としてもC++プログラマーより競技プログラマーの方が多い。

キャディは最新のC++を現場で使う興味深い企業であるので、強い興味を持ちながらも、ドワンゴの環境も捨てがたく、結局転職はしなかったのだが、つい先日、技術顧問として月2回ぐらいキャディの社員向けにC++教育をしてくれないかという話がきた。キャディ社内には私と同等以上にC++に詳しい人間がいる。ただし彼には数学力もあるので教育よりは現場の開発に労力を費やしてほしい。単発の勉強会で30分ぐらい話してくれというのであればともかく、定期的に月2回も社員向け教育をしてくれというのは流石に無償では引き受けられない。では報酬も支払うという話になった。

ところで、今私が雇用されているドワンゴでは兼業申請が通れば兼業をしてもよいということになっている。兼業申請が認められるための条件はいくつかあるが、特に重要な条件に、ドワンゴと競合関係にないという条件がある。ドワンゴが板金加工事業をしているという話は聞いたことがないし、その他の条件も問題はないだろうと考え、兼業申請を出してみたところ承認された。

そしてこのたび、キャディ株式会社のテクニカルアドバイザーとなった。

当面の予定としては、月2回、勉強会を開くことになっている。勉強会でキャディの社内情報がかかわらない内容については、聞きたい人がいれば社外から少し人が聞きに来てもいいだろうということで、connpassで勉強会の告知を行うことになった。

C++勉強会 #1 - connpass

勉強会の内容としては、さしあたって、テンプレート、メタプログラミング、ムーブセマンティクスを理解できるようにする目的だが、記念すべき第一回目では、ポインターについて話すことになった。

なぜポインターなのか。私はちょうどC++入門書を書いている。この入門書ではC++知識のブートストラップを試みた。参考書ではすでに教えた知識だけを使って、次の知識を教えるようにした。このような入門書を書くのは初めてなので手探りで牡蠣進めていった結果、ポインターを教えるまではなんともいいがたい教えにくいという違和感があったのだが、ポインターを説明してからは、ポインターの知識を前提にできるので圧倒的に教えやすくなった。そのため、もしポインターが理解できていないのであれば、まずポインターを理解するべきであると考えた。そのため、今回はポインターだ。

https://github.com/EzoeRyou/cpp-intro

ポインターの難しさは複合的な要因がある。アドレスや関節参照という概念の難しさ。C++の型システムの難しさ。C++の文法の難しさがある。この3つを意識しながら7月30日は2時間、ポインターについて話をする。

江添自宅ボドゲ回@7月28日

7月28日に自宅ボドゲ回を下記の要領で開催します。

江添ボドゲ回@7月28日 - connpass

2019-07-15

標準C++規格が3年おきに制定される理由

現在、C++の標準規格は3年おきに制定されている。このスケジュールはC++14から厳しく守られていて、C++20は2020年に制定される予定だ。

なぜなのかを議長のHerb Sutterが解説している。

Draft FAQ: Why does the C++ standard ship every three years? – Sutter’s Mill

まず現状認識として、C++の標準規格は3年おきに制定される。ドラフトにバグがあるので遅らせるべきではないのか。ダメだ。そのために2年間の機能追加と1年間の機能フリーズとバグ修正の期間が儲けられている。

しかし、ある「機能」はあと数回の会議を経たならば完全に合意できて入れられる状態になっている。この「機能」のためにC++20を少しだけ遅らせられないのか? ダメだ。期限までにC++20に入れられない「機能」はC++23に回される。

このスケジュールは厳しすぎる。なぜ3年という固定の期間で物事が進むようになったのか。なぜならば、プロジェクト管理をする方法は2つしかない。長年の経験により、この方法が別の方法よりマシだとわかったからだ。

プロジェクト管理をする2つの方法とはなにか? 完成品をリリースするタイミングを決定する方法で、2つある。一つは機能に合わせること。もう一つは期間に合わせること。どちらか一つしか選ぶことはできず、一報を選んだならば他方を選ぶことはできない。

リリースを機能に合わせる場合、機能が完成しなければリリースできない。そのため、リリース期間を決めることはできない。結果としてリリース期間が際限なく遅延する。

リリースを期間に合わせる場合、期間に間に合わなかった機能は今回のリリースには含まれない。

リリースを機能に合わせる場合というのは、C++98とC++11だった。C++98規格は本来1994年までに制定されるはずだった。Bjarne Stroustrupは1994年までに制定できなければ失敗だとまで言った。結果、1998年になった。C++11は200x年までに制定されるはずだった。結果として2009年にすら2年間に合わなかった。

ある機能が、あと数ヶ月で完成するという時、それは数ヶ月で完成しないし1年でも完成しない。にもかかわらず、あと数ヶ月で完成するからという言い訳でリリースを延々と引き伸ばすと、当初の予定より大幅にリリースが遅れてしまう。

C++の規格制定が遅れると、C++コンパイラーの実装も遅れる。C++コンパイラーベンダーとしては、せっかく実装した機能が、規格の変更によって変わってしまうということを考えると、まだ変更されているうちはわざわざ労力を費やして積極的に実装する理由がない。規格を制定することでようやく実装にかかることができる。

C++98とC++11でこのような失敗をしたのは、我々は経験不足だったからだ。経験を得た我々は、プロジェクト管理に厳格な期間を定めることによって、スケジュール通りに規格を制定するようになった。3年の期間に間に合わなかった機能は次回以降の規格に回す。

2019-07-14

AMDのZen 2でRDRANDが-1を返すので最近のGNU/Linuxがブートできない問題

AMDのZen 2アーキテクチャの新製品が発売されて沸き立っているが悲しいお知らせがある。最近のGNU/Linuxディストロはブートしない。例えばUbuntu 19.04はブートしない。

理由は、ハードウェア乱数を返す命令、RDRANDに不具合があり、常に-1を返すのだという。このため、rdrandを直接使っているsystemdが失敗し、結果としてブートできなくなる。

AMDによればこの問題はBIOSアップデートで修正可能であるという。しかしこれはとても怪しい陰謀論を考えたくなる。なぜRDRANDが常に-1を返すような不具合が未然に発覚せずに製品リリースまでこぎつけてしまったのか。なぜファームウェアのアップデートで修正可能なのか。まさかバックドアなのではないか。

陰謀論はともかくとして、もう一つの問題は、なぜsystemdはRDRANDを直接使っているのかということだ。Linuxカーネルの提供する/dev/urandomではなぜだめなのか。

その理由は他ならぬsystemdのソースコードにコメントとして記載されている。

https://github.com/systemd/systemd/blob/f90bcf8679e8708788290519dc09371e60c6fc82/src/basic/random-util.c#L34

Linuxカーネルのブートの直後では、十分なエントロピープールが溜まっておらず、/dev/urandomへのアクセスすらかなり長い時間ストールする可能性がある。特に組み込みやVMといった環境では問題になる。このため、systemdはブート直後かつ、UUIDとハッシュテーブルのシードの生成のみにrdrandの値を直接使っている。

UUIDはユニーク性さえあればよく、セキュアなRNGは必要がない。

ハッシュテーブルは攻撃者が恣意的な値を多数登録することで、オーダーをO(1)ではなくすることができてしまう。そのためにハッシュ計算のシードに乱数値をつかている。

当初はsystemdはRDRANDを直接使うなんて馬鹿げたことをするものだと思っていたが、どうもこの状況を考えるに使いたくなる気持ちもわかる。systemdはRDRANDがない環境もサポートしているため、フォールバック実装はあるのだが、rdrandがあるが壊れている環境ではどうしようもない。

現在、systemdにworkaroundがコミットされているほか、ファームウェアアップデートが予定されているそうだが、問題はマザーボードベンダーがアップデートを提供するには時間がかかるため、しばらくはこの問題に悩まされそうだ。

結論としてはAMDが悪い。

2019-07-12

GPLv3でライセンスされた自由な技術書の電子書籍を買う理由

江添亮の詳説C++17の電子書籍の売上に対する印税の通知書がアスキードワンゴから送られてきた。額としては微々たるもので、9ヶ月かけて執筆した労働力に対する収入としては圧倒的に少ない。私がドワンゴに雇用されていて安定した給与所得があるのでなければ割に合わない。回らない寿司が何度か食べられる程度の額だ。

アマゾンで江添亮の詳説C++17を購入:https://www.amazon.co.jp/dp/4048930605

ところで、この本はGPLv3でライセンスされた自由な本だ。当然ソースコードが公開されている。

https://github.com/EzoeRyou/cpp17book

にもかかわらず電子書籍に金を払う理由はなぜだろう。しかも、その電子書籍の中にはAmazonのKindleのような不自由な本も含まれているのだ。

AmazonのKindleで多くの本を読んでいるので、不自由ではあるが同じデバイスで本を読みたいという人はいるだろう。

販売している電子書籍はプロの編集者によって組版されている。ここに価値を見出しているのかもしれない。

あるいは単に筆者に対するお布施かもしれない。

気になったのでTwitterで聞いてみた。

どうやらお布施目的が多いようだ。

技術書の出版が商業的に成立するにはお布施目的だけでは成り立たない。難しいものだ。

それはそうと、私の書いたC++入門書の出版作業をいましている。近いうちにアスキードワンゴから出版できる見込みだ。

https://github.com/EzoeRyou/cpp-intro

今回は、古くからの友人のプロのカメラマンである三浦大に依頼して、著者近影を撮影した。

http://www.masarumiura.jp/

著者近影の写真の著作権は撮影したカメラマンのものだが、それ以外の本はGPLv3だ。

また、C++と並列処理について書かれたAnthony WilliamsのC++ Concurrency in Actionの翻訳もアスキードワンゴから出版される予定だ。こちらは私が査読をする。

2019-07-09

C++20 Deprecate implicit lambda capture of this

C++20 deprecated the implicit lambda capture of this. What does it mean and how can we keep up with the change?

As of now, you probably don't need to do anything. Because it's just the deprecation, not the removal. But in the future, it will eventually be removed so be prepared.

So what is "implicit lambda capture of this" anyway? The following code relies on that behavior.

struct S
{
    int x ;
    void f()
    {
        // C++17: Well-formed
        // C++20: Deprecated.
        [=]{ std::cout << x ; }
    }
} ;

So above code use the variable x. this name x is a data member of class S. So "x" in this context means "this->x". Above code works because lambda capture implicitly capture this pointer. That's why we can use "x" inside the lambda expression.

You may not realize it but this "x" is not captured by copy. In fact, "x" is not captured at all. What lambda capture instead is this pointer. So we can use "x" via the this pointer. So "x" is effectively captured by reference. We can change the value of x.


void f()
[
    auto change_x = [=]( auto value ) { x = value ; } ;
    change_x(123) ;
}

This behavior breaks novice's assumption that lambda capture clause "[=]" means capture by value. People may write code like this:

struct S
{
    int x ;
    auto get_closure()
    {
        return [=]{ return x ; } ;
    }
} ;

int main()
{
    std::function<int()> f ;
    {
        S s{123} ;
        f = s.get_closure() ;
        // s's lifetime ends here
    }
    int result = f() ;
}

Assuming that it's safe because of captured by value and get caught by Undefined Behavior.

C++17 partially fixed this by introducing the explicit capture of this.


struct S
{
    int x ;
    void f()
    {
        // capture this pointer by value, x is reference
        [this]{ x ; } ;
        // catpure *this by value, x means x is value
        [*this] { x ; } ;
    }
} ;

"[*this]" copy the this pointer which means x is accessed through this pointer. "[*this}" copy the *this, that is the value of class object S. Since it copy the class object, x is copied too.

C++17 retained the implicit capture of this.

In C++20, it is decided that the standard deprecate the implicit capture of this via [=].


struct S
{
    int x ;
    void f()
    {
        // C++17: well-formed, it means [=, this]
        // C++20: deprecated, it still means [=, this]
        // C++??: if removed, ill-formed.
        [=] { x ; } ;
    }
} ;

The behavior of "[&]" doesn't change. It still implicitly capture this pointer by value. So the data members are effectively captured by reference. There should be no confusion on this.