2017-01-13

GCCがC++17のコア言語機能を実装完了

Hacker Newsで話題になっていて知ったのだが、GCCがいつのまにか、C++17の現ドラフトの全コア言語機能を実装している。

C++ Standards Support in GCC - GNU Project - Free Software Foundation (FSF)

とうとう、なかなか実装されなかったクラステンプレートのコンストラクターからの実引数推定も試すことが出来た。

#include <iterator>


template < typename T >
    struct X
    {
        X( T t ) { }
        template < typename Iterator >
        X( Iterator first, Iterator last ) { }
    } ;
// deduction guide
template < typename Iterator >
    X( Iterator, Iterator ) -> X< typename std::iterator_traits<Iterator>::value_type > ;


int main()
{
    // X<int>
    X x1(0) ;
    
    int a[] = {1,2,3} ;
    // X<int>
    X x2(std::begin(a), std::end(a))  ;
}

これが動く。感動だ。

ドワンゴ広告

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

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

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

2

2017-01-10

ボルダリングを初めて2年がたった

ボルダリングを初めて2年がたった。最初は7級すら苦労していたのだが、今は全く苦労せずに登れるようになった。現在のグレードは4級で、1ヶ月以上取り組み続けて簡単な3級が落とせる程度だ。

ボルダリングを初めてから体重が増えた。これは筋肉が増えたこともあるが、脂肪も明らかに増えている。運動により食事量が増えたためだろうか。ボルダリングを始める前は60kgであった体重が、いまは73kgほどになっている。体が重いと登りにくい。減量のため、食事量を意識的に減らし、通勤を電車から徒歩に切り替え、自宅から会社までの5kmほど歩くようにしている。5km歩くと50分ほどかかる。しかし電車で行くにしても自宅は駅から遠く、かつ乗り換えが発生するので結局40分かかる。通勤時間が10分しか変わらないのであれば、歩いてもよいということになる。一週間ほど徒歩通勤を続けたところ、体がなれたので特に疲れることはなくなった。一ヶ月ほど続けているが、痩せない。

ボルダリングでは特別な靴を使う。しかし未だに納得のいく製品に出合っていない。これまでは主にスポルティバの靴を履いてきたが、スポルティバの靴は3ヶ月ぐらいで潰れてしまう。スポルティバの中では、Geniusが一番気に入った製品だった。Geniusは9ヶ月履くことができた。今はFive TenのQuantumを履いている。この靴はFive Tenには珍しく、幅広の足でも履くことができる。3ヶ月ほど使ったが、つま先のソールが大分削れてきた。あと3ヶ月は持たないだろうと思う。Five TenのQuantumは相当に気に入ったので、次の靴もこれにするかもしれない。

ところで、クライミングジムでは、たまに白髪交じりの齢六十はとうに越していようと思われる翁が極めて動的な動きをしてグレードの高い課題を登っている。それをみて、なるほど、ボルダリングというのは歳をとっても問題なくできるスポーツなのだなと老後も頼もしく考えていると、ある人から、「果たしてそうだろうか。あの歳でなおあれだけ動ける強者のみ生き残っているだけではないだろうか」と生存者バイアスの可能性を指摘された。確かにそうだ。

2017-01-05

GoogleがGoによるPython実装、Grumpyを発表

Googleが既存の社内のPythonコードをGoで実行するためのPython実装を公開している。

Google Open Source Blog: Grumpy: Go running Python!

google/grumpy: Grumpy is a Python to Go source code transcompiler and runtime.

Googleの発表によれば、YouTubeのフロントエンドサーバーとYouTube APIはほとんどPythonで書かれているという。現在、YouTubeのフロントエンドはCPython 2.7で実行されているが、CPythonの制約により効率化には限界があるのだという。

GrumpyはPython 2.7のコードをGoのコードに変換するツールgrumpcの実装だ。grumpcはPythonで実装されていて、astモジュールでPythonをパースして、Goコードを吐く。Python 2.7系なのは、Google社内の既存のコードを実行するためだ。また、execやevalのようなインタープリター実装ならではの極めて動的な一部の機能は実装されていない。

Pythonの標準ライブラリのほとんどは、Pythonによって実装されているため、そのままGoコードに変換されてそのまま動く。

GrumpyにはGlobal Interpreter Lockが存在しない。リファレンスカウントのかわりにGoのGCにオブジェクトの生存管理を任せている。この設計のため、C extension moduleは使えない。この設計により、GrumpyはCPython実装よりスケールすると主張している。

Grumpyはインタープリターではない。Grumpyによって生成されたGoのコードは通常通りGoによってコンパイルされる。これにより開発やデプロイの柔軟性は下がるが、ネイティブコードへのコンパイルと静的解析により、より効率のよいコードを吐くことができるようになる。また、Grumpyで実行されるPythonコードは、Goのモジュールをimportして使うことができる。

興味深いツールだ。

2017-01-03

ディストピア小説のネタとして使える実話

事実は小説より奇なりとはよく言ったもので、ディストピア小説を超える実話が世の中に溢れている。

Automated book-culling software drives librarians to create fake patrons to "check out" endangered titles / Boing Boing

East Lake Country図書館のシステムは、貸出記録から人気のない本を破棄するようになっている。この図書館の司書は、フェイクの利用者情報を登録して、司書がお気に入りの破棄されてほしくない本を守るために、多数の貸出記録を捏造した。

Healthcare workers prioritize helping people over information security (disaster ensues) / Boing Boing

ある病院の医療システムのセキュリティは、一定期間の入力がないとパスワードの入力を要求する仕組みになっている。このために、看護師の医療システムの利用時間の大半は、パスワードを入力することに費やされている。刻一刻と病状が悪化する患者の情報を即座に入手しなければ患者の生死に関わる状況では、パスワードの入力にかかる時間は致命的である。そのため、ある病院では、新米の看護師を医療システムのキーボードを一定期間ごとに押し下げる係に割り当てている。

このような話をネタにしたディストピア小説が読みたいものだ。例えば監視社会で観測データから社会に不要な人間が自立型の機械によって自動的に破棄されていくディストピア世界において、人類の大半が破棄された荒廃とした世界で、生存のために観測データの捏造を続ける人類。

あるいは、電力や食料の生産が完全に自動化された世界において、生産量の調整のためにひたすら入力を一定間隔で叩き続ける不毛な仕事。

そういえば、年末にPixivのアカウントに対して大規模な他所から流出したIDとパスワードの組み合わせによるログイン試行が行われ、結果として何が行われたかというと児童ポルノ画像のアップロードだったという。これは、児童ポルノというのは法律で所有が違法なことにより、画像を投稿できるWebサイトに対する最も手っ取り早い嫌がらせの手段であるのだという。この事件から、児童ポルノを武器として利用するディストピアネタを思いついた。

例えば、児童ポルノは存在が違法であり、児童ポルノを記録するストレージも違法である。そこですべてのコンピューター機器はRFC 3751ストレージは児童ポルノを検出すると自動的に自己破壊し、また児童ポルノを検出して破壊する自律型ロボットがそこらじゅうを徘徊している。大規模な児童ポルノをばらまくマルウェアの影響によりコンピューターの大半がOmniscience Protocolにより文鎮化した世界で人類が生き残りをかけて児童ポルノを武器にロボットと戦う。児童ポルノが武器になる理由としては、児童ポルノだと判定されるものををロボットに観測させるとロボットはOmniscience Protocolを発動して自己破壊を行うからだ。

2016-12-26

誤り:paizaの問題はC++17でも成り立つ

この記事は間違っていた。

この変更では、インクリメント演算子の副作用のコミット順序はまだ規定されていない。

paizaが以下のような質問を出している。

問題は、int i = 0 ;であるとき、以下の式を評価した結果が1になるのはどれかという問題だ。
  1. i++ + ++i
  2. ++i + ++i
  3. i++ + i++
  4. ++i + i++

C言語では、この式を評価した結果は未定義である。

C++14までは、この式を評価した結果は未定義である。

C++17では、サブ式の評価順序が固定されたことにより、この式は以下のように評価されることが規格上保証されている。

C++17でも未だに未定義。§1.10 p18に書かれている。

  1. 2
  2. 3
  3. 1
  4. 2

参考:

[PDF] P0145R3: Refining Expression Evaluation Order for Idiomatic C++

P0400R0: Wording for Order of Evaluation of Function Arguments

現在、Clang 4.0 headがP0145R3とP0400R0を正しく実装している。GCC 7 headはP0145R3の実装を謳っているが現時点ではバグのため、2番目の式の評価が4になるようだ。

ドワンゴ広告

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

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

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

2016-12-09

freeeの保有する特許5503795について読んで解釈を試みたがやはり新規性も技術的価値もわからないしゴミだったのでfreeeのエンジニアは早くfleeするべき

freeeが特許侵害でマネーフォワードに訴訟を起こしたそうだ。freeeのプレスリリースでもその事実を記載している。

特許権侵害訴訟の提起について | プレスリリース | freee株式会社

これによると、マネーフォワードが侵害したとfreeeが主張している特許は、特許第5503795号だそうだ。他の特許については触れていないのでこの特許を読んでみることにする。

特許 第5503795号 会計処理装置、会計処理方法及び会計処理プログラム - astamuse

この特許は本当にゴミなのだが、私の解釈した限りで、この特許を使った技術的な実装とは以下のようなものだ。ここで、この特許が主張している文脈やアイディアは無視して、単なる根本的な実装のみを書いている。

ユーザーの利用している金融機関やクレジットカードの取引の履歴データ(何をどこからいくらで買った、売ったという取引情報)をWeb上からスクレイピングする。

会計処理では、取引情報は、勘定科目という様々な名目に仕訳しなければならない。勘定科目とは、例えば費用の項目では、仕入とか接待交際費とか消耗品費とか賃借料といったものである。スクレイピングした取引履歴情報には、勘定科目はない。勘定科目というのはその文脈に依存する。例えば、鉛筆を購入したとして、その鉛筆を自社の社員が業務のために使うのであれば名目は消耗品費だ。鉛筆を更に第三者に販売するのであれば名目は仕入だ。

勘定科目は人間がその文脈に基づいて分類しなければならないのだが、ある程度の推測はできる。例えば、鉛筆の購入費用は、水道光熱費とか保険料にはまずならない。大方は消耗品費か仕入だろうし、大抵の場合は消耗品費だろう。したがって取引履歴情報から「鉛筆」というキーワードを抽出して、キーワードと勘定科目の対応テーブルを参照して、自動的に勘定科目の仕訳を行うことができる。

自動で仕分けた上で、人間に仕訳結果を見せて、間違いは修正させる。

とまあ、基本的にはこのような実装だ。

ちょっとまてよ、そんな分類作業は会計や簿記といった概念が発明されて以来行われている極めて一般的な既知既存の作業ではないか。キーワードで勘定科目を推定することに新規性などあるのか。

この特許は、極めてバカバカしい、人をけむにまくような、ポストモダンもかくやと思われるような言葉遣いをして、このような人間が何千年も行ってきた作業を再発明している。

まず、請求項1の冒頭を読んでみよう。

請求項1

クラウドコンピューティングによる会計処理を行うための会計処理装置であって、

「クラウドコンピューティング」とは一体何を意味する言葉であろうか。一般的に考えればAWSとかAzureのようなプラットフォームのことを言うのであろうか。クラウドだろうが非クラウドだろうが本質的には大差ないし、第一肝心の処理を実装するソフトウェアのみかけの実行環境は古典的なコンピューターと全く変わらない。クラウドは既存のソフトウェア資産を使えるように大変な努力をしているからだ。

気を取り直して、続きを読んでいこう。

ユーザーにクラウドコンピューティングを提供するウェブサーバを備え、

おや、「クラウドコンピューティング」とはユーザーに提供するものなのか。ということはAWSとかAzureなどではないということになる。freeeは自ら「クラウド会計ソフト」と名乗っている。すると、エンジニアがインフラとして使う意味のクラウドコンピューティングではなく、ユーザー側ではなくサーバー側で処理を行うことを「クラウドコンピューティング」と称しているのだろうか。こちらの意味であれば、例えばGMailはクラウド電子メールソフトであるし、Google Docsはクラウド表計算ソフトということもできる。

この特許の前文を検索しても、「クラウドコンピューティング」なる用語の定義が出てこない。ただし、

本発明はウェブ明細データを利用する点で、現時点では、中小企業及び個人事業主のうち、その恩恵を受けることができる割合が限られている。すなわち、我が国の企業におけるクラウドコンピューティングの利用率は、非特許文献1に記載されているとおり、9.1%に過ぎない、つまり大部分においてウェブ上のリソースが活用されていないのである。本発明は、中小企業及び個人事業主に初めて焦点を当てた上で、かつ、今後のクラウドコンピューティングの利用率向上を見越してなされたものであり、そこに大きな先進性がある。

この非特許文献1というのは、総務省、平成23年通信利用動向調査(企業編)、31頁のことで、以下から入手できる(ただしHTTPなので改変されていない保証がない。)

http://www.soumu.go.jp/johotsusintokei/statistics/pdf/HR201100_002.pdf

これによれば、クラウドコンピューティングの定義はしていないが、どうやら処理の一部をサーバー側で行い、ユーザー側のクライアントにWebブラウザーを使うソフトウェアのことを意味しているようだ。

しかし、「Web上のリソースが活用されていない」という表現は謎である。まるですでに持っている資産を使わずに放置しているような書きようだが、サービスを使う契約すら結んでいないのに、活用していないとは一体なんだろうか。例えば東京には流しのタクシーが多数走っているが、タクシーにほとんど乗らない東京人は、「公道上のタクシーリソースを活用していない」と言えるのだろうか。

そして、先進性というのも疑問だ。というのも、GMailは10年以上前に公開されているし、それ以前にもWeb上でメールを管理、送信できるようなWebサイトは、クラウドという言葉の登場以前にも存在していた。企業が使いたいかどうかということに先進性があるのだろうか。より使いやすいUI、管理しやすい機能、セキュリティなどは、別にいまに限った話ではない。

なぜ筆者が「クラウドコンピューティング」なる用語の定義にこんなに細かく考察しているかというと、この特許では、「クラウドコンピューティング」なる用語を多用しているからだ。特許の文章中に13回使われている。例えば、

ウェブサーバが提供するクラウドコンピューティングによる

クラウドコンピューティングによる会計処理を行うための会計処理装置

ユーザーにクラウドコンピューティングを提供するウェブサーバ

Webブラウザーをクライアントとして使いサーバー側の処理に依存するソフトウェアが、Webブラウザーではないソフトウェアクライアントと比べて技術的に何か先進性があるようには思われない。特許のクラウドコンピューティングをクライアントサーバー型サービスに変えても別に何の違いも内容に思われる。

クラウドコンピューティングという用語の使い方だけで謎であるし新規性のかけらも感じられない。

特許では、さも当たり前の人間が何千年もやっている作業が、まるで先例なく画期的な思いつきによって発明されたかのように書かれている。

例えば、キーワードには表記ゆれがある。ANAはエー・エヌ・エーと書かれるかもしれないし、エーエヌエーと書かれるかもしれない。このようなひらがな、カタカナ、漢字、アルファベット、ハイフンの有無、マイナス、長音記号などの表記ゆれを補正して同じキーワードとして扱うようにする処理が、あたかも画期的な思いつきで実施したところ効果があったかのように書かれている。

また、「モロゾフ JR大阪三越伊勢丹店」のようにキーワードが複数ある場合、JR大阪三越伊勢丹店のJRというキーワードで勘定科目を推定すると旅費交通費だが、モロゾフという洋菓子店で推定すると、贈答品を購入したので接待費だと推定できる。このように複数のキーワードがある場合、キーワードの種類によって優先度を設けることで、正しい判定ができる。この優先度として、品名は取引先より優先させた方が推定の制度が上がることが画期的なひらめきにより発見されたなどとしている。そんな複数のキーワードに優先順位をつけることは人間が何千年も行ってきたことであるし、コンピューターの発明以後即座に行われただろうに、どういう新規性があるのだろうか。

ちなみに、この特許は一度却下されているのだが、この優先順位という請求項を付け加えることで認められている。

この特許は、会計処理は発生主義の原則に基づいて「デイリーベース」(1日単位でという意味か?)で行われているが、個人や中小企業などの場合、期日までに会計処理を終わらせればよく、自動的に仕訳をした上で、ユーザーにWebブラウザー上で候補を表示して修正させる方法で、一括して仕訳を行うので、新規性があるとしている。

これもよくわからない話だ。というのも、人間が行う金の動きなのだから、その日のうちに申請し忘れて期日ギリギリに会計処理を行うことなど大企業であってもよくあるだろうし、コンピューターが今のように高性能になる前は、バッチ処理といってデータを一括で一気に処理していたものだ。すると、バッチ処理をしていた頃のコンピューターシステムで、キーワードに対応した分類を行うもので、更に会計処理をする先例が存在すれば、この特許は無効になる。そのような先例は探せばあるのではないかと思われるし、会計処理に限定したところで何か技術的に変わるとも思えない。

この特許は、とにかく人間が文字と数学を発明して以来、数千年も行ってきた会計処理という既存の作業をコンピューターでやったという他なく、しかもそのコンピューターというのが、クラウドコンピューティングとかWebサーバーとかWebブラウザーとか極めて限定的な範囲になっている。いくら範囲を狭めたところで、技術的に何か新規性のある発明には思えない。

さて、こんなゴミ特許は一体誰が発明したのか。特許に記載の情報によれば、佐々木大輔、横路隆、平栗遵宜が発明者になっている。

freeeのWebサイトの会社概要によれば、

会社概要 | freee株式会社

佐々木大輔は創業者で代表取締役。「形式的で非効率なことは大嫌い」とあるが、こんなゴミ特許を形式的で非効率的なゴミ特許を取得した上で、競合他社を極めて短い交渉期間でまるで妨害するかのように訴訟を起こす形式主義と非効率性は持ち合わせているようだ。

横路隆はCTOで共同創業者。「テクノロジーでスモールビジネスのありかたを再定義していきます」とあるが、技術ではなく特許で競合他社を妨害するビジネスを定義中のようだ。定義といえば、クラウドコンピューティングなる謎の用語も定義していただきたい。

平栗遵宜は開発本部長。「ユーザーに価値を届けるために必要なのは技術力と気合」とあるが、技術力ではなくゴミ特許で競合他社を妨害することで他の価値を抹消した上で唯一の価値を独占して届けるようだ。まさに気合が必要とされる。

創業者で代表取締役、共同創業者でCTO、開発本部長といった、役職キーワードから推定して会社の運営や技術の方向性の最終的な判断をする役割の人間が技術で勝負せず、くだらないゴミ特許を恥ずかしげもなく申請して同業他社に特許訴訟を起こすとは、freeeの技術的な先行きが危ぶまれる。

freeeのエンジニアは早くfleeするべきではないか。

2016-12-08

freeeのゴミのような特許の新規性が全く理解できない

freeeが特許侵害でマネーフォワードを提訴したというニュースが流れている。

freeeがマネーフォワードを提訴、勘定科目の自動仕訳特許侵害で | TechCrunch Japan

肝心の特許は、以下のものらしい。

特許 第5503795号 会計処理装置、会計処理方法及び会計処理プログラム - astamuse

読んでみたが、何の新規性もあるようには読めない。やたらとクラウドコンピューティングなる言葉が出てくるが、この特許でAWSとかAzureとかGoogle Apps Engineのようないわゆるクラウドかそうでない従来のサーバーかで何か違いがあるとは思えないし、その他のことも、人間が有史以前からやってきた分類作業であるようにしか読めない。数千年も存在する既存の概念をコンピューターで行うというだけのゴミ特許が乱立しているが、どうやらそのコンピューターを更に細分化してクラウドコンピューティングとかウェブサーバーとかのバズワードを追加しただけで、範囲をいくら狭めようと分類は分類でしかない。

特許制度は消え去るべきだ。

そして、歴史的に、このような特許ゴロ訴訟をしだす企業というのは4パターンある。

  • 何も具体的な特許技術を利用した現物を提供していないが、ゴミのような特許権だけ保有していて、わずかでも抵触している可能性のある企業団体個人に対して特許利用料を支払え、さもなくば訴訟だと迫るもの
  • 大量のゴミ特許を保有している大企業が小遣い稼ぎに、お前はうちの膨大な特許プールのどれかを侵害している可能性があるので特許使用料を支払え、さもなくば訴訟だと迫るもの
  • 競合相手に対し自由市場による競争をしたくないため存在を妨害するためにゴミ特許を振りかざして利益が出ないほど法外な利用料を要求して訴訟を行うもの
  • 時代についていけない死に体で破産寸前の企業が昔取ったゴミ特許を振りかざして最後の小遣い稼ぎを試みるもの

この特許は本当に新規性のかけらもない。

2016-12-07

江添ボドゲ会@12月24日

12月24日に私の自宅でボードゲームを遊ぶ会を開催することにした。参加方法と会場の場所は以下の通り。

江添ボドゲ回@12月24日 - connpass

参考書に昔の技法を書くべきか:C++17のコンパイル時分岐

今、C++17のライブラリの参考書を書いているのだが、C++14時代の、今は現役だが、もうすぐ古代の技術になる技法を紹介すべきかどうか迷っている。

問題はコンパイル時分岐だ。たとえば、イテレーターがランダムアクセスイテレーターかどうかで、最適な処理が異なるアルゴリズムがあったとする。以下のように書けばいいだろうか。

template < typename Iterator >
void algorithm( Iterator first, Iterator last )
{
    if ( std::is_same<
            std::random_access_iterator_tag,
            typename std::iterator_traits<Iterator>::iterator_category
        >{}
    )
    {
        // ランダムアクセスイテレーターに特化した高速なアルゴリズム
        first + 1 ; // ランダムアクセスイテレーターの処理の例
    }
    else {
    // Forward Iteratorにも対応できるアルゴリズム
    }
}

残念ながら、このコードにランダムアクセスイテレーター以外を渡すとコンパイルエラーになる。その理由は、イテレーターと整数をoperaotr +に渡しているからだ。これはランダムアクセスイテレーターしか提供していない操作だ。

コンパイルエラーを防ぐには、あるテンプレートコードが条件次第で実体化される措置が必要だ。つまり、コンパイル時分岐が必要になる。

C++14でコンパイル時分岐を実現する方法はふたつある。関数テンプレートのオーバーロードを使う方法と、テンプレートの特殊化(部分的特殊化)だ。

関数テンプレートのオーバーロードを使うには、以下のようにiterator_tagでオーバーロード解決を行う。


template < typename Iterator >
void algorithm_impl( Iterator first, Iterator last,
    std::random_access_iterator_tag )
{
// ランダムアクセスイテレーターを必要とする処理
}

template < typename Iterator >
void algorithm_impl( Iterator first, Iterator last,
    std::bidirectional_iterator_tag )
{
// 双方向イテレーターを必要とする処理
}

template < typename Iterator >
void algorithm( Iterator first, Iterator last )
{

    algorithm_impl( first, last,
        typename std::iterator_traits<Iterator>::iterator_category{}
    ) ;
}

テンプレートの特殊化は特にひねりはない。

template < typename T >
struct algorithm_impl
{
template < typename Iterator >
static void process( Iterator first, Iterator last )
{
// 前方イテレーター以上が必要な処理
}

} ;

template  <>
struct algorithm_impl< std::random_access_iterator_tag >
{
template < typename Iterator >
static void process( Iterator first, Iterator last )
{
    first + 1 ;
}

}

template < typename Iterator >
void algorithm( Iterator first, Iterator last )
{

    algorithm_impl<
        typename std::iterator_traits<Iterator>::iterator_category
    >::process( first, last ) ;
}

このようにコンパイル時分岐は実現できるのだが、C++17ではconstexpr ifが入ったことでこのような技法は古臭いハックに成り下がってしまった。

template < typename Iterator >
void algorithm( Iterator first, Iterator last )
{
    using iterator_category = typename std::iterator_tratis<Iterator>::iterator_category ;

    // ランダムアクセスイテレーターの場合の処理
    if constexpr ( std::is_same< iterator_category, std::random_access_iterator_tag >{} )
    {
        first + 1 ;
    }
    // 前方イテレーター以上
    else
    {
    }
}

constexpr ifがあれば、昔の泥臭いコンパイル時分岐ハックはいらなくなる。とすれば、参考書にわざわざ昔のハックを書く必要はない。

とはいえ、それはC++17が普及してからの話だ。C++17が制定されるのにまだ1年かかり、GCCやClangの規格準拠のC++コンパイラーの安定版がリリースされるまでに数年かかり、普及には更に時間がかかる。

とはいえ、歴史を振り返れば、かつてのenumハックがstatic constな整数型のデータメンバーになり、今ではstatic constexprなデータメンバーになっているのを考えると、わざわざ昔のハックを載せる必要はないように思える。

ドワンゴ広告

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

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

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

2016-12-01

AWSのアクセスキーをハニートークンとして使うアイディア

Early Warning Detectors Using AWS Access Keys as Honeytokens

この発想はなかった。

AWSのアクセスキーはハニートークンとして使える。

ハニートークンとは、普段使用しないものが使用されたことを検知して、意図しない利用を検知するトリックである。例えば、通常ならば使われないメールアドレスをパスワードとともに、自分しかアクセスできないストレージに格納しておく。その状態で、もしメールサーバーにログインされた場合は、自分しかアクセスできないはずのストレージに他人がアクセスして、マヌケにもメールアドレスとパスワードをストレージ上に保存しているのを発見して、利用を試みたということになる。つまり、侵入を検知できる。

AWSのアクセスキーは、ハニートークンに使うことができる。AWSに権限を持たないユーザーを追加して、そのユーザーでアクセスキーを発行する。アクセスキーの使用を検知してログをとったりアラートを飛ばしたりするために、AWSのCloudTrail/CloudWatchを設定する。あとはこのアクセスキーを自分しかアクセスできないはずの秘密の場所にばらまいておけばいい。たとえば、

  • サーバーのストレージ、特に ツールが慣習的に使う~/.aws/credentialsなど
  • 自分のローカルのコンピューターのストレージ
  • アプリケーションとかsystemdの設定ファイルの中
  • GitHubのプライベートレポジトリ

その後、アクセスキーが使われた場合、自分以外の誰かが自分以外には本来アクセスできないはずのストレージにアクセスしたということだ。

この方法の素晴らしいことには、AWSのインスタンスは立ち上げないため、金がかからないということだ。アクセスキーは無料でいくらでも発行できる。

2016-11-15

C++標準化委員会の文書: P0480R0-P0489R0

P0480R0: Explicit type checking with structured bindings

構造化束縛に型を制約できる機能を追加すべきではないかという提案。

我々は変数の型に制約をかけられる。

SpecificType var = func() ;
// 間に長いコード
process( var ) ;

このように書いた場合、funcの返す型はSpecificTypeに変換可能であり、processはSpecificTypeを受け取るという制約を書いたことになる。後にfuncやprocessの定義が書き換わって、このコードが通らなくなった場合は、コンパイル時に発見できる。

auto var = func() ;
process( var ) ;

このように書いた場合、型に制約がかからない。

構造化束縛では、型に制約をかす方法がない。ライブラリである程度の制約をかすことはできるが、そのためには冗長なコードを書かなければならない。構造化束縛で型に制約をかける機能が必要ではないか。

[PDF] P0481R0: Bravely Default

デフォルトのコピーコンストラクターがあるならば、デフォルトのoperator ==を生成しようという提案。

そして、operator ==が定義されていて、operator !=が定義されていないならば、デフォルトのoperator !=を生成する。

コピーは等価と深く結びついているので、この挙動は問題がないという主張。

なお、タイトルはスクエアエニックスから出された3DSのゲームが元ネタだという。タイトルは完全に意味不明だが、この提案に不思議と合っているから使ったという。

P0482R0: char8_t: A type for UTF-8 characters and strings

UTF-8文字型であるchar8_tの提案。

UTF-8文字列リテラルの型もchar8_t[]型になる。

移行のために、char8_t[]からchar[]への暗黙の型変換を追加する。この暗黙の型変換を追加するには標準変換の細かいルールを変更しなければならないので、最初からdeprecated扱いで入れるのもありだ。

std::u8stringからstd::stringへの暗黙の変換も提供する。

必ず入れなければならない。

[PDF] P0483R0: Extending Memory Management Tools, And a Bit More

T型の値を参照するイテレーター[first, last)を受け取り、未初期化のメモリを参照する出力イテレーターoutに対して、T型がnoexceptなムーブを提供していればムーブ構築を、そうでなければコピー構築を行うアルゴリズム、uninitialized_move_if_noexcept(first, last, out)の提案。

実装は簡単だがあっても困らないだろう。

[PDF] P0484R0: Enhancing Thread Constructor Attributes

C++のスレッドライブラリを使わず、実装依存の独自拡張のスレッドを使う理由に、スレッドに対して様々な実装依存のオプションを指定したいという需要がある。

オプションというのは、例えばスレッドの優先度、アフィニティ、スケジューリング戦略、スタックサイズ、スタック拡大の有無などだ。

これらのオプションをどうやって指定するか。実装がサポートしていない無効なオプションを渡した時にどう通知するか。実装がサポートしているオプションをクエリーする方法などについて、どのように設計すればいいのかということについて軽くまとめている。特に提案はない。

[PDF] P0485R0:Amended rules for Partial Ordering of function templates

テンプレートのpartial orderingの文面に考慮漏れがあり、パラメーターパックが関わった時に、関数テンプレートのテンプレートの実体化が曖昧になる問題を修正。

[PDF] P0486R0: for_each_iter algorithm proposal

参照する値ではなくイテレーターを得るfor_each_iterアルゴリズムの提案。

std::vector<int> v = { 1,2,3,4,5 } ;
for_each_iter( begin(v), end(v), [](auto && i )
    { std::cout << *i << '\n' ; } ) ;

ありそうでなかった。

P0487R0: Fixing operator>>(basic_istream&, CharT*) (LWG 2499)

以下のコードはバッファーオーバーフローの危険性がある。

char buffer[32] ;
std::cin >> buffer ;

C11でgetsが廃止されたように、operator >>( basic_istream &, charT * )も廃止しようという声がある。このオーバーロードは廃止すべきだが、以下のように変更してはどうか。

template<class charT, class traits, size_t N>
  basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& in,
        charT* scharT (&s)[N]);

これでバッファーオーバーフローの危険性はなくなる。

ついでにstd::arrayにも対応させよう

template<class charT, class traits, class arrayT>
  basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& in,
        charT*arrayT&& s);

という提案。安全のために採用されるべきだ。

[PDF] P0488R0: WG21 Working paper: NB Comments, ISO/IEC CD 14882

現在のドラフト規格の文面に対するNBコメント集

[PDF] P0489R0: WG21 Working paper: Late Comments on CD 14882

NBコメントの締め切りまでに提出が間に合わなかったコメント集

ドワンゴ広告

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

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

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

2016-11-14

C++標準化委員会の文書: P0471R0-P0479R0

P0471R0: Single argument std::inserter

引数が1つのinserterを追加する提案。

vectorの要素をすべてsetにコピーする場合、inserterを使うと便利だ。

std::vector<int> vector ;
std::set<int> set ;

std::copy( begin(vector), end(vector), std::inserter( set, begin(set) ) ) ;

問題は、inserterの第2引数は、この場合何の意味も果たしていないということだ。第2引数はbegin(set)でもend(set)でも挙動は変わらない。ならば、inserterに引数を1つしか取らないオーバーロードを追加すべきではないか。

引数を1つしか取らないinserter( Container c )は、inserter( c, c.begin() )と同じ意味になる。

これは入るべきだ。

P0472R0: Put std::monostate in <utility>

<variant>にあるstd::monostateを<utility>に移動する提案。

monostateは1値を表現する型である。monostateは1種類の状態しか取らない。monostateは比較演算子をサポートしている。この特性は汎用的に便利なので、variant以外の場面でも使いたい。monostateだけを使うのに<variant>に依存させるよりは、<utility>に移したい。

利用例としては、テンプレートコードがうっかり型独自の操作に依存しているかどうかを調べるテストの入力として、futureなどで値を持たないことを意味するためにvoidを渡しているが、voidは特殊な特性があり扱いづらいため、voidの代わりとして渡すことが上げられている。

voidは完全型にする提案が上がっているが、まあ、別にutilityでもいい気はする。

P0473R0: + for std::vector concatenation

operator +とoperator +=でvectorを連結できる機能の提案。まるでbasic_stringのようだ。

int main()
{
    std::vector<int> v1 = { 1, 2, 3 } ;
    std::vector<int> v2 = { 4, 5, 6 } ;

    auto v3 = v1 + v2 ;
    v3 += v1 ;

    // v3の中身は{1,2,3,4,5,6,1,2,3}
}

いまさら? 確かに、vectorの連結はよく行う処理ではあるので、簡単にかけるのは便利なのだろうが。

P0474R0: Comparison in C++: Basic Facilities

partial, weak, total orderの3種類の比較を提供する関数群の提案の文面案

P0475R0: LWG 2511: guaranteed copy elision for piecewise construction

C++17でコピー省略が必須になったので、CopyConstructible要件を付けなくて良くなった箇所から要件を取り除く提案。

P0476R0: P0476r0: Bit-casting object representations

ビット列を指定した型として解釈するライブラリ、bit_cast<To>(From)の提案。

ビット列を型として解釈するには、reinterpret_castやunionがよく使われるが、これには未定義の挙動の問題がある。規格に詳しいプログラマーはstd::aligned_storageとmemcpyを使うが、memcpyはconstexprではない。そこで、constexprなビット列キャストライブラリを追加する。

[PDF] P0477R0: std::monostate_function<>

関数ポインター、メンバー関数へのポインターのラッパーライブラリ、std::monostate_functionの提案。


void f() { }

struct X
{
    void f() { }
} ;

int main()
{
    std::monostate_function<&f> f1 ;
    f1() ; // fを呼び出す

    std::monostate_function< &X::f > f2 ;
    X x ;
    f2( x ) ; // X::fを&xをthisとして呼び出す
}

C++17から新しく入った非型テンプレートパラメーターに対するautoを使っているので、テンプレート実引数には値を指定するだけでよい。


template < auto Callable >
struct monostate_function
{
    template < typename ... Types >
    constexpr
    auto operator () ( Types ... args )
    noexcept( std::invoke( Callable, std::declval<Types>... ) )
    {
        return std::invoke( Callable, std::forward<Types>(args)... ) ;
    }
} ;

なぜこんなライブラリが必要なのか。関数ポインターを呼び出したければそのまま呼びだせばいいのではないか。一見するとそう思うかもしれない。このライブラリの目的は、非型ではなくて型を受け取るテンプレートに関数ポインターを渡すためのものだ。

当然ながら、関数ポインターは値である。値は型ではない。型ではないものは型テンプレートパラメーターには渡せない。

たとえば、setの比較関数に独自のcompare関数を使いたいとする。以下のように書ける。


struct UserData { /* データ */ } ;
// UserData型を比較する既存の関数
bool compare_UserData( UserData const & a, UserData const & b ) ;

int main()
{
    std::set< UserData, decltype( &compare_UserData )> set( &compare_UserData ) ;
}

これは甚だ冗長だ。かならずcompare_UserDataを呼び出す型ががあれば、setのコンストラクターに関数ポインターを渡す必要はない。そこで、以下のように書ける。

struct call_compare_UserData
{
    bool operator ()( UserData const & a, UserData const & b )
    {
        return compare_UserData( a, b ) ;
    }
} ;

int main()
{
    std::set< UserData, call_compare_UserData > set ;
}

しかし、これでは関数ごとにクラスをつくって引数を転送するだけのボイラープレートコードを書かなければならない。そこで、monostate_functionの登場だ。

std::set< UserData, std::monostate_function< compare_UserData > > set ;

このように簡単に書ける。

また、unique_ptrにデリーターをわざわざ書かなくても、引数さえあうのであれば、monostate_functionが使える。例えば、mallocで確保したメモリはfreeで解放しなければならないが、monostate_functionを使えば、以下のようにunique_ptrのデリーターが書ける。

int main()
{
    std::unique_ptr<int, std::monostate_function<&std::free> >
        ptr( reinterpret_cast<int*>(malloc(sizeof(int))) ) ;
}

なかなか悪くない。

[PDF] P0478R0: Template argument deduction for non-terminal function parameter packs

Variadic Templatesが最後のテンプレートパラメーターではなくても、実引数推定を行えるように制限を緩和する提案。

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

template < typename ... A, typename B > struct A { } ;
template < typename A, typename ... B, typename C > struct B { } ;

Variadic Templatesはテンプレート内に1つでなければならない。

これにより、パラメーターパックの最後の要素を取得したり、引数の順序に自由度が出せたりする。

これは当然入るべきだ。

P0479R0: Attributes for Likely and Unlikely Branches

条件分岐の分岐が実行される頻度をヒントとして与える属性、[[likely]]と[[unlikely]]の提案。

条件分岐で、どちらかの分岐がほぼ実行されることが事前に予測できる場合、これをコンパイラーに伝えると、よりよいコードを生成できる。また、現在の深いパイプライン、高度な分岐予測になったアーキテクチャ上でも、条件分岐の結果があらかじめ予想できるのは都合がいい。

GCCとClangには、__builtin_expectedという拡張機能がある。これを使って、ある分岐の選択が期待できるかどうかをコンパイラーにヒントとして与えることができる。既存のコードを調べたところ、__builtin_expectedを使うコードのほとんどは、

#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

このふたつのマクロだけで用が足りる。現在、likelyとunlikelyを最も使っているのはおそらくLinuxカーネルで、likelyを3000回以上、unlikelyを14000回以上使っている。他にも、Mozillaはlikelyを200回以上、unlikelyを7000回以上使っている。chromiumも数百回以上likelyとunlikelyを使っている。

そこで、分岐が選ばれることが期待できる[[likely]]と、分岐が選ばれないことが期待できる[[unlikely]]というふたつの属性を追加する。これにより、コンパイラーにヒントを与えることができる。

この属性は、conditionに記述できる。

// エラーは通常起こらない
if ( [[unlikely]] check_error() )
{
    do_error_log() ;
}

// 計算は通常ならばすでに終わっている。
if ( [[likely]] is_calculation_completed() )
{
    show_result() ;
}

入るべきだ。

ドワンゴ広告

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

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

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

2016-11-10

C++標準化委員会の文書: P0460R0-P0469R0

[PDF] P0460R0: Flat containers wording

連続したストレージ上に構築したソート済みのデータ構造を持つ連想コンテナー、flat_map/flat_setの文面案。

入るべきだ。

[PDF] P0461R0: Proposed RCU C++ API

物理ページを仮想2ページに分割するクソみたいなオナニーレイアウトを使用したゴミ文書。

内容はRCUライブラリのようだがレイアウトがクソすぎて詳しく読む気がしない。

[PDF] P0462R0: Marking memory order consume Dependency Chains

memory_order_consumeの依存チェインについてLinusが激怒したことが発端の議論の落とし所。

これまたクソみたいなオナニーレイアウトを使用しているため読みづらい。

P0463R0: endian

コンパイル時にエンディアンを取得するこれ以上ないくらいわかりやすいライブラリの提供。<type_traits>に以下の定義が追加される。実装例は以下の通り。


enum class endian
{
    little = __ORDER_LITTLE_ENDIAN__,
    big    = __ORDER_BIG_ENDIAN__,
    native = __BYTE_ORDER__
};

nativeは実装のエンディアンになる。

コンパイラーはバイトオーダーを知っているし、バイトオーダーはコンパイル時に取得できる。

世の中にはバイトオーダーを実行時に変更できるCPUが存在するが、バイトオーダーの実行時の変更に耐えるOSは存在しない。バイトオーダーを変更する関数は提案されていないが、これは提案しない。PDPエンディアンが存在しないが、現在、PDPをターゲットにしたC++14コンパイラーは存在しない。将来、全く新しいエンディアンに対応する必要が生じた場合でも、この設計ならば容易に対応可能である。

P0464R0: Revisiting the meaning of foo(ConceptName,ConceptName)

現在のコンセプト提案では、

R f( ConceptName a, ConceptName b ) ;

という関数宣言は、

template < Conceptname C >
R f( C a, C b ) ;

と書いたものと同等になるが、これを、

template < Coceptname C1, ConceptName C2 >
R f( C1 a, C2 b ) ;

と同等にしようと言う提案。

これは当然こうなるべきだ。

[PDF] P0465R0: Procedural Function Interfaces

一応読んだが、なんとも一言でまとめがたい提案だ。一種の契約型プログラミングなのだろうか。それにしても、コードが冗長になり、しかも関数という単位が断片化され、非常に人間の目によって処理が追いにくくなるのではないか。

[PDF] P0466R0: Layout-compatibility and Pointer-interconvertibility Traits

2つの型がレイアウト互換かどうかを調べるtraits, are_layout_compotible<T, U>の追加

レイアウト互換とは、例えば以下のような型だ。

struct A { int x, y ; } ;
struct B { int x, y ; } ;

void f( A * a )
{
    B * b = reinterpret_cast<B *>(a) ;
}

このようなコードは必要になる。問題は、プログラマーがいかに注意深くレイアウト互換に気をつけようと、後でクラスの定義が変更されてレイアウト互換が壊れた場合、このコードはコンパイル時にエラーにならず、実行時に不可解なエラーとなる。ところで、コンパイラーは型がレイアウト互換であるかどうかを知っている。ならば、型がレイアウト互換であるかどうかを返すtraitsがあれば、このようなコードはコンパイル時に検証できる。

struct A { int x, y ; } ;
struct B { int x, y ; } ;

void f( A * a )
{
    static_assert( are_layout_compatible_v<A, B> ) ;
    B * b = reinterpret_cast<B *>(a) ;
}

これは便利なtraitsだ。

この提案は他にも、以下のようなtraitsを提案している。

is_initial_base<base, derived>

baseがderivedの最初の基本クラスである場合にtrueを返すtraits

struct b1 { int data ; } ;
struct b2 { int data ; } ;

struct d1 : b1, b2 { } ;
struct d2 : b2, b1 { } ;

// true
constexpr bool a = is_initial_base_v<b1, d1> ;
// false
constexpr bool b = is_initial_base_v<b1, d2> ;
template <class S, class M>
constexpr bool is_initial_member( M S::*m ) noexcept;

Sが標準レイアウトクラス型で、Sがunion型か、mがSの最初の非staticデータメンバーである場合にtrueを返す。

struct X { int x, y ; } ;
union Y { int x ; short y ; } ;
int main()
{
    // true
    is_initial_member( &X::x ) ; 
    // false
    is_initial_member( &X::y ) ;

    // true
    is_initial_member( &Y::x ) ;
}
template <class S1, class M1, class S2, class M2>
constexpr bool
are_common_members( M1 S1::*m1, M2 S2::*m2 ) noexcept;

S1とS2が標準レイアウト型で、m1とm2がそれぞれS1とS2のレイアウト内で共通のオフセットから始まる場合にtrueを返す。

struct S1
{
    int x ;
    int y ;
} ;

struct S2
{
    int x ;
    char y[sizeof(int)] ;
} ;

// true
are_common_members( &S1::y, &S2::y ) ;

あれば便利だろう。

P0467R0: Iterator Concerns for Parallel Algorithms

並列アルゴリズムは、既存のアルゴリズムのオーバーロードという形で追加された。既存のアルゴリズムのイテレーターの要件はあまりにも弱すぎて、並列アルゴリズムで使うには問題がある。

入力イテレーターと出力イテレーターは、値に対する具体的なオブジェクトがあるとは限らず、かつマルチパス保証(複数回イテレートして結果が同じこと)がない。並列アルゴリズムで入力イテレーターから入力を得るには、まず入力の個数分のメモリを確保して入力を全部コピーしなければならない。並列アルゴリズムで出力アルゴリズムに順番に出力するには、先頭から出力するよう同期処理が必要だ。

このため、並列アルゴリズムのイテレーターの要件を前方イテレーターに引き上げる。将来的に、イテレーターの要件を引き下げることができる制約や実装が示されたならば、要件を引き下げる。

P0468R0: P0468R0 : An Intrusive Smart Pointer

intrusive reference countingを採用したスマートポインター、retain_ptrの提案。

現在のshared_ptr<T>は、Tへのポインター型とリファレンスカウントのための整数型を別々に確保して管理している。

template < typename T >
class shared_ptr
{
    T * ptr ;
    long * count
public :
    explicit shared_ptr( T * ptr )
        : ptr( ptr ), count( new long{1} )
    { }
    shared_ptr( shared_ptr other )
        : ptr( other.ptr ), count( other.count )
    {
        ++*count ;
    }

    ~shared_ptr( )
    {
        --*count ;
        if ( *count == 0 )
        {
            delete ptr ;
            delete count ;
        }
    }
} ;

これを見ればわかるように、shred_ptr<T>は、ポインターの参照数を管理するためにlong型のオブジェクトを動的確保している。つまり、T型のオブジェクトのためのメモリに加えて、long型のオブジェクトのためのメモリを別々に確保する必要がある。

しかし、もしT型のなかにカウンターがあればどうだろう。メモリ確保は一回で済む。メモリ管理によるオーバーヘッドが減少し、データの局所化によるキャッシュの恩恵も受けられる。


class UserData
{
    long count = 1 ;
    // その他のデータ

public :
    void increment() noexcept { ++count ; }
    void decrement() noexcept { --count ; }
    long use_count() noexcept { return count ; }
} ;

あとは、要素型の中のカウンターの取得、インクリメント、デクリメントをする方法さえ共通化してしまえばよい。この提案では、mixin設計を採用している。

要素型Tは、reference_count<T>を基本クラスに持つことでカウンター処理を提供できる。

class UserData : public std::reference_count<UserData>
{
// その他のデータ
} ;

不自由なMicrosoft Windowsが使っているクソみたいなAPIであるCOMのリファレンスカウントのような独自のリファレンスカウントのAPIに対応するには、retain_traitsを使う。

template < >
struct retain_traits<IUnknown>
{
    static void increment ( IUnknown * ptr ) noexcept
    { ptr->AddRef() ; }
    static void decrement ( IUnknown * ptr ) noexcept
    { ptr->Release() ; }
} ;

retain_ptrはリファレンスカウンターの値が0になっても、自動的にdeleteを呼び出してくれない。deleteを呼び出すのはretain_traits::decrementの役目である。Micorsoft WindowsのCOMの場合、deleteを呼び出す設計ではないため何もしていない。また、リファレンスカウントの値が取得できる場合は、long retain_tratis::use_count() noexceptを呼び出すと取得できる。COMの場合、リファレンスカウンターを変更せずに値を得る方法がなく、またその値もテスト目的のみであるとドキュメントに示されているので、実装しない。retain_traitsにuse_countメンバー関数がない場合は、retain_ptrのuse_countは-1を返す。

様々な場合に対処できる設計になっているのでなかなか悪くない。

ちなみに、retain_ptrはBoost.intrusive_ptrとは全く異なる設計になっている。これは、boostのintrusive_ptrは2001年当時の設計のままで、当時のC++の制約を受けているので、今の進化したC++の恩恵を受けることができないからだ。boostのintrusive_ptrと混同されることを考えて、名前もretain_ptrにしたそうだ。

reference_countの代わりにatomic_reference_countを用いることでリファレンスカウンターの増減をアトミック操作にできる。

また、掴んでいるポインターの解放はreleaseではなくdetachになっている。これはretain_ptrはポインターを所有しているわけではなく、ポインターの解放処理にもかかわらないため、その違いを区別するためにわざと別の名前にしているらしい。

悪くない。

P0469R0: P0469R0: Sample in place

inplace_sampleアルゴリズムの提案。

sample( first, last, out, n, g )は、[first,last)の範囲の値から乱数gを使ってmin( distance(first, last), n )個の標本をoutにコピーするアルゴリズムだ。

inplace_sample( begin, end, n, g )には、コピー先のoutがない。標本は[first,first+min( distance(first, last),n) )の範囲に配置される。イテレーターの参照する型のコピーのコストが重いか不可能で、swapのコストは軽い場合に便利なアルゴリズムだ。

これは追加されるべきだ。

ドワンゴ広告

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

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

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

2016-11-08

C++標準化委員会の文書: P0451R0-P0459R0

P0451R0: P0451r0: Future-Proofing Parallel Algorithms Exception Handling

並列アルゴリズムで要素アクセス関数が例外を投げた時、並列に実行されるので当然複数の例外がキャッチされる可能性があるが、この例外はexception_listという複数の例外を格納するクラスに入れてthrowされるということになっていた。

しかし、このexception_listについて、実装経験がなく規格通りに実装できるかどうかわからないとして、要素アクセス関数が例外を投げた場合は、std::terminate()が呼ばれるように変更された。

この提案では、将来の拡張のために、カスタマイゼーションポイントとしてfail()関数を呼ぶようにしてはどうかと提案している。デフォルトのfailはterminateを呼ぶ。

P0452R0: P0452r0: Binary-Binary transform_reduce(): The Missing Overload

inner_productは規格の定義上並列化できないので、並列化できる設計のtransform_reduceを提案。また、既存のアルゴリズムの流儀に従って同じオーバーロードを提供する。

P0454R0: P0454r0: Wording for a Minimal mdspan

連続したストレージを多次元配列に見せかけるラッパーライブラリ、mdspanの文面案。

名前が暗号的だ。

P0457R0: String Prefix and Suffix Checking

basic_stringとbasic_string_viewに対して、指定した文字列が冒頭、末尾にあるかどうかを調べる、starts_with/ends_withというメンバー関数を追加する。

この機能は極めてよく使う機能であり、また一般的でもある。例えばPythonやJavaの文字列ライブラリは実装しているしQtの文字列ライブラリも実装している。また、最近のQtのコードは1193件のstarts_withと953件のends_withを利用している。他の例を見ても、webkitは304件のstarts_with、142件のends_withを使っている。LLVMは113件のstarts_withと38件のends_withを使っている。

フリー関数ではなくメンバー関数にした理由としては、既存の設計と一貫性があることと、フリー関数にした場合は引数の順序という問題が発生するためである。

using std::literals ;

auto s = "hello world" ;

bool b1 = s.starts_with("hello") ;
bool b2 = s.starts_with("hell") ;
bool b3 = s.starts_with('h') ;

basic_string_viewもしくはcharを引数に取る。

今更追加するのか呆れるが、とはいえよく使う処理ではある。

P0458R0: Checking for Existence of an Element in Associative Containers

連想コンテナーに指定した要素が存在するかどうかを調べるメンバー関数containsの追加の提案。

現状では、連想コンテナーcにある値eの要素が存在するかどうかを調べるには、メンバー関数findを呼び出して、戻り値がc.end()と等しいかどうかを調べることで実装できる。

template < typename C, typename E >
bool contains( C && c, E && e )
{
    return c.find( e ) != c.end() ;
}

このコードは甚だ冗長である。初心者はこのコードを読んでも意図がわからない。また、初心者はこのコードを思いつかず、stack overflowに「連想コンテナーにinsertせずに要素が存在するかどうかを調べる方法ってないの?」という質問をする

c++ - How to check if std::map contains a key without doing insert? - Stack Overflow

この提案により、以下のように書けるようになる。

template < typename C, typename E >
bool contains( C && c, E && e )
{
    return c.contains( e ) ;
}

あまりにも今更な機能追加だが、直ちに追加すべきだ。もともと要望としてはstd-proposalsに上がっていたのだが、誰も公式に提案文書を書いて標準化委員会に提出して国際会議に出席して議論するという労力をかけるものがいなかったのだ。

委員会による設計の欠点の最たる例だ。

[PDF] P0459R0: C++ Extensions for Ranges, Speculative Combined Proposal Document

修正案をマージしたRangeの文面案。

ドワンゴ広告

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

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

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

C++標準化委員会の文書: P0440R0-P0448R0

P0440R0: P0440r0 : Floating Point Atomic View

浮動小数点数に対するatomic_viewの提案

P0441R0: Ranges: Merging Writable and MoveWritable

Range TSでWritableコンセプトとMoveWritableコンセプトがほとんど同じなので、このままにしておくとジェネリックコードが分断されてしまうので、マージしようと言う提案。

P0443R0: A Unified Executors Proposal for C++

様々なC++の提案で必要な実行媒体について、その最小の共通項を抜き出して低レベルなライブラリとする提案。

[PDF] P0444R0: Unifying suspend-by-call and suspend-by-return

コルーチンとresumable関数の統合のための低レベルライブラリの提案。

文書中で、WG21は技術の発明ではなく、既存の技術の標準化を目的としている、この提案は既存の記述の標準化であると言い訳をしているが、どうも発明しているように思えてならない。

[PDF] P0445R0: SG14: Low Latency Meeting Minutes 2016/09/21-2016/10/13

[PDF] P0446R0: SG5: Transactional Memory (TM) Meeting Minutes 2016/07/18-2016/10/10

それぞれ、タイトル通りの会議の議事録

P0447R0: Introduction of std::colony to the standard library

colonyコンテナーの提案。colonyコンテナーは順序保証がないソート可能な非連想コンテナーだ。要素の追加や削除の操作で、削除される要素とイテレーターの終端以外を指すイテレーターは無効化されない。

データ構造的には、複数の要素を格納できる大きさのメモリブロックが複数と、メモリに要素が構築されていないことを示すスキップフラグで構成されている。リファレンス実装では、メモリブロックとスキップフラグを双方向リンクリストで管理している。メモリブロックが空になった場合はメモリは解放される。要素を追加した時に、メモリブロック内に要素を確保できる空きがある場合は、その空きに確保される。このため、順序保証がない。

colony<int> c ;

// 要素を挿入
// 要素を挿入する位置を指定する必要はない。
c.insert( 1 ) ;
c.insert( 2 ) ;
c.insert( 3 ) ;

// イテレーターを取得。値の順番は保証されていない
auto i1 = c.begin() ;
auto i2 = std::next(i1) ;
auto i3 = std::next(i2) ;
auto end = c.end() ;

// イテレーターi2の参照する要素を削除
c.erace(i2) ;

// i1, 13, endは無効化されない

// assertはかからない
assert( i3 == std::next(i1) ) ;

// 要素を挿入、endが無効化される
c.insert( 4 ) ;

利用用途として想定しているのはゲームだ。ゲームでは、多数のオブジェクトがhas-a関係を構築する。たとえば、あるゲーム中の物体を表現するオブジェクトはメッシュやテクスチャやシェーダーやサウンドといったオブジェクトを参照する。このオブジェクトは当たり判定だとか描画だとかを受け持つ様々なオブジェクトから参照される。さらに、オブジェクトはゲームに頻繁に追加されたり削除されたりする。

参照を表現するのに最も都合のいい方法はポインターだ。しかし、オブジェクトを管理するのにvectorなどの既存のコンテナーを使うと、要素の追加削除の際に、イテレーターが無効化されて参照が壊れてしまう。ゲームでは要素の追加削除が頻繁に行われる。

このために、ゲーム業界ではたいてい独自のコンテナーをそれぞれ書いて使っているわけだが、汎用的な実装がほしい。

それが今回提案しているcolonyコンテナーだ。

なかなか良いので、flat_map同様に入ってほしい。

[PDF] P0448R0: A strstream replacement using span<charT> as

廃止されたstrstreamの代替案の提案。固定長のバッファーを使うストリームライブラリは必要であり、近代的な設計により安全に使うことができるというもの。

今更ストリームに労力を注ぐのもどうかと思う。

残りの文書はあと40件ぐらい

ドワンゴ広告

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

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

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

2016-11-07

C++標準化委員会の文書: P0430R0-P0439R0

[PDF] P0430R0: File system library on non-POSIX-like operating systems

<filesystem>はPOSIX準拠のファイルシステムを前提にして設計されているが、POSIX以外のファイルシステムの対応も考慮している。その考慮が浅い部分がいくつかあるので修正案。

P0432R0: Implicit and Explicit Default Comparison Operators

暗黙と明示的な比較演算子を生成する提案。

前回の提案では、比較演算子をそれぞれクラスの定義内で=defaultするものであった。これは極めて冗長であり、プリプロセッサーマクロが書かれることが予想できる。

今回の提案は、operator ==とoperator <を=default、=deleteすると、残りは暗黙に生成してくれる。

  • 明示的に比較演算子を生成する=defaultの文法
  • deleted定義する=deleteの文法
  • 生成された比較演算子は条件次第でconstexprやnoexceptになる
  • aとbが同じ型の場合、a == bはEquality-by-subobjectする
  • aとbが同じ型の場合、a < b はLess-than-by-subobjectする。
  • a == b が定義されていてdeleted定義されていない場合、a != b は!(a==b)として生成される
  • a < b が定義されていてdeleted定義されておらず a > b がユーザー定義されていない場合、a > b は b < a として暗黙に生成される
  • a == bとa < bが定義されていてdeleted定義されておらずa <= bがユーザー定義されていない場合、a <= bはa == bもしくはa < bとして暗黙に生成される
  • a <= bが定義されていてdeleted定義されておらず a >= がユーザー定義されていない場合、a >= bはb <= aとして暗黙に生成される。

Equality-by-subobjectは、operator ==を使ってサブオブジェクト同士を比較するものだ。

Less-than-by-subobjectは、operator ==とoperator <を使ってサブオブジェクト同士を比較するものだ。

例えば、


struct A { } ;

に対しては、以下のような比較演算子が生成される。

constexpr bool operator==(A const &, A const &) noexcept {
    return true;
}
constexpr bool operator!=(A const &, A const &) noexcept {
    return false;
}

以下のコードは、コンパイルエラーになる。

struct B {
    A a;
};
bool operator<(B const &, B const &) = default;

理由は、クラスAはoperator <を提供していないからだ。

以下のように書くと、

struct C {
};
bool operator<(C const &, C const &) = default;

以下のような比較演算子が暗黙に生成される。

constexpr bool operator==(C const &, C const &) noexcept {
    return true;
}
constexpr bool operator!=(C const &, C const &) noexcept {
    return false;
}
constexpr bool operator<(C const &, C const &) noexcept {
    return false;
}
constexpr bool operator>(C const &, C const &) noexcept {
    return false;
}
constexpr bool operator<=(C const &, C const &) noexcept {
    return true;
}
constexpr bool operator>=(C const &, C const &) noexcept {
    return true;
}

以下のように書くと、

struct E {
    int a;
    int b;
    std::string c;
    bool operator<(E const &) const = default;
    bool operator<=(E const &) const = delete;
};

以下のような比較演算子が暗黙に生成される。

inline bool operator==(E const & lhs, E const & rhs) {
    return lhs.a == rhs.a and lhs.b == rhs.b and lhs.c == rhs.c;
}
inline bool operator!=(E const & lhs, E const & rhs) {
    return !(lhs == rhs);
}
bool E::operator<(E const & other) const {
    if (this->a == other.a) {
        return false;
    }
    if (this->a < other.a) {
        return true;
    }
    if (this->b == other.b) {
        return false;
    }
    if (this->b < other.b) {
        return true;
    }
    return this->c < other.c;
}
inline bool operator>(E const & lhs, E const & rhs) {
    return rhs < lhs;
}
bool operator<=(E const &, E const &) = delete;

だいぶマシな提案になった。

P0433R0: Toward a resolution of US7 and US14: Integrating template deduction for class templates into the standard library

C++17にはクラステンプレートのコンストラクターに実引数推定が追加された。これにより以下のように書ける。

template < typename T >
struct A { } ;

// A<int>
A a(1) ;

この機能を追加することにより、既存の標準ライブラリにどのような影響を与え、どのような対応が必要か調査が必要だというNBコメントに応える形で、標準ライブラリすべてを調査した結果、多くのライブラリでdeduction guideが必要であることが判明した。

コンストラクターはテンプレート仮引数名以外の型を使うこともあるので、実引数推定ができないことがある。例えばvectorにはイテレーターのペアを取るコンストラクターがある。


template < typename T, typename Allocator = std::allocator >
class vector
{
public :
    template < typename Iter >
    vector( Iter begin, Iter end ) ;
} ;

このような場合に、deduction guideという文法を使って、型推定のヒントを与えることができる。

template < typename Iter >
vector( Iter, Iter )
    -> vector< iterator_traits<Iter>::value_type > ;

この文書は、標準ライブラリでdeduction guideが必要な場所を列挙している。

[PDF] P0434R0: Portable Interrupt Library

ポータブルな割り込み処理のためのライブラリの提案。

割り込み処理はデバイスドライバーやファームウェアの実装に重要な処理であるが、C++は標準の割り込み処理方法を提供していない。そのため、てんでばらばらな方法で実装されている。そこで、標準の割り込み処理用のライブラリを提供することで、割り込み処理をポータブルに書けるようになる。

device_baseはTriggerというpure virtual関数を持っていて、派生して実装する。割り込み番号やタイマー割り込みを処理できと、大雑把なことが書いてある。

趣旨はわかるが、デバイスドライバーやファームウェアのプログラマーは出力されるアセンブリ言語がわかるほどの低級なコードを好むという偏見があるのだが、果たして標準の割り込みライブラリなど可能なのだろうか。

[PDF] P0435R0: Resolving LWG Issues re common_type

common_typeに持ち上がっている様々な問題を解決すべく、コンセプトを用いたcommon_typeの実装の提案。

問題はコンセプトが入らないことだが。

[PDF] P0436R0: An Extensible Approach to Obtaining Selected Operators

比較演算子の自動生成の提案。

比較演算子を自動的に生成するP0221が却下されてしまったので、別の提案が出てきている。この提案では、P0221の問題のない部分だけを入れる提案だ。

常識で考えて、a == bとa != bの結果は異なるものであるべきだ。また、boolを返すoperator <が比較の意味で定義されている場合、その他の演算子も、x > yがy < x、x >= yが!(x < y)、x <= yが!(y < x)と考えるのが最も自然だ。

ならば、この自動的な解釈だけ提案しよう。つまり、a != bと書いて、boolをoperator ==が定義されていて、operator !=が定義されていない時、a != bは!(a == b)と解釈される。その他も比較演算子も同様。

この提案では、比較の大本であるoperator ==とoperator <を自動的に生成することはないが、このふたつの比較を使って他の比較を自動的に生成する。

[PDF] P0437R0: Numeric Traits for the Next Standard Library

<limits>, <cfloat>, <cmath>に点在する数値の特性を取得するメタ関数を、モダンなtraitsベースの設計のライブラリ、<num_traits>に集約する提案。

例えば、numeric_limits<T>::max()と書いていたものを、num_max<T>::valueもしくはnum_max_v<T>もしくはnum_max<T>{}と書ける。

便利なので追加されてほしい。

[PDF] P0438R0: Simplifying simple uses of <random>

に持ち上がっている数々の提案を寄せ集めたTSを作ろうと言う提案。これ自体には特に内容はない。

P0439R0: Make std::memory_order a scoped enumeration

タイトル通りmemory_orderをscoped enumにする提案。

今のmemory_orderは、C言語風のクソみたいな流儀で定義されている。これは、もともとatomicをCとC++で共通化する目的だったが、C言語はC言語で_Atomicのようなこれまたクソみたいな文法を追加したので、C++としてもmemory_orderをC言語のクソみたいな流儀に合わせる理由は、もはや何もなくなった。

そこで、scoped enumを使う。

現在のクソみたいなmemory_orderの定義

 namespace std {
    typedef enum memory_order {
      memory_order_relaxed, memory_order_consume, memory_order_acquire,
      memory_order_release, memory_order_acq_rel, memory_order_seq_cst
    } memory_order;
  }

これを以下のようにする。

  enum class memory_order {
      relaxed, consume, acquire, release, acq_rel, seq_cst
    };

互換性のために、以下の定義も追加する。


    inline constexpr auto memory_order_relaxed = memory_order::relaxed;
    inline constexpr auto memory_order_consume = memory_order::consume;
    inline constexpr auto memory_order_acquire = memory_order::acquire;
    inline constexpr auto memory_order_release = memory_order::release;
    inline constexpr auto memory_order_acq_rel = memory_order::acq_rel;
    inline constexpr auto memory_order_seq_cst = memory_order::seq_cst;

これは即座に追加されるべきだ。C言語のクソな流儀を取り払え。

ドワンゴ広告

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

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

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

2016-11-05

江添ボドゲ会@11月23日

11月23日の12時から、自宅でボードゲーム会を開催します。

詳しくはconnpassを参照のこと。

江添ボドゲ会@11月23日 - connpass

2016-11-04

PornhubはWebSocketを使ってAdBlockを回避している

BugReplay

あるWeb開発者が、開発のためにchromeで通信内容をキャプチャしたいと考えchrome.webRequestを使ったが、WebSocket経由の通信は得られないことを発見した。さっそくこれをバグ報告した。

その後、インターネット上でわいせつ動画を頒布する大手Webサイトとして有名なPornhubの運営会社であるMindGeek社の社員がこのバグを修正しないようコメントした。

不思議に思って調べてみると、PornhubはWebSocketを使って広告データをやり取りすることで、AdBlock系のブラウザー拡張による広告除去を回避していることが判明した。

なお、この記事を公開して程なくして、AdBlock PlusとuBlock OriginはPornhubに対するWebSocket経由の広告除去も実装した。

技術的に可能であることを示すことと、実際に労力を割いてまで実用的に実装するということの間には、大きな苦労の差があるが、そこまでするとは。

Pornhub Bypasses Ad Blockers with WebSockets | Hacker News

このことについて、Hacker Newsのコメントにコメントを寄せたある人物は、実際にポルノ業界で働いていた人間で、ポルノ業界には世界でも一流の技術者と広告人がいると書いている。ポルノ業界では、ある技術がトラフィックを増やしたり、トラフィックの品質を上げたりする可能性がわずかでもあるならば、その技術を試みるのだという。この人物は、今は世界的な大企業で働いているそうだが、ポルノ業界に比べてなんと保守的でつまらない開発サイクルであり、技術者に裁量が与えられていないかということを嘆いている。この人物はまたあの技術的に魅力的なポルノ業界に戻りたいとは思っているが、ポルノ業界の秘密的でマフィア的な部分には嫌気が差しているとのことだ。

少し前、まだWebサイト上で動画を提供するにはほとんどがFlash Playerを使っていて、YouTubeもサムネイルを実装していなかった時代、なぜYouTubeなどの大手動画サイトはFlashを使いプレイヤー実装も貧弱なのに、ポルノサイトの提供する動画プレイヤーは、いち早くHTML5も提供し、サムネイルも提供し、動画のシークも完璧に動き(当時、YouTubeなどでは動画のシークはあまりよく動かなかった)、画質もよく、その他あらゆる点で優れているのかという疑問がネット上で話題になっていたこともあった。その時は、ポルノ業界は激しい競争に晒されているので市場の原理によりそうなるのだという回答が一番納得の行くものであった。また、ネット上でクレジットカードによる支払いにいち早く対応したのはポルノ業界であるという。

なお、chrome.webRequestがWebSocket経由の通信を補足しないという問題は、すでにパッチが書かれ、いずれ修正される見込みだそうだ。

2016-11-03

Ubuntu 16.10にアップグレードしたらログイン時にディスプレイが点灯しない不具合に悩まされている

Ubuntu 16.10にアップグレードした。

アップグレードは何事もなく終了したようであるが、ログイン時に高確率でディスプレイが点灯しないという不具合に悩まされている。

今使っているコンピューターは、AMDのGPUを積んでおり、Ubuntu 16.04からは、AMDのGPUはAMDの不自由なドライバーを廃止して、自由なドライバーが使われている。Ubuntu 16.04では、OpenGLを使うゲームを起動してしばらく負荷をかけると、急にGPUに問題が発生したらしきエラーメッセージを端末に表示してカーネル自体が止まる問題があったが、日々のテキスト編集作業では何の問題もなかった。

Ubuntu 16.10では、起動に16.04より時間がかかり、かつログイン時に不自然なディスプレイのちらつきがある。時にLightDMのGreeterが表示する場面でディスプレイが消灯してしまう。Greeterが表示されていても、ログインするとディスプレイが消灯することもある。

ディスプレイが点灯しない以外は問題がないので、手探りでリブートを行うと、再びディスプレイの点灯ガチャが引ける。ディスプレイが点灯するかどうかは確率の問題のようだ。一度点灯したままGreeterを突破まで行くと、そのあとは安定しているようだ。少なくとも、現在これを書いている時点で、30分ほどはディスプレイが安定して点灯している。

そして、安定して動作する状態でdmesgをみてみると、AMDのGPU回りのエラーがいくつも表示されている。

画面が見えない状態でリブートする最も確実な方法は、Ctrl+Alt+F1でtty1に切り替えて、ユーザー名とパスワードを入力し、"sudo shutdown -r now"と入力し、パスワードを入力することだ。

さて、Ubuntu 16.10は、GCCのバージョンが6になっている。その他にはあまり目新しい変更はない。デフォルトのClangはまだ3.8のままだ。3.9もパッケージには存在する。libc++の<string>のコンストラクター宣言でnoexceptが一致していない問題もそのままだ。

2016-11-02

C++標準化委員会の文書: P0421R0-P0429R0

P0421R0: Static class constructor

staticクラスコンストラクターの提案。

staticクラスコンストラクターは、一度だけ、main関数の実行前に実行される。

同等のことは、グローバル変数でもできるが、ヘッダーオンリーのコードで実現するためには、提案されている機能が必要になる。

クラスのメンバーという文法である必要があるだろうか。

P0422R0: Out-of-Thin-Air Execution is Vacuous

Out of thin air valueの考察

複数のスレッドで複数のアトミックオブジェクトに対するアトミック操作をmemory order relaxedで行った場合に、値が未規定になることが規格上許されているが、その結果として、絶対に起こりえない値が出てきたらどうするのかという問題。

例えば、2つの初期値がゼロのアトミック変数を複数のスレッドからお互いに間接的に代入し合った結果、人生、宇宙、すべての答えである42がでてくる可能性がある。

Java規格では10年以上も問題になっていて、未だに結論が出ていない問題。

P0423R0: Variable templates for Networking TS traits

Networking TSのtraitsに値テンプレート版(_v)が提供されていないので、提供する提案。

[PDF] P0424R0: Reconsidering literal operator templates for strings

ユーザー定義リテラルのオーバーロードに文字列リテラルを取って文字をそれぞれテンプレート実引数に渡すオーバーロードの追加の提案。

template < typename T, T ... args >
auto operator "" _udl( ) ;

// operator "" _udl< char, 'h', 'e', 'l', 'l', 'o', > ;
"hello"_udl ;

この提案は、C++14にも提案されたが、コンパイル時に文字列を扱う機能が必要だとして却下された。そのような特別な機能は必要ないので当時の提案をそのまま入れるべきだと主張しているのがこの文書。

P0426R0: Constexpr for std::char_traits

string_viewをconstexpr対応させるために、char_traitsのメンバー、length, compare, findをconstexprにする提案。

[PDF] P0428R0: Familiar template syntax for generic lambdas

lambda式にテンプレートを記述できる機能の提案。

[]<typename T >(T x ) { } ;

もともと、C++14に入ったジェネリックラムダの提案に含まれていた機能だが、C++14ではautoを使ったジェネリックラムダだけが入った。しかし、引数の型を扱いたい場合に、autoだけでは不便なので、テンプレートも明示的書けたほうがよい。

[PDF] P0429R0: A Standard flat_map

連続したストレージ上に構築されたソート済みの要素をバイナリサーチすることによる連想コンテナー実装、flat_mapの提案。Boostにあるものが土台になっている。

flat_mapと従来のmapのベンチマーク結果があるが、要素の挿入と削除はとても遅く、イテレートはとても速く、検索はmapより速いがunordered_mapよりは遅い結果となっている。そのデータ構造から考えて予想通りの特性だ。

また、メモリ使用量が少ない。これは当然の話で、mapを素直に実装するには左右の葉ノードと親ノードへの3個のポインターを持つノードによるバイナリツリーになる上に、メモリ確保をノード単位で行うためにメモリ管理のためのメモリも必要になる。また、連続したストレージ上に確保されるためキャッシュの局所性が最高だ。

多くのmapを使うところでは、flat_mapをデフォルトで使ったほうがいいのではないかと思う。

ドワンゴ広告

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

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

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

2016-11-01

C++標準化委員会の文書: P0403R0-P0418R1

P0403R0: Literal suffixes for basic_string_view

string_viewに対するユーザー定義リテラル、svの提案。

std::string_view s = "hello"sv ;

P0406R1: Intrusive Containers

タイトル通り、Intrusiveコンテナーを追加する提案。BoostのIntrusive Containersを土台にしているが、だいぶ簡略化している。

要素型のIntrusiveコンテナーへの対応方法として、Base hookしか提供しない。要素型は、container_name_element(boostならばcontainer_name_base_hookであったもの)を派生することによってIntrusiveコンテナーに対応できる。

Boostにあるauto linkとsafe mode機能は提供しない。

現在標準ライブラリにあるコンテナーに相当するものだけを追加する。つまり、list, forward_list, set, multiset, unordered_set, unordered_multisetだ。メンバーも現行の標準ライブラリのコンテナーにあるものだけを提供する。Boostのようなアルゴリズムやデータ構造が固定された具体的なコンテナーやリッチなメンバーは提供しない。

あれば便利だろう。

P0409R1: Allow lambda capture [=, this]

ラムダ式のデフォルトキャプチャーに=が書かれた場合でも、thisを書けるようにする提案。わかりやすさのために。

P0412R0: Benchmarking Primitives

ベンチマーク用に最適化を抑制するライブラリの提案。

あるコード辺の処理時間を計測するには、以下のようなコードを書く。

auto start = chrono::high_resolution_clock::now();
double answer = perform_computation(42);
auto delta = chrono::high_resolution_clock::now() - start;

問題は、コンパイラーは最適化によってこのベンチマークを意味のないものにしてしまう。関数に渡した値はコンパイル時に計算できるので、コンパイラーは処理をコンパイル時に済ませてしまうかもしれない。また、結果であるanswerが出力に使われていないので、コンパイラーは処理をまるごと消してしまうかもしれない。

さらに、C++規格上、コンパイラーが処理を以下のような順番に並び替えてもよい。

double answer = perform_computation(42);
auto start = chrono::high_resolution_clock::now();
auto delta = chrono::high_resolution_clock::now() - start;

P0409R0では、この問題に対処するため、時間フェンスが提案された。timing_fence()を呼び出すとその前後の処理がリオーダーされないことを保証する。しかし、時間フェンスはコンパイラー実装者によって、実装不可能だと回答された。

そこで、この提案では、最適化阻害のための擬似IOライブラリを提案している。

namespace std {
namespace experimental {
namespace benchmark {

template<class T>
void keep(T &&) noexcept;

template<class T>
void touch(T &) noexcept;

} } }

keepは、実引数に渡されたオブジェクトの内部表現のバイト列を、あたかも(as-if)何らかのデバイスに出力したかのように振る舞う。

touchは、実引数に指定されたオブジェクトの内部表現のバイト列を、あたかも(as-if)何らかのデバイスから入力を得てきたかのように振る舞う。

これにより、C++実装はコンパイル時に値を推定して最適化することができなくなる。

auto start = chrono::high_resolution_clock::now();
int value = 42;
experimental::benchmark::touch(value);
double answer = perform_computation(value);
experimental::benchmark::keep(answer);
auto delta = chrono::high_resolution_clock::now() - start;

実装可能なライブラリとしては簡単だが、使いづらそうだ。漏れがあると最適化がかかってしまう。

P0414R1: Merging shared_ptr changes from Library Fundamentals to C++17

shared_ptrを配列に対応させる変更をLibrary Fundamentals TSから切り離して単独でC++17に追加。

P0415R0: Constexpr for std::complex

std::complexをconstexprに対応させる提案。コンストラクターがconstexprになるのでリテラル型となりconstexpr変数として使うことができるが、演算はconstexprに対応しない。

libstdc++は、std::complexの実装にコンパイラーマジックである__complex__を使っていて、これはコンパイル時定数として扱える。

libc++は、純粋にライブラリとして実装している。<cmath>に依存しているため、演算をconstexpr化できない。

[PDF] P0416R1: Operator Dot (R3)

operator .をオーバーロードできるようにする提案。

目的は、スマートリファレンスを実装できるようにするため。ただし、operator .のオーバーロードを実現するために挙動がとても不可解になっており、理解不可能なほどクソになっている。

こんなクソな提案が行われているのは、これがBjarne Stroustrupのお気に入りの提案だからだろう。Bjarne Stroustrupはもはや現代のC++の発展のために害悪ではないかと思う。Bjarne Stroustrupが関わった最近の機能はだいたい失敗している気がする。コンセプトしかり、dynarryしかり。

P0418R1: P0418r1: Fail or succeed: there is no atomic lattice

言語法家の熾烈な争い。

atomic操作のcompare_exchangeに渡すメモリーオーダー引数、successとfailureについて、規格は、「failure引数はsuccess引数より強くてはならない」としている。この「より強く」とはどのような意味か。規格は「より強く」を定義していない。メモリーオーダー同士を比較してより強いかどうかを判断するための半順序をC++規格は規定すべきだ。

これを受けて、Urbana会議で、relaxedが一番弱く、set_cstが一番強く、その間にいろいろと半順序でメモリーオーダーが入る、以下のようなメモリーオーダーの束を規定した。

relaxed release consume acquire acq_rel seq_cst

しかし、メモリーオーダーの束を規定するこの修正案には問題がある。releaaseとconsume/acquireの間には順序がないが、どのように解釈すればいいのか。そもそもsuccessとfailureにこのような制約を設けることに意味はあるのか。ロードとストアがそれぞれ単独の命令であるようなアーキテクチャでは、命令ごとにメモリーオーダーも異なるわけだ。

そこで、最終的な修正案では、このような制約自体をなくすことにした。これにより、メモリーオーダー同士の束を規定する必要もなくなった。

ドワンゴ広告

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

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

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

2016-10-31

C++標準化委員会の文書: P0165R0-P0293R0

P0165R3: C++ Standard Library Issues to be moved in Issaquah

Issaquah会議で議論されるマイナーな問題集。

[PDF] P0187R1: Proposal/Wording for Bit-field Default Member Initializer Syntax

ビットフィールド付きのデータメンバーにメンバー初期化子を書けるようにする提案。

前回の提案では以下のような文法が提案されていたが、

struct X
{
    int data : 5 = 1 ;
} ;

この文法は曖昧だとして、以下のような文法になった。

struct X
{
    int data : 5 : = 1 ;
    int another_data : 5 : {2} ;
} ;

P0194R2: P0194R2 — Static reflection

様々な型情報、ソースコード情報を取得する静的リフレクション機能の文面。テンプレートメタプログラミングを土台としている極めて使いづらい設計となっている。

P0195R1: Modernizing using-declarations

using宣言をパラメーターパックに対応させる提案。

template < typename ... Funcs >
struct overload
{
    using Funcs::operator () ... ;
} ;

overloadのようなライブラリを再帰を使わずに簡単に実装できるようになる。

[PDF] P0196R2: Generic none() factories for Nullable types

nullableな型のためにnone_t型と、ファクトリー関数none<T>()を追加する提案。ポインターやoptionalなど様々なnullableな型に使える。

[PDF] P0201R1: An indirect value-type for C++

ディープコピーを行う版unique_ptr<T>のような型、indirect<T>の提案。前回の提案では、cloned_ptr<T>と呼ばれていた。


indirect<int> i{ new int(42) } ;
// ディープコピーされる
// 新しいint型のオブジェクトが確保されて値42がコピーされる
auto j = i ; 

// 42
std::cout << *j << '\n' ;

// i, jのデストラクターで所有するint型のオブジェクトがそれぞれ破棄される

unique_ptrはポインターを所有し、コピーを禁止し、デストラクターで破棄する。。shared_ptrはポインターを所有し、コピーはリファレンスカウントにより所有権を共有し、デストラクターでリファレンスカウントを減じて、どこからも参照されていなければ破棄する。indirectはポインターを所有し、コピー時にはディープコピーし、デストラクターでは破棄する。

コピーと破棄には、コピーアーとデリーターによって独自の挙動を設定できる。

int * ptr = static_cast<int *>( std::malloc( sizeof(int) ) ) ;
*ptr = 42 ;
indirect<int> i( ptr,
[]( auto ptr ){
    int * copy = static_cast<int * >( std::malloc( sizeof(int)) ) ;
    *copy = *ptr ;
    return copy },
[]( auto ptr )
{
    std::free( ptr ) ;
} ) ;

// mallocでメモリが確保される
auto j = i ;

// freeでメモリが破棄される

[PDF] P0214R2: Data-Parallel Vector Types & Operations

SIMDやGPGPUなどの並列処理を行うためのベクトル型の提案。

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

ハザードポインターライブラリー、haz_ptrの提案

P0237R3: Wording for fundamental bit manipulation utilities

符号なし整数型に対して、ビット単位のイテレーターを提供するライブラリ。イテレーターを提供することにより、汎用的なアルゴリズムにビット列処理のために渡すことができる。

[PDF] P0249R2: Input Devices For 2D Graphics

2Dグラフィックライブラリのために、GUIプログラミングで一般的なイベント駆動を実現するためにメッセージ処理を行うためのライブラリの提案。

[PDF] P0252R2: Operator Dot Wording

複雑怪奇で理解不可能なoperator .のオーバーロードを可能にする文面案。

このような極めてわかりにくい機能が提案されているのは、おそらくこれがBjarne Stroustrupのお気に入り案件だからだろう。最近のBjarne StroustrupのC++の判断能力は、最近の書籍から判断すると、極めて疑問である。Bjarne StroustrupはC++から引退するべきではないだろうか。

P0261R1: C++ Distributed Counters

高頻度のインクリメントを行うが読み込みはまれなアトミックなカウンターライブラリの提案。

アトミック操作では、インクリメントは読み込みよりコストがかかる。多くのプログラムではカウンターを用いるが、めったに読み込まないカウンターのためにアトミックなインクリメント操作を行うのはスケールしない。そこで、読み込みのコストを犠牲にインクリメントを高速化した分散カウンターが標準ライブラリにほしい。

P0262R1: A Class for Status and Optional Value

値と情報を保有できる、status_valueライブラリの提案。

現在提案されているexpectedは、値か情報のどちらかを保有するライブラリだ。expectedは値と情報の両方を保有したい時には使えない。つまり、

  • 値を保有する。情報は保有しない
  • 値を保有しない。情報を保有する
  • 値を保有する。情報を保有する
  • 値を保有しない。情報を保有しない

というどの状態にもでき、かつoptionalやexpectedのように値がない場合の簡単に書けるようなライブラリを提案している。

こういう目的に特化したライブラリを乱立させるより、C++のコア言語にnullableな値も含む多値を扱える仕組みを入れたほうがいいのではないか。

[PDF] P0279R1: Read-Copy Update (RCU) for C++

RCUライブラリの提案。

P0290R1: P0290R1: apply() for synchronized_value

synchronized_value<T>はT型とmutexを保持するクラスだ。この文書で提案されているapplyは、synchronized_valueをlockして値にアクセスしunlockするための関数だ。

void f( synchronized_value<int> & s )
{
    apply( []( auto value ) { ++x ; }, s ) ;
}

applyは関数オブジェクトと、Variadic Templatesで任意個のsynchronized_valueを受け取る。デッドロックを起こさない方法ですべてロックして、値を関数オブジェクトに渡し、アンロックする。

便利だ。

[PDF] P0293R0: Template deduction for nested classes

クラステンプレート内にネストしたクラステンプレートの実引数推定ができるようにする提案。

趣旨はわかるのだが、すでにC++規格でも標準ライブラリのコンテナーのイテレーターはコンテナークラスの外で定義されるように規定されているので、このような提案が必要になるコードはそもそも扱いづらいので避けるべきではないか。

ドワンゴ広告

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

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

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

2016-10-25

C++標準化委員会の文書: P0009R3-P0099R1

P0009R3: P0009r3 : Polymorphic Multidimensional Array Reference

連続したストレージを多次元配列に見せかけるラッパーライブラリの提案。

柔軟に対応できるようにした結果、すごく使いづらいように思える。

P0019R3: P0019r3 : Atomic View

非アトミックオブジェクトに対してアトミック操作ができるatomic_viewの追加。


// 従来のコード
int data = 0 ;

atomic_view atomic_data( data ) ;

++atomic_data ;

従来のコードをatomic<T>に置き換えるのが現実的ではない場合に使える。

P0020R3: P0020r3 : Floating Point Atomic

atomicライブラリを浮動小数点数に対応させる提案。

P0022R2: Proxy Iterators for the Ranges Extensions

vector<bool>は1ビット単位で要素を格納している。イテレーターは実リファレンスではなくプロクシークラスを返す。イテレーターは実リファレンスを返すことを要件付けられているので、vector<bool>のイテレーターは真のイテレーターではなく、したがってvector<bool>も真のコンテナーではない。

実リファレンスではなくプロクシークラスを使う際の問題は、スワップ操作にあるので、iter_swapをカスタマイゼーションポイントに変えてしまえば解決できる。また、ムーブのためのカスタマイゼーションポイントとして、iter_moveを追加する。

P0037R3: Fixed-Point Real Numbers

固定少数点数、fixed_pointの提案


#include <fixed_point>

int main()
{
    fixed_point< std::uint64_t, 10 > f( 1 ), sum( 0 ) ;
    // tick = 0.0001
    auto tick = f / 10000 ; 

    // 0.0001を1万回足す
    for ( unsigned i = 0  ; i != 10000 ; ++i )
        sum += tick ;

    // 結果は1
    std::cout << static_cast<unsigned>( sum ) << '\n' ;
}

fixed_point< Rep, Exponent >という形で、Repは内部で使用する整数型、Exponentは小数点以下の2進数桁を指定する。

P0040R2: Extending memory management tools

未初期化の生のストレージ上にオブジェクトを構築したりオブジェクトを破棄するための関数テンプレートの追加。トリビアルなデストラクターであればデストラクターを呼ぶ処理をしないなどの最適化が行われる。

[PDF] P0051R2: C++ generic overload function

関数オブジェクトを登録して、登録した関数オブジェクトの間でオーバーロード解決をしてくれるoverloadライブラリの提案。


auto f = overload(
[]( int i ) { },
[]( double d ) { },
[]( std::string s ) { }
) ;

f( 0 ) ;
f( 0.0 ) ;

using std::literals ;

f( "hello"s ) ;

実装はとても簡単。

[PDF] P0057R6: Wording for Coroutines

コルーチンの文面案

キーワードがco_return, co_yeild, co_awaitなのが残念だ。

[PDF] P0059R2: Add rings to the Standard Library

ringバッファーライブラリの提案。前回と違い、ring_spanという名前になった。ring_spanは連続したストレージを受け取り、その上にリングバッファー構造を構築するラッパーライブラリだ。

[PDF] P0098R1:Towards Implementation and Use of memory order consume

タイトル通り、memory_order_consumeの実装と応用の話。もともとは、Linus TorvaldsがC/C++のatomicの仕様にケチをつけたところから始まる。Linuxカーネルでも使えるような実用的なアトミック操作を規格化するために、Linuxカーネルのメモリモデルやアトミック操作の応用を学ぶ文書が多数出た。

[PDF] P0099R1:A low-level API for stackful context switching

レジスターやスタックなどの実行環境ごと保存、リストアするスタックフルコンテキストスイッチを実現するための低レベルなライブラリの提案。

ドワンゴ広告

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

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

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

2016-10-24

C++標準化委員会の文書: N4607-N4617

[PDF] N4607: Toronto Meeting Information

2017年7月の10日から15日にかけてトロントで行われる会議の会場案内。

N4608: PL22.16/WG21 draft agenda: 7-12 Nov 2016, Issaquah, WA, US

N4609: ISO/IEC JTC1/SC22/WG21 N4609

SC22/WG21の議長の事務的な報告書。

2017年11月の7日から12日にかけてIssaquahで行われる会議の日程。

N4609: ISO/IEC JTC1/SC22/WG21 N4609

[PDF] N4610: Working Draft, Extensions to C++ for Modules

モジュールのドラフト。C++17にモジュールは入らないので、現段階ではここまで議論したぐらいの目安にしかならない。

[PDF] N4611: Editor's Report for the Modules TS

モジュールのドラフトの編集者の報告書。

[PDF] N4612: Working Draft, C++ extensions for Networking

Boost.asioを元にしたネットワークライブラリのドラフト。

N4613: Networking TS - Editors Report

ネットワークライブラリのドラフトの編集者の報告書。

[PDF] N4614: WG21 telecon meeting: Pre-Issaquah

Issaquah会議前の電話会議の議事録。

ドワンゴ広告

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

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

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

2016-10-11

C++17のクラスのテンプレート実引数推定

テンプレートは以下のように定義する。

template < typename T >
class C
{
    C ( T t ) { }
} ;

template < typename T >
void f( T t ) { }

テンプレートは以下のように使う。

int main()
{
    C<int> c(42) ;
    f<int>(42) ;
}

しかし、C++プログラマーの大半は、関数テンプレートfをこのように使うことはない。大抵は、f(42)のように使う。このように、テンプレート名を書いた場合、C++コンパイラーはテンプレートの実引数推定と呼ばれる仕組みを使ってテンプレート実引数を推定する。

その詳しい仕組みは難解だが、考え方としてはこうだ。42の型はintである。すると、tの型はintである。tの型はTとされているので、Tはintである。Tはテンプレート仮引数である。するとテンプレート仮引数Tに対する実引数はintである。

テンプレートの実引数推定は、残念ながらC++14ではクラステンプレートには存在しない。しかし、上の例をみるように、クラステンプレートにもコンストラクターがあるのだから、適用できるはずだ。

C++17ではまさにクラステンプレートに実引数推定ができるようになった。以下のように書ける。

C c1(42) ;
C c2 = 42 ;

C++17では、実引数とクラステンプレートCのコンストラクターの仮引数から、クラステンプレートのテンプレート実引数を推定できるようになる。

しかし、C c(42)という形はもっとも簡単なものだ。現実には、以下のような場合もある。

template < typename T >
class C
{
    template < typename Iterator >
    C ( Iterator begin, Iterator end ) ;
} ;

int main()
{
    int a[] = { 1,2,3,4,5 } ;
    // エラー、IteratorからTは推定できない
    C c( std::begin(a), std::end(a) ) ;
}

この場合、IteratorからTは推定できないのでエラーとなる。

この問題を解決するために、C++17では、推定ガイド(deduction guide)という文法が追加される。以下のように書けば、IteratorからTが推定できる。


template < typename T >
class C
{
    template < typename Iterator >
    C ( Iterator begin, Iterator end ) ;
} ;

template < typename Iterator >
C( Iterator begin, Iterator end )
-> C< std::iterator_traits<Iterator>::value_type >

int main()
{
    int a[] = { 1,2,3,4,5 } ;
    C c( std::begin(a), std::end(a) ) ;
}

文法は以下の通り。

テンプレート名 (引数リスト) -> 実引数付きのテンプレート名

2016-10-09

高専プロコンの問題がクソすぎるのでプログラミングを放り出して人力に走るのは最適解であり協賛企業はプログラミングを軽視する企業として唾棄されるべき

第27回高等専門学校プログラミングコンテストが不評を買っている。プログラミングコンテストと名前が付いているのにもかかわらず、本選の上位入賞者は、人力で問題を説いたという。特にコンピューターを持ち込んですらいないチームまでいたという噂まで流れている始末。

なぜそんな残念な結果になるのか、高専生のアルゴリズム力が低いからこうなったのだろうか。この謎を改名すべく、筆者は課題を確認した。

http://www.procon.gr.jp/uploads/procon27/1_Apply27.pdf

課題を要約すると、以下の通りだ。

問題

「一枚の木の板(中密度繊維板)を切り出して、50個以下のピース(凹多面体を含む多角形)に分割する。このピースを枠内で組み合わせて板にせよ。正しい位置のピースの数が得点となる」

制限時間は10分から20分。

その他:コンピューター類を持ち込んでよい。ただし競技ブースに電源はない。外部との通信は認めない。

まるでプログラミングするなと言っているような課題だ。要するに、問題は多角形のパズルを解くものであるが、それは問題の最も簡単な部分である。

ピースは物理的に与えられるので、プログラミングで処理するためには、これを何らかの方法でデータとして表現しなければならない。ピースが十分に薄ければスキャナーが使えるかもしれないが、会場に電源はない。したがってスキャナーのような大電力の装置を稼働させるには、大容量バッテリーか発電機を持ち込む必要がある。

なるべく電力を消費しない方法としては、無地単色の布などを背景に、距離を固定したカメラでピースを一つづつ撮影する必要がある。しかし、制限時間は最大でも20分、ピースは最大50個。撮影装置を一台用意した場合、何のトラブルも起こらずに撮影しても10分から20分の制限時間を超過してしまうだろう。

すると、ピースをすべて並べて一度に撮影して、複雑な画像処理をする必要があるが、カメラの角度がピースに対して正面ではない、レンズの歪み、ピースの加工精度など、様々な要因がピースをデータで表現して処理する際の面倒事となる。

外部との通信が禁じられているので、たとえば画像処理に外部のクラウドサーバーのインスタンスを立ち上げるといったこともできない。

それ以外でも、極めて短い制限時間はやはり問題だ。機器のわずかなトラブルにより、10分、20分程度の時間は容易に失われる。

さて、ここに人間という計算機がある。人間は高精度なカメラ、マイク、スピーカー、多関節脚、マニピュレーターを取り付けられた計算機である。人間は完全に自律移動できる。人間は極めて正確なピースの移動操作ができる。人間のパターン認識能力は極めて高く、完全な乱数列に対して存在しないパターンを見出してしまうほどである。この人間を訓練すれば、パズルを高速に解くことができる。人間はコンデンサの破裂、接触不良、宇宙線によるソフトメモリエラーを起こしたりしない。人間のバッテリー容量はとても高く、外部から燃料を一切供給せずとも長時間稼働できる。

したがって、この問題を解くのに人間を使うのは当然の最適解である。ちなみに、同点の場合、勝敗はサイコロを振って決定される。実際に優勝者はサイコロを振って決定されたそうなので、この戦略は適切であったことが実証されている。

明らかに、この問題と制約(電源なし、制限時間10分、外部通信禁止)はプログラミングコンテストにふさわしくない。この内容でプログラミングをさせたいのであれば、競技ではなく、課題公開から半年から1年ほど問題を解けるシステムを開発させたうえで皆で集まって成果報告会を開くべきである。競技には競技として適切な課題がある。この課題は競技に向かない。

高専プロコンの上位入賞者が人力解答者で占められたのは今年が初めてではない。高専プロコンの課題作成者は競技プログラミングを理解していないとしか言いようがない。この課題は、少し考えただけでも、純粋なプログラミング以外の部分が困難である。課題作成者は自分で課題を解いてみたのだろうか。

そして、この高専プロコンを協賛した企業は覚えておくべきだ。これらの企業は、プログラミングコンテストと銘打ちながら純粋なプログラミング力を競う競技のための課題を作成できず、至極自然で適切な戦略として人力解答者が上位を占めるという失態をしでかしたイベントの恥ずべき協賛者である。これらの協賛企業のプログラミングの認識と熱意と尊敬は、こんなイベントを協賛したレベルである。

全国高等専門学校プログラミングコンテスト - 協賛企業 - 第27回大会

協賛企業

プログラミングコンテストは情報関連企業のみなさまの多大なご協力により運営されています。

これらの企業はプログラミングコンテストの目的や高専教育に対して理解を示して下さっています。皆さんも就職対象として検討されてはいかがでしょうか。

上に引用した内容によれば、これらの協賛企業は、プログラミングコンテストに不向きで人間が最適解である見当違いの課題を設定するこのような謎のイベントの目的に理解を示しているようだ。イベントの実際の内容から考えるに、これらの協賛企業はプログラミングに理解は示しておらず、人間が頑張って単調作業をすることが最適解となることに理解を示しているのだろう。プログラマーの地位と尊厳を重要視する人間はこの事実を踏まえて、就職対象として検討するべきである。

なお、信じられないことに、高専プロコンのWebサイトはこの2016年にもかかわらずTABLEレイアウトを使っていた。繰り返す。TABLEレイアウトを使っていた。お後がよろしくなさすぎる。

2016-09-29

C++17の標準ライブラリの参考書を書く

江添がドワンゴに入社してから、もうかれこれ3年目になる。そろそろ、「江添ごときがC++の規格で飯を食っているのはけしからん。俺はC++の規格にも詳しいしC++コンパイラーも実装できる。俺が代わりにやる」という強者が出てきて私の仕事が楽になって欲しいのだが、残念ながら、そのような状況にはなっていない。より一層のC++の啓蒙活動が必要だ。

ところで、2017年に制定される予定のC++17規格が迫っている。すでにドラフトには多くの新機能が入っている。C++17の参考書を書くのであれば今から始めるしかない。まだ紙の本として出せるかどうかはわからないが、とにかく書き始めることにする。

前回のC++11のコア言語の参考書の執筆と、Bjarne Stroustrupのプログラミング入門書の査読を経て、私はいくつかの教訓を得た。

  • 不必要に堅苦しく難しい文章を書くな。簡潔で必要最小限の文章を書け
  • 参考書のソースコードはファイルを分割しろ
  • 参考書のサンプルコードは自動でテストしろ

私の最初の本も、Bjarne Storustrupのプログラミング入門書も、必要以上に文章が堅苦しく複雑で読みづらかった。これは改善しなければならない。

私の最初の参考書のソースコードは、単一のXHTMLで書かれた。最終的にはファイルサイズが1MBを超えてしまい、テキストエディターでの編集が難しくなってしまった。

Bjarne Stroustrupのプログラミング入門書のサンプルコードは、数割がコンパイルすら通らないお粗末なものであった。これは一度書いたソースコードを手で修正したにもかかわらず、コンパイラーによるチェックを行わなかったためである。参考書は、何度もの修正編集を経て完成する。そのため、参考書の中のサンプルコードは、参考書のソースコードから自動で抽出して自動でコンパイルにかけてテストする仕組みを作る必要がある。

そこで今回、上の3つの教訓を元に、以下のような対策を講じた。

  • textlintを使って日本語の文章に制約をかける
  • Markdownで書いてPandocで他のフォーマットに変換する
  • 参考書のソースコードからサンプルコードを抽出してGCCとClangでコンパイルにかける仕組みを作る

textlintは、node.jsで書かれた日本語の文章を形態素解析して特定のルールに従っているかどうかをチェックするためのツールである。あらかじめ技術文書用のルールが用意されているので、今回はこれをそのまま使う。デフォルトのルールはやや厳しいと思うのだが、文章を簡潔にするための制約として甘んじて受け入れる。

PandocはMarkdownなどの様々な入力フォーマットから、更に多数の様々な出力フォーマットへの変換を行うツールだ。アスキードワンゴ編集部はtexを使っているが、作業用のtexを生成するのもMarkdownからPandoc経由で生成している。

参考書のソースコードからサンプルコードを抽出してコンパイルするテストは、適当なツールがなかったので適当に実装した。C++で書いて200行ぐらいだった。

あとは通常のプログラムと変わらない。参考書のソースコードはテキストファイルであるのでgitでバージョン管理できる。アスキードワンゴ編集部の編集者はgitもGitHubも使えるので、修正作業などはgitとGitHubのPRを経由して行うことができる。ソースコードから参考書のビルドにはMakeを使う。

思うに、すべての作家はgitを使うべきである。また、簡単なコードのひとつぐらいは書けたほうが執筆に必要な作業のやテストの一部を自動化できてよい。

さて、ここまでは理想通りだ。しかし、現実は理想通りには行かない。

私がこれから書くのは、C++17規格に準拠した参考書である。しかし、C++17は2017年の年末になるまで制定されない。当然、執筆中にドラフトの内容が変わっていくので、執筆当時のドラフト段階の規格は、正式な規格とは異なる可能性がある。もちろん、サンプルコードのテストは用意したので、規格準拠のC++17コンパイラーが出れば検証できる。しかし、ここに最大の問題がある。

現在、ドラフトに完全準拠しているC++17コンパイラーは存在しない。したがって、いま執筆している部分のサンプルコードは、コンパイラーによるチェックができない。私の予想では、C++17のコア言語の規格に準拠したC++コンパイラーのリリースには、まだ2年ほどかかると見ている。C++17の標準ライブラリの規格準拠の実装はさらに遅れるだろう。

状況は2009年ごろにC++11のコア言語の参考書を書いていた時と同じだ。ただし、当時と違って今はClangがある。Clangの規格準拠度と実装速度は素晴らしいので、当時よりはマシだ。例えば、Clangは最新の構造化束縛を不完全ながら実装し始めている。

std::tuple< int, float, double > f()
{
    return { 1, 2.0f, 3.0 } ;
}

int main()
{
    auto[ a, b, c ] = f() ;
}

ただし、コンストラクターからのクラステンプレートのテンプレート実引数推定はまだ実装していない。

// std::tuple<int, float, double>
std::tuple t( 1, 2.0f, 3.0 ) ;

そして、ライブラリの参考書の執筆はコア言語以上に難しい。

ドワンゴ広告

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

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

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

XKCD 1739: 問題の修正

xkcd: Fixing Problems

問題の修正

「何をしているんだい?」

「問題を修正しようとして作り出した問題を修正しようとして作り出した問題を修正しようとして作り出した問題を・・・」

titleテキスト:「修正しようとしている問題の最初のきっかけとなった問題は何だったんだ?」、「えっとね、使ってたツールが非効率的で時間の無駄だったんだ」

ついに来たか!

2016-09-25

新しく買ったバリカンが素晴らしい品質だった

バリカンという言葉の語源は、日本に輸入されたバリカンの製造元の社名、バリカン・エ・マール製作所に由来する。社名がそのまま製品名として普及し、一般名詞化してしまったものだ。

私は頭を定期的に剃る習慣を有していたが、この半年ほど剃髪を怠っていた。その理由は自身の怠惰でもあるのだが、現在使っているバリカンが、もう購入してかれこれ7,8年はたっており、すっかりくたびれてしまったからだ。あのジャストシステムの一太郎に対してヘルプアイコン特許訴訟を起こした悪名高いパナソニックのER510Pというバリカンで、髪の毛を吸引する機能が付いている。買った当初は、まだもう少しまともではあった覚えがあるのだが、今となっては剃り味も悪く、吸引機能も著しく劣化している。

とはいえ、髪は伸びている。髪を伸ばしていいことはひとつもない。髪があると夏暑く、汗フケ臭いの元となり、洗髪も煩わしく、なかなか乾かず、寝癖も尽くし、ヘッドフォンを装備するにも邪魔であるし、メガネをかけるにあたってもみあげがメガネのツルに当たる。およそ機能的と言える利点が一つとしてない。

世の中のコンベンショナル・ウィズダムに染まりきった浅はかで非科学的な人間は、髪は防寒の用に立つとか、頭を強く打ち付けた時あるいは物が頭に強く打ち付けられた時髪があることによって衝撃を吸収し怪我を緩和させる、という機能を挙げる。自分の頭髪を剃って比較実験をせず、また科学的に検証された研究結果を出典として参照することなしにそのような主張を行う。

もし、首をマフラーのごとく2,3回も巻けるほど長い頭髪を有しているのであれば、なるほど防寒の用にも立とうが、大抵の人間はそこまで頭髪を伸ばさない。頭髪による頭部への衝撃の緩和の効果はきわめて小さい。

我々近代的な人間は、防寒用の帽子や、防御用の帽子を発明している。まだ石器すら使わない文明レベルの人間ではないのだから、より強力な機能を提供する方法を用いるべきである。

人間は進化の過程で体毛を薄くしたが、頭髪を失わなかった理由はなぜだろうか。自然淘汰は、生存と生殖に有利な特性をひいきする。頭髪は生存の有利な機能を提供していない。しかし、我々の人間社会を観察するに、頭髪の有無は生殖の機会に大きな影響を与えているのではないか。これを科学的に実証するには、千人ほどの被験者に、頭髪を伸ばした状態と剃り落とした状態で、多数の異性に対して生殖活動を行う交渉を持ちかけさせ、その交渉結果を集計して比較すればよい。残念ながら、筆者はそのような科学的な検証が試みられたかどうかを知らないので、この説はまだ証明できない。

それはさておき、近日、たまたまネット上で以下のようなブログ記事を読んだ。

はてなインターンで優勝して,高級バリカン買った - 人権真骨頂

それによると、フィリップスのQC5580というバリカンが、坊主頭にするのにとても性能がよいということであった。@hitode909氏もおすすめの製品であったので、買ってみることにした。

私が買ったのは、マイナーアップデートがされたと思しきQC5582という型番で1万円もした。ヘルプアイコン特許訴訟を起こしたパナソニックのバリカンは、当時8千円ほど支払った記憶があるので、吸引機能というギミック付きのバリカンに比べてもお高い高級バリカンである。そして吸引機能はない。

買ってからも一週間ほど、髪を剃るのが煩わしく使っていなかったが、今日、意を決して髪を剃ってみた。なるほど、この製品の性能はたしかに素晴らしい。あれだけ長かった髪を一瞬で刈り終えることができ、しかも頭皮への損傷が感じられない。これはとてもよいバリカンだ。1万円の価値はある。

ひとつ疑問点を上げると、このフィリップスのバリカンには刃の角度を変えられるギミックがあるが、この機能の必要性はわからなかった。

2016-09-24

カプコンのPC版ストリートファイター5にチート対策として誰でもカーネルモードで任意のコードを実行できるルートキットが仕込まれている問題

Double KO! Capcom's Street Fighter V installs hidden rootkit on PCs • The Register

カプコンのPC版ストリートファイター5のアップデートで、チート防止機能の実装に、rootkitが含まれている問題。

PC版ストリートファイター5のアップデートに含まれているカーネルドライバー、capcom.sysは、IOCTLでサービスを提供する。その挙動は、まずSMEPを無効にし、呼び出し元の指定したポインターの参照するユーザーモードのアドレス空間上のコードを実行し、SMEPを再び有効にする。

とんでもない脆弱性で、ユーザーモードからカーネルモードでの任意のコード実行を可能にするので、rootkitに分類できる。

Supervisor Mode Execution Protection(SMEP)とは、カーネルモードからユーザーモードに割り当てられたアドレス空間のコードの実行を防ぐCPUの保護機能。

この実装者がヤクでもキメていたかのようなイカれた挙動は、ストリートファイター5でメモリ書き換えのチートを防止する機能のために使われているようだ。

カーネルドライバーを実装できるほどMSのドキュメントを辛抱強く読める能力を有したプログラマーが、この挙動がいかにヤバイものであるかを認識できないというのはにわかに信じられないのだが、一体どうなっているのだろう。

なお、カプコンは公式Twitterアカウントで近いうちに修正を出すと発言している。