2013-03-30

Arch Linux、MariaDBをデフォルトに変更

Arch Linux - News: MariaDB replaces MySQL in repositories

二日も前のニュースだが、Arch Linuxが、デフォルトでMySQLのかわりにMariaDBの使用を決定した。

MariaDBは単純に置き換えることができるとされているが、互換性上の懸念から、インストールされたMySQLの自動的な置換はされない。

Arch Linuxは全ユーザーにMariaDBへの意向を推奨している。MySQLは数ヶ月で、公式レポジトリからAUR送りの扱いになるそうだ。

2013-03-29

C++WG 2013-03-pre-Bristol mailingの簡易レビュー

ISO/IEC JTC1/SC22/WG21 2013-03-pre-Bristolが公開された。

ちなみに、今回から標準C++財団のWebサイトであるisocpp.orgの方でも、論文が公開されている。従来、C++標準化委員会の論文は、死んだ木時代(dead-tree era)からの伝統であるISOとANSI/INCITSの規則に基づき、基本的に会議前と会議後に、ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committeeで公開されていた。幸い、JSTORや政府のような情報特権を維持し、文字通り百年も前に書かれた論文すらいまだに閲覧だけで何十万ドルもの購読料を要求するようなくだらない勢力とは違い、今までの論文は1992年からすべて公開されている。過去のMLのアーカイブなども公開する議論があるが、まだなかなか進んでいない。

しかし、納期を定めて論文を集めてまとめて発表というのは、この時代にはあまりにも遅すぎる。もはや、論文を公開、流通させるコストは実質無料になったのだ。論文は書かれた時点で広く公開して世間の評価を求めるべきである。

いや、実際のところ、この2013年は、情報を公開するコストが最も高い時代である。今から50年後の未来、読者は孫に囲まれてこう物語るだろう。

「ああ、そうじゃとも、お若いの。ワシらがお前さんぐらいの時分には、硬い円盤とて、両手に余るほどの大きさなのに数テラバイトと記憶密度の極めて薄く、シーク時間が10ミリ秒もあって、シーケンシャルアクセスすらたったの100MB/secというどうしようもなく遅い記憶媒体を、みんな使っておったよ。全部シーケンシャルコピーするだけでも何時間もかかったのう。いまから思えば信じられんわい。爪の先に印刷可能な容量数百ペタバイトのストレージ、しかも全コピーは数マイクロ秒という今の時代には考えられんことじゃな。しかし、お前さん方には悪いことをしたのう。当時、ニコラ・ガッデム・テスラがトーマス・ドッシュ・エジソンという悪の親玉に宗教裁判で負けて、しかもわしらはエジソンを偉大な発明王よと褒め称えたんじゃから。もしニコラ・ガッデム・テスラが正当に評価されておれば、来年始まる全世界統一無線送電はあと20年早く行われていたかもしれんのう。それから、アーロン・シュワルツの宗教裁判がなければ、今までに書かれた全論文を全人類の小指に印刷する論文アーカイブ計画はもっと早く達成できていたじゃろうて。まあ、ガリレオ・ガリレイの失敗から何も学ばず、当時まだカルト宗教の総本山をのうのうと生かしておいたワシらじゃから、当然の失敗だったのかもしれんのう」

そういうわけで、標準C++財団のWebサイトで、論文が書かれ次第、バラバラに公開するようになった。もちろん、従来の死んだ木時代の慣習も維持されるので、ISO/IEC JTC1/SC22/WG21の方でもまとめて一括して公開している。

[失礼にもPDF]N3525: Polymorphic Allocators

実行時ポリモーフィックアロケーターを追加する提案。

従来のアロケーターは、テンプレート引数経由で渡す、コンパイル時ポリモーフィックアロケーターであり、非ジェネリックなコードからは使えない。しかし、世の中のコードがすべてジェネリックであるわけもなく、これでは使いづらい。そこでこの提案。

実際、今のアロケーターはイケてないにも程がある。まだどのコンパイラーもテンプレートの実装が貧弱だった時代は、コンパイラー付属の標準ライブラリの実装でもアロケーターの汚らしいハードコードが行われていたし、今でもやはり使いづらい。

問題は、コンテナーにアロケーターを指定した場合、

  1. コンテナの要素にもそのアロケーターを適用させたいこと
  2. アロケーターのオブジェクトの型は、ストレージ上に構築する型から独立しているべき

という、至極当然のことができない。1.に関しては、C++11に入ったscoped allocatorで、とりあえずは解決した。あロケーターを直接使わず、allocator tratis経由で使えばの話だが。

だいたい、アロケーターを変えるだけで異なる型になるというのはとても使いづらい。promiseやfutureは、type erasureのテクニックを使ってアロケーターの型を隠している。ただし、type erasureは既存のコードに適用できないし、それにtype erasureのテクニックを使うには、深いC++の知識が必要だ。日本でC++でtype erasureをまともに書ける人間は数えるほどしかいないだろう。それに、書けるからといって、わざわざ書きたいかというと、書きたいわけがない。

というわけで、C++11のscoped allocatorと組み合わせる、実行時ポリモーフィズムを実現するアロケーター、polymorphic_allocator(仮称)を追加する提案。アロケーターを指定するテンプレート実引数にはこれを指定して、アロケーターの実装は実行時に切り替えることができるようになる。

N3526: Uniform initialization for arrays and class aggregate types

アグリゲートの初期化の構文を統一する提案。

アグリゲート(配列かクラスで、特定の条件を満たすもの)は、Cから受け継いだ従来のリスト初期化で初期化できる。

  int aggr_array[2] = {1, 2};

  struct aggr_t {
      int a;
      int b;
  } instance = {3, 4};

アグリゲートを含むアグリゲートの初期化は、以下のようになる。

  int aggr_array[2][2] = {{1, 2}, {3, 4}};

  struct aggr_t {
      struct {
          int a;
          int b;
      } x, y;
  } instance = {{1, 2}, {3, 4}};

とまあ、ここまではいいのだが、クラスと配列を混ぜると、初期化の構文が一致しなくなる。

  struct aggr_t {
      int a;
      int b;
  }  array_of_aggr[2] = {{1, 2}, {3, 4}};

  struct aggr_ex_t {
      int x[2][2];
  };

  aggr_ex_t bad  = {{1, 2}, {3, 4}};      // Error: Too many initializers, see below for details
  aggr_ex_t good = {{{1, 2}, {3, 4}}};

クラスの配列の初期化はいいのだが、配列をメンバーにもつクラスは、{}が余分に必要になる。しかし、根本的には変わらないはずであり、t{}は特に何も機能しておらず冗長で、構文が非統一である。

この問題は、std::arrayを多次元に拡張しようとした際に発見された。現行の文法に従うと、多次元版のstd::arrayの初期化が非常に厄介なことになってしまう。

// 多次元arrayの実装は省略

    // 一次元、統一感あふれる初期化
    int _a[2] = {0, 0};
    array<int, 2> a = {0, 0};

    // 二次元、ネイティブな配列に比べて余分な{}が必要
    int _b[2][2] = {{1, 1}, {2, 2}};
    array<int, 2, 2> b = {{{1, 1}, {2, 2}}};

    // 三次元、もうわけわからん。
    int _c[2][2][2] = {{{3, 3}, {4, 4}}, {{5, 5}, {6, 6}}};
    array<int, 2, 2, 2> c = {{{{{3, 3}, {4, 4}}}, {{{5, 5}, {6, 6}}}}};

さすがにこれは構文を統一すべきだろうというわけで、構文の統一が提案されている。

N3527: A proposal to add a utility class to represent optional objects (Revision 2)

optional<T>の提案。T型を格納しているかしていないクラス。Boostのoptionalが元になっている。

こんなコードは書きたくない。

bool get_value( int & output ) ;

void f()
{
    int value ;
    bool b = get_value( value ) ;

    if ( b )
    {
    // valueには妥当な値が入っている
    }
}

このコードは、valueに妥当な値が書き込まれたかどうか、別の変数を使って確認させている。

こんなコードはもっと書きたくない。

int get_value( ) ;

void f()
{
    int value = get_value() ;

    if ( value != -1 )
    {
    // valueには妥当な値が入っている
    }
}

これは、-1という値が、いわばエラーコードであり、それ以外ならば妥当な値が入っているとみなすものである。

int型やポインター型ならばまだしも、ユーザー定義のクラスの場合は悲惨だ。わざわざ妥当な値が格納されているかどうかを、プログラマーごとに非互換な方法で実装したがる。その結果、非互換な確認方法が余にあふれている。

そもそも、そのオブジェクト自体が特殊な状態を表すことによって、妥当な値を格納しているかどうかを独立して実装するのは非効率的だ。そのため、妥当な値を格納しているかどうかをしめすクラスを標準で定義すべきだ。optionalならば、以下のように書ける。

std::optional<int> get_value() ;

void f()
{
    auto o = get_value() ;
    if ( o )
    {// *oが妥当な値
        auto & valid_value_ref = *o ;
    }
}

optionalのoperator boolが妥当な値を格納しているかどうかを返し、operator *が格納している値を返す。

N3528: Minutes of Feb 5 2013 SG1 Phone Call

並列ループのクリティカルセクションに関する電話会議の議事録。

[失礼なPDF]N3529: SG5: Transactional Memory (TM) Meeting Minutes 2012/10/30-2013/02/04
[不快なPDF] N3544: SG5: Transactional Memory (TM) Meeting Minutes 2013/02/25-2013/03/04

Transactional Memoryに関する会議の議事録。

[失礼極まりないことにPDF]N3530: Leveraging OpenMP infrastructure for language level parallelisation

言語による並列プログラミングを、既存のOpenMPバックエンドで実装するとしたらどのようになるかという論文。もちろん、言語に組み込むからには、プロプロセッサではなくてキーワードが付加される。

[不敬罪が適用されるべきPDF]M3531: User-defined Literals for Standard Library Types (version 3)

多くの標準ライブラリに便利なユーザー定義リテラルを用意する提案。

N3532: C++ Dynamic Arrays

実行時に長さを指定する動的配列クラスdynarrayの提案。

std::vectorは便利だが、必要以上に汎用的で、格納できる要素数を増減できる。実行時の配列を作る際に長さが決定、それ以上増やす必要がない場合、std::vectorは鶏を割くのに牛刀を用いるの感がある。そこで、実行時に固定長を指定できる標準ライブラリdynarrayの提案。

コア言語の配列自体を実行時に要素数指定できるようにしようという提案もあるが、こちらはライブラリでの実装の提案となっている。かねあいはどうするのか。もちろん、コア言語の配列とライブラリは違うわけで、範囲チェックが行えたりアロケーターを指定できたり、その他諸々のライブラリならではの利点もあるわけだが。

N3533: C++ Concurrent Queues

標準ライブラリconcurrent queueの提案。リファレンスを返すdequeとは違い、値を返す。これにより複数のスレッドから同時にアクセスできるよう実装できる。また、失敗する可能性があるNon-WaitingやNon-Blockingな操作も提供。

N3534: C++ Pipelines

伝統的に、UNIXではプロセスとパイプを使ってマルチスレッドプログラミングを気軽に行なっている。例えば、ログファイルからエラーメッセージを抽出して、特定のメールアドレスだけに絞り込み、結果を整形するには、以下のように書ける。

cat log.txt | grep '^Error:' | grep -v 'test@example.com' | sed 's/^Error:.*Message: //' > output.txt

これはマルチスレッドパイプラインである。それぞれのプロセスは独立して並列実行される上に、デッドロックやデータ競合や未定義動作は一切存在しない。

もちろん、すべてのマルチスレッド処理が、このようなパイプライン処理に落とし込めるわけではない。しかし、パイプラインに落とし込めるような処理は、このように書けばとても簡潔かつ安全に実装できる。

残念ながら、C++にはこのようなマルチスレッドパイプラインを手軽に書けるライブラリが存在しない。mutexやthreadといった基本的すぎるライブラリを駆使してパイプラインを実装するのは苦痛だ。そこで、パイプラインを手軽に実装できるライブラリを提案する。

具体的には、利用者が入力元、出力先、そして間の入力を受け取って出力を返す機能を実装すれば、残りのパイプ処理やスレッドプール実行などの処理を引き受けてくれるライブラリということになる。

処理は、expression templateの技法により、f1 | f2 | f3 のようにわかりやすく記述できる。

これはすばらしいライブラリだと思う。ぜひとも入って欲しい。

N3535: C++ Stream Mutexes

ストリームに排他的処理を追加する提案。現行のストリームは競合しないことが保証されているが、出力結果がどうなるのかはわからない。ストリームに排他的処理の機能を提供することで、出力結果を保証できるようにする拡張の提案となっている。

N3536: C++ Sized Deallocation

クラスのメンバーのoperator deleteをオーバーロードする際には、ストレージのサイズが引数として渡される。しかし、グローバルなoperator deleteをオーバーロードしても、サイズは渡されない。これにより、近代的なアロケーターのテクニックである、小さな単位のストレージには、ストレージに直接サイズ情報を埋め込まず、同じサイズごとに共通のヒープを用意してそこから確保するという実装のパフォーマンスが低下する。というのも、ストレージのアドレスからヒープを検索しなければならないからだ。

というわけで、従来の宣言に加えて、以下のグローバル名前空間のoperator deleteがオーバーロードできるようになる。

void operator delete(void*, std::size_t) throw();
void operator delete[](void*, std::size_t) throw();

これもぜひ入るべき提案である。

N3537: Clarifying Memory Allocation

規格を厳密に解釈すると、アロケーターはメモリ確保要求の前後のつながりから、動的に挙動を変えてはならないとしている。また、すべてのメモリ確保要求に対応するよう、内部的な確保関数の呼び出しを行わなければならないとも読める。

実行時の特性に合わせて、メモリ確保の戦略を変えるのは、アロケーターの実装として妥当なものであり、実装例も多数ある。重要なのは、アロケーターは利用可能なメモリーを返すことであって、その内部ではない。したがって、このような制約は廃止すべきだという提案。

N3538: Pass by Const Reference or Value

プログラマーはパフォーマンスを重要視する。そのため、機械的に値渡しよりconstなlvalueリファレンスで渡すことを好む。リファレンスというのは、内部的にはアドレスを渡しているわけであり、多くのアーキテクチャでは、int型のような基本的な型は、アドレス渡しと全く同じレジスタ経由で渡されるため、パフォーマンスは変わらない。むしろ、アドレス渡しには、エイリアシングの問題もある。Cのrestrictは問題を完全に解決しない。従来、プログラマーはエイリアシングの問題を単に無視してきたし、よくてドキュメントにエイリアシングをサポートしないと書くぐらいだった。エイリアシングに対応するためにアドレスを調べて分岐を書くと、近代的な深いパイプラインのアーキテクチャでは、値渡しか参照渡しかという違いが吹き飛んでしまうほどのペナルティが発生する。

どの型ならば値渡しでも効率がいいのかということは、環境ごとに異なるので、移植性の高いコードで書くことはできない。さらに、ABIにも関係するので、そう簡単に環境ごとに変えるわけにもいかない。

そこで、最適な渡し方を、コンパイラーに決めさせるための構文を用意しようという提案。

それにしても、提案されている文法がキモい。宣言子の文法を拡張しているので、あたかもポインターとして*を指定するように、|を指定する。

type f( const type | input ) ;

N3542: Proposal for Unbounded-Precision Integer Types

無制限精度整数型の提案。つまりは、メモリの許す限りの精度で計算できる整数ライブラリということだ。

普通、このような基本的なライブラリはとっくの昔に標準で入っているべきなのだが、むしろC++だからこそ、なかなか標準では決まらなかったライブラリとも言えるだろう。Boostでも、この機能を提供するライブラリは、似たようなものがいくつもレビューに書けられ、その都度否定されるという歴史をたどってきた。去年、ようやくBoostでMultiprecisionがレビューを通過して採択されたそうだ。

もちろん、C++用のライブラリ、あるいはC++から使えるCのライブラリで、やれFFTだアセンブリだといった最先端で最速の実装は多数ある。問題は、モダンなC++にふさわしい洗練されたインターフェースを提供しているライブラリとなると、これがなかなか難しい。演算子のオーバーロードがあるC++では、むしろ作りやすいはずなのに、やはりC++は難しいのか。あるいは、数学者というのは、見た目がきれいだが難しい言語より、見た目が暗号的だが習得の簡単な言語を好むのか。大規模な数値計算の分野で、Pythonが多用されているのも、やはりわかりやすさのためなのか。

[屈辱的なPDF] N3543: Priority Queue, Queue and Stack: Changes and Additions

Priority queue, queue, stack, heapは、すでにコンテナーアダプターとして提供されているが、機能が貧弱すぎる。そこで、新しいアダプター群を追加する提案。どうやら従来のアダプターを拡張するのは無理だと判断したらしい。

ヒープへのイテレーターを追加し、ヒープの要素を変更できるようにし、ある種のヒープに対する効率的なマージも提供し、またヒープをソートを安定ソートにできるようにし、ヒープ同士の比較もできるようにしている。

[不愉快なPDF] N3545: An Incremental Improvement to integral_constant

integral_constantにconstexprなoperator()を追加する提案。これにより、いままで、integral_constantを返すtype traitsを使うには、

std::is_arthmetic<T>::value

としなければならなかったのが、

std::is_arithmetic<T>{}()

と書けるようになる。

これはすばらしい提案だ。ぜひとも入るべきだ。

[不当なPDF] TransformationTraits Redux

メタ関数をエイリアステンプレートでラップする提案。わざわざnested typeを指定する必要がなくなる。

従来のメタ関数の戻り値は、nested typeで受けるしかなかった。

meta_function<T>::type

しかし、C++11にはエイリアステンプレートがある。エイリアス宣言でメタ関数をラップすれば、冗長なnested type指定は省略できる。

template < typename T >
using meta_function_t = meta_function<T>::type ;

こうすれば、meta_function_t<T>とかいただけで、meta_function<T>::typeと同等になる。

これは、私も以前から考えていたものであり、当然入るべきだと思っていたのだ。C++11では時間が足りなかったのが残念なくらいだ。

[虫唾の走るPDF] N3547: Three <random>-related Proposals

乱数ライブラリ関連の提案。

一つ目は、algorithmにsampleを追加。C++11のドラフトの段階で、sampleとsample_nを追加する提案があった。これは、SGIのSTL実装にあったもので、それぞれ有名なsamplingのアルゴリズムである、algorithms S (“selection sampling technique”) and R (“reservoir sampling”)の実装である。詳しくはDonald E. Knuth: The Art of Computer Programming, Volume 2: Seminumerical Algorithms (Third Edition)の§3.4.2を参照。

個人的には、このようなアルゴリズムを自力で正しく書けるわけがないので、あると嬉しい。議論の結果、時期早尚なので見送りになった。

今がその見直しの時だというわけで、新しい設計のsampleが提案されている。なんでも、アルゴリズムによって関数テンプレートを分けるのではなく、イテレーターの種類によってアルゴリズムをtag dispatchingによって選択するようになっている。つまり、入力イテレーターがrandom access iteratorの場合はアルゴリズムRを使い、そうでない場合はSを使う。

そのほか、この論文にはやたらとKnuthネタが多い。まあ、randomからして、標準にknuth_bという引数定義済みのtypedef名(The Art of Computer Programmingで説明されているアルゴリズムと一致するもの)があるぐらいなのだから、Knuthネタがまじるのも当然といえる。

二つ目のrandom自体に対する変更は、もっと初心者フレンドリーにしようということで、グローバルなURNGオブジェクトを返す関数(global_urng)とその初期状態ランダム化の関数(randomize)、さらには範囲を指定したint型とdouble型の値を返す関数(pick_a_number)を用意している。

論文では、randomに寄せられた苦情として、あまりにも初心者向けではないというものが多かったらしい。randomについては私もいくつか記事を書いているが、その記事にはてなブログとかTwitterなどで間接的に寄せられた感想は、「めんどくさすぎる。もっと簡単な方法はないのか」であった。randomの設計はすばらしいのだが、どうも初心者には汎用的すぎるのだろう。

グローバルなURNGとその初期状態のランダム化は、当然用意されているべきだと思う。たしかに、URNGの状態をクラスのオブジェクトとして持てるのは便利だが、プログラム中で共有できるURNGオブジェクトは当然欲しい。その初期状態のランダム化方法も標準で用意されていて欲しい。

pick_a_numberはどうかと思うが、いちいちuniform_distributionの特殊化のオブジェクトを作るというのは、たしかに初心者には面倒に感じるかもしれない。

例えば、六面ダイスのプログラムを実装してみよう。このプログラムは、実行するとランダムで1から6までの数字のうちどれかを出力して終了するものとする。このプログラムは以下のように書ける。このコードのバグに注意、私は試しただけで、正しく動くことを証明していない(Knuth先生ごめんなさい)

#include <iostream>
#include <random>

int main()
{
    std::default_random_engine engine ;

    std::random_device rd ;
    engine.seed( rd() ) ;

    std::uniform_int_distribution<int> dist( 1, 6 ) ;

    std::cout << dist( engine ) << std::endl ;  
}

このコードを書くには規格書を読まなければならなかった。また、このコードを理解するには、randomの設計思想、すなわちエンジンとディストリビューションというコンセプトについて理解しなければならない。また、random_deviceというクラスの理解も必要だ。どうもジェネリックなものの考え方が要求される。それに、もっと複雑なプログラムとなると、やはりプログラム中で共通なエンジンのオブジェクトがほしくなり、グローバルなエンジンのオブジェクトにアクセスできるための何らかの手段を講ずるだろう。

この提案では、上記のコードは以下のように書ける。

#include <iostream>
#include <random>

int main()
{
    std::randomize() ;
    std::cout << std::pick_a_number( 1, 6 ) << std::endl ;    
}

多くの初心者の乱数の需要はこの程度で足りるのだろう。

3つ目の提案。これは私にとっては当然だが、いろいろと反発が予想されそうな提案だ。ズバリ、cstdlibとstdlib.hの乱数関連のものを、deprecated扱いにしてしまおうというものだ。

個人的には、これは当然の処置である。C++11で追加されたrandomは圧倒的に既存の貧弱なrand(笑い)を凌駕しており、もはやrandは使われるべきではないのだ。

また、algorithmのrandom_shuffleもdeprecatedにしようと提案されている。かわりに新しく追加された、randomフレンドリーなshuffleを使うべきである。

randomが初心者フレンドリーではないというのはよく言われることであるし、まあ、悪くない提案ではないかと思う。

[これもPDF] N3548: Conditionally-supported Special Math Functions for C++14

C++11では採用されなかった数学用関数を、C++14に、"conditionally-supported"として追加する提案。実装はこの関数を提供する義務はない。

この分野には疎いのでよくわからない。

[意味もなくPDF] N3549: s/bound/extent/

いままで、歴史的に、配列にはboundという言葉を使ってきたが、この言葉は、extentと言ったほうがいい。そのため、規格の文面における配列のboundという言葉をextentに置き換える、文面の一貫性を改善する提案。

この手の用語の統一は度々起きてる。

[無神経なPDF] N3550: Proposed C++14 Value Classification

lvalue/xvalue/prvalueの分類の定義の文面を大幅に変更する提案。正直value categoryは鬼門だ。

[PDFは帰れ] N3551: Random Number Generation in C++11

まるでrandomの入門書のような論文。ランダムの設計思想である、engineとdistributionの明確な分離やその使い方を説明している。最後にN3547での提案内容に触れている。特に目新しいことが書いているわけではない。

まあ、randomの入門用にはいいと思うが、C++WGの論文を参考書がわりにする変わり者がどのくらいいるのか。

ただ、現状でC++11を学ぼうと思ったら、規格とC++WGの論文を読むのが一番だが。

[なぜ複雑なレイアウトの必要がないのにPDFを選択するのか] N3552: Introducing Object Aliases

オブジェクトエイリアスの提案。

昔々、N1785でオブジェクトテンプレートが提案された。この提案は受け入れられるなかったが、いまのエイリアス宣言の文法を流用して、オブジェクトにもエイリアスという概念を持ち込もうという提案している。

古くはゼロックスのFortranマニュアルにすら、こう書かれている。

DATA文の主目的は、定数に名前を与えることである。円周率を使う場面で毎度3.141592653589793と記述するより、DATA文で変数Piにその値を与えて、定数として使うことができる。これは、円周率の値を変更する必要に迫られた時も、プログラムの変更を容易にする。

円周率が定数だとか変更するものだとか書かれているのはなかなかおもしろいが、プログラミングの世界では、定数に名前をつけることは良い習慣であるとされている。有効精度やハードウェアの制約や、あるいは新しい計測方法によって既知の値より高い精度が求められた場合など、定数の定義を変更するだけですむ。

問題は、C++における定数の定義は、型が固定されているということだ。プリプロセッサーマクロにしろ、コンパイル時定数となる変数にしろ、型が固定されている。

#define pi 3.141592653589793 
constexpr double pi = 3.141592653589793 ;

このように定数を定義すれば、doubleの時はいいが、floatとかlong doubleとかintとか、他の型で計算するときも、定数の方はdouble型になってしまう。あるいは、別の名前をつけるという手もある。たとえば、double型はpi、float型はpif、long double型はpilなどと別の名前をつける。しかし、この方法では型に対して汎用的なコードを書くことが出来ない。

そのため、オブジェクトをテンプレート化できるようにしてはどうか、というのが、オブジェクトテンプレートの動機だ。

C++11の今日では、メタプログラミングが発達し、constexprができ、エイリアス宣言やそのテンプレートであるエイリアステンプレートという機能もある。この機能を組み合わせて、オブジェクトをエイリアステンプレート化できるようにしてはどうか。例えば、

template < typename T = double >
constexpr T & pi_constant()
{
    static constexpr T value = static_cast<T>(3.141592653589793L) ;
    return value ;
}


template < typename T = double >
using pi = pi_constant<double>() ;

template < typename T >
T area_of_circle( T radius )
{
    return pi<T> * radius * radius ;
}

このようにすれば、あとはpi_constantテンプレートを変更するだけで、どのような型にも対応できる。定数piはオブジェクトエイリアスとなり、型に対して汎用的に使えるようになるのだ。

もちろん、これは関数テンプレートのシンタックスシュガーに過ぎず、constexprがある今、既存の関数テンプレートでも十分に実現できることだ。ただし、関数テンプレートの場合、関数呼び出しの()を書かなければならない。

template < typename T >
T area_of_circle( T radius )
{
    return pi<T>() * radius * radius ;
}

しかし、この問題について調査したところ、プログラマーは皆、関数呼び出しの()は冗長だと回答した。たかが定数にアクセスするがごときに括弧を書くというのは、良くて「不自然」、悪くて「不快」だという。数学的には、定数とは無引数関数として表現できるが、プログラマーとしては、コードを書くが如きにそんなモデル化は受け入れ難い。

あまりに単純すぎるシンタックスシュガーをあまり好まない私としては、どうも納得できない。というのも、結局<T>で型実引数を与えなければならないわけだから、()を書かなくていいぐらいの違いはささいなことだとおもうのだが。

ただし、すでに型に対するエイリアスがあり、型を返すメタ関数をエイリアステンプレートでラップすれば、nested typeを指定しなくてもいい以上、オブジェクトに対するエイリアスを追加するというのも悪くないかもしれない。

[ぴーぴーうるさいひよこたちに挨拶でもしてやろうかな] N3553: Proposing a C++1Y Swap Operator

スワップ演算子の追加。

なんと演算子の追加である。近年、多くの研究者が、スワップはコピーやムーブと同じく基本的な操作であり、むしろコピー代入よりも基礎的な操作であると主張している。実際、ムーブの概念が導入された今、スワップを基本的な操作とみなすのは、理にかなっている。

そのため、スワップ演算子の追加を提案する。

現在の提案では、演算子は、operator :=: が提案されている。つまり、スワップは以下のように書く。

int a = 1 ;
int b = 2 ;

// a == 1, b == 2
a :=: b ; // スワップ
// a == 2, b == 1

スワップ演算子の優先度は、代入演算子と同じく、ほぼ最下位になる。また、評価結果は、左辺オペランドとなる。

他の演算子と同じく、非クラス型にはデフォルトのオーバーロードが提供される。クラスはスワップ演算子を暗黙に生成される特別なメンバー関数として持つ(条件次第でdeleted定義される)

その他、他にも多数の細かい規則があるが、演算子であるがゆえの規則だ。

ライブラリとしては、スワップ可能かどうかを返すis_swappable<>やis_nothrow_swappable<>が追加される。

これは採用すべきだ。

[ピーピーディーエフエフ] N3554: A Parallel Algorithms Library

algorithmを並列版に拡張する提案。従来のalgorithmは暗黙にシーケンス実行版であり、std::seq, set::par, std::vecというポリシーベースのタグディスパッチにより、明示的にシーケンス実行、パラレル実行、ベクトル実行を指定する。

void f( std::vector<int> & vec )
{
    // 従来のソートは暗黙にシーケンス実行
    std::sort(vec.begin(), vec.end());
    // 明示的なシーケンス実行のソート
    std::sort(std::seq, vec.begin(), vec.end());
    // パラレル実行のソート
    std::sort(std::par, vec.begin(), vec.end());
    // ベクトル実行のソート
    std::sort(std::vec, vec.begin(), vec.end());
}

シーケンス実行というのは、従来通りの実行だ、パラレル実行は、処理を複数のスレッドに分散することを許可するものである。ベクトル実行は、パラレル実行と同じだが、イテレーターやファンクターに強い制約を課すことにより、実装によるさらなる最適化を許すものである。

さらに、実装はこれ以外の実行モードを独自拡張として提供することもできる。

今回の提案では、識別子をparallel_から初めたり、あるいは別の名前空間std::parallel::を使うのではなく、タグで実行モードを決定する。これにより、実行モードを切り替えることが可能になる。例えば、コンパイル時に型に応じて実行モードを切り替えたり、実行時に要素数に応じて実行モードを切り替えたりできる。

論文では、既存の実装による利用例をすべて補足できるようにしたことや、新しく追加するアルゴリズムや、既存のアルゴリズムをどのように並列実行に対応させるのか、あるいは対応させないアルゴリズムなど、様々なことについて細かく解説している。

N3555: A URI Library for C++

なぜか論文が公開されていない。N3507: A URI Library for C++の改訂版だそうだが。

[こいつもPDF] N3556: Thread-Local Storage in X-Parallel Computations

並列実行がC++に入る機運が高まっている。秘匿に並列実行と言っても、様々なものがある。命令単位で並列のSIMDから、自動的にスレッドプールに分散されるものや、あるいはGPUのような並列実行用のハードウェア上で実行されるものや、あるいはプロセス単位などなど。

この論文では、そのような様々な並列実行の文脈で、スレッドローカルストレージ(TLS)へのアクセスはどうなるのか。動作は保証されるのか保証されないのか。あるいは利用はできるがその利用法が制限されるのか。などのことについて考察している。そして、TLSの動作保証としてありうる5段階のレベルと、用語の定義を提案している。

[またPDFだよ] N3557: Considering a Fork-Join Parallelism Library

並列プログラミングのうち、fork-joinのライブラリによる実装の考察。

並列プログラミングは、次期C++の主要な機能のひとつになるだろうと思う。この論文の筆者Pablo Halpernは、もともとfork-joinを言語側でサポートするべきだという意見を表明していたが、色々と反対意見が多かったため、とりあえずはライブラリ実装の設計を考察している。

考察結果としては、ライブラリで実装するには、引数渡しの方法や遅延戻り値を実現する言語機能が欠けており、例外の扱いも面倒で、まあ、実際にライブラリ実装はたくさんあるし有用性も証明されているがなんともかんともと、直接はっきりとは書いていないものの、どうも筆者のライブラリではなく言語側でサポートしたい意思が伺える内容になっている。

[PDF有害論]N3558: A Standardized Representation of Asynchronous Operations

futureに非同期処理を追加する提案。futureは非同期だが、結局結果を取り出すgetはブロックする。

この提案では、futureに多数の機能を付け加える。たとえば、futureが完了次第、次の動作に受け渡すthen、futureがネストしている場合に一気に値を取得するunwrap、値がブロックせずに得られるかどうかを確かめるready、複数のfutureをまとめて、どれかひとつが完了するか、あるいは全部が完了するまでまつwhen_any/when_all、あるいはreturnせずに途中で値を用意するmake_ready_future。

非同期I/Oをかじった経験からみると、when_anyやwhen_allが今までなかったのは信じられない。

[びっくするほどPDF! びっくりするほどPDF!] N3559: Proposal for Generic (Polymorphic) Lambda Expressions

ジェネリックlambdaの提案。たぶんC++14に入る。

lambdaの何がジェネリックになるかというと、引数だ。従来、以下のような関数に渡すlambda式を書けなかった。

template < typename Func >
viod f( Func func )
{
    func( 0 ) ;
    func( 0.0 ) ;
}

これは、lambdaがジェネリックではなく、仮引数の型を明示的に指定しなければならないからだ。しかし、従来のクラスによる関数オブジェクトは、普通にジェネリックにできるのだ。

struct Func
{
    template < typename T >
    void operator ()( T t ) const ;
} ;

クラスでできるのだから、lambdaでも当然できてしかるべきだ。ジェネリックlambdaでは、これができるようになる。

C++11には時間がなかったので入らなかった機能だが、色々と文法上の問題が多い、例えば、もっとも簡単な、型を省略するという文法は、問題が多い。

[]( value ) { } ;

今回の提案では、autoを使うことになった。

[]( auto value ) { } ;

また、少しでも反対意見のあった機能は削られた。つまり、N3418で提案されていた、lambda式の本体省略は、今回の提案からは外れる。

// N3418で提案されていた本体省略機能
[]( value ) value + 1 ;

これは、今の提案で書きなおすと以下のようになる。

[]( auto value ) { return value + 1 ; }

どうやら、世の中のlambda信者は、わずかなタイプ数の増加にも耐えられない人種らしく、冗長な文法を極端に嫌う。おそらく、今回のautoキーワードの記述強制は、禿げ上がるほどのストレスを与えることだろう。

それから、関数テンプレートをポインター型にキャストしたときにインスタンス化されてポインターに変換されるように、キャプチャーなしジェネリックlambdaもポインター型に変換できる。

auto (*p) ( int, int ) -> void = []( auto a, auto b ){ }  ;

また、Clangベースの実験的な実装が公開されている。

Generic Lambdas in C++ using Clang by faisalv

[汚らしいPDF] N3560: Proposal for Assorted Extensions to Lambda Expressions

lambda式の改良案4つ。これもいくつかは上記のClangベースのジェネリックlambda実装で実験的に実装されている

1. lambda式でテンプレートの文法が使えるようにする(実装済み)

ジェネリックlambda式を導入するからには、やはり明示的にテンプレート仮引数を書きたい場合もある。そのために、使い慣れたテンプレートが書けるようにする。

auto f = []< typename T >( T t ) { return t ; } ;

lambdaキャプチャーと実引数リストの間に記述する。

2. lambdaの本体を式にできるようにする(実装済み)

せっかくlambdaなんだから、return文ひとつですむようなlambda式に、わざわざ冗長な{}を書きたくない。そのため、lambda式の本体を式のみの記述にできるようにする提案。

// [] ( auto x, auto y) { return x + y ; } と同じ
auto plus = []( auto x, auto y ) x + y ;

これにより、こんなにかっこいいlambdaをネストしたコードが書ける。

auto curry3 = [](auto f)
                [=](auto a) 
                   [=] (auto b)
                      [=] (auto c) f(a, b, c) ;

auto sum = [](auto a, auto b, auto c) a + b + c ;

auto val = curry3(sum)(1)(2)(3); // val = 1 + 2 + 3

ちなみに、現行のlambda式で書くとこんなに野暮臭くなる。

auto curry3 = [](auto f) {
                return [=](auto a)  {
                   return [=] (auto b) {
                     return [=] (auto c) {
                          return f(a, b, c); 
                     }}}};
                    
auto sum = [](auto a, auto b, auto c) 
 { return a + b + c; };
auto val = curry3(sum)(1)(2)(3);

ちなみに、クラスで書くめちゃくちゃ読みにくいので書かない。

ただし、この提案は結構な文法上の曖昧性をもたらす。一番問題になるのは、コンマがlambda式の本体の一部なのかどうかが曖昧になる文脈だろう。この提案では、括弧で囲まない限り、コンマはlambda式の本体の式の終了を意味するようになるとしている。

3. 戻り値の型の指定にもautoを使えるようにする。

ジェネリックlambda式では特に必要になる機能だ。

auto L = [=](auto f, auto n) -> auto & { return f(n) ; } ;
auto M = [=](auto f, auto n) -> auto * { return f(n) ; } ;
auto N = [=](auto f, auto n) -> auto   { return f(n) ; } ;

また、decltype(auto)のような使用方法も許されるらしい

4. ジェネリックlambda式でVariadic auto仮引数

[]( auto ... args ) { } ;

まあ、見ての通りの機能。

また、今回の提案では、時間が足りないため、lambdaの文法を通常の関数にも広げるだとか、名前付きlambda、通常の関数におけるauto仮引数などの機能はパスするそうだ。C++1yにはぜひとも欲しい機能だ。

[カァーッペッDF] N3561: Semantics of Vector Loops

この論文の著者は先月の会議で、ベクトルループ内のクリティカルセクションは未定義動作が当然ということを話したが、そもそもベクトルループってよく分からんから解説してくれよと言われたので、その解説の論文。パラレルループよりも制約が多く難しいベクトルループを解説している。

[また性懲りもなくPDF] N3562: Executors and schedulers, revision 1

関数オブジェクトで表現される細かい処理群を実行するexecuterライブラリの提案。このライブラリはthread poolでもあるが、もっとスレッドプール以外の一般の処理をも扱うそうだ。

[こんなにPDFが多いのは何かの陰謀か] N3563: C++ Mapreduce

mapreduceライブラリの提案の改訂版。

[あのなぁピー坊] N3564: Resumable Functions

Resumable functionの提案。

名前の通り、途中で中断し、その後中断したところから実行を再開する関数。これはfutureとの合わせ技で使う。

たとえばこんなコード

future<int> f(shared_ptr<stream> str)
{
    shared_ptr<vector<char>> buf = ...;
    return str->read(512, buf)
        .then([](future<int> op)
        {
            // lambda 1
            return op.get() + 11;
        });
}

future<void> g()
{
    shared_ptr<stream> s = ...;
    return f(s).then([s](future<int> op)
    {
        s->close();
    } );
}

ストリームからの読み出しが完了してからなにか処理をしたい場合、これまでは関数を分けて指定子なければならなかった。それが、

future<int> f(stream str) resumable
{
    shared_ptr<vector<char>> buf = ...;
    int count = await str.read(512, buf);
    return count + 11;
}
future<void> g() resumable
{
    stream s = ...;
    int pls11 = await f(s);
    s.close();
}

このように簡潔に書けるようになる。

resumable関数が中断した時点で、呼び出し元に処理が戻る。呼び出し元は、戻り値のfutureのオブジェクトを経由して結果にアクセスする。futureの完了を待つという事は、つまり中断した関数の実行が、ストリームからの読み出しが終了した後、再開する。

resumable関数は、明示的にresumable文脈依存キーワードを指定する。さらに、中断箇所ではawait(キーワード、もしくはresumable関数内の文脈依存キーワード)を指定する。戻り値の型は、futureかshared_futureでなければならない。

resumable関数の呼び出し側としては、通常のfutureを返す関数と何らかわりなく使える。

論文ではさらに、実装方法について説明している。教育が難しそうな機能だ。

[前に論文はHTMLで書こうっていうN3325の勧告はどうした] N3565: IP Address Design Constraints

IPアドレスを扱うライブラリの設計の議論の次第を紹介する論文。これは最終決定ではなく、あくまで議論の内容の要約である。

まず、IPアドレスを扱うクラスについてだ。現在、IPv4アドレスとIPv6アドレスがある。この2つのアドレスをどうやってクラスで表現するかという問題がある。

まず、どちらのアドレスもひとつのクラスで表現する設計。これはRFCでも推奨されている設計である。また、将来新たなアドレスが登場した時でも、既存のクラスを対応させればいい。ただし、これにはパフォーマンス上の問題がある。というのも、IPv4とIPv6では、表現に必要なオブジェクトのサイズが異なるから、無駄が生じるのだ。

IPv4とIPv6を表現するクラスを分けるのは、RFC非推奨であり、さらに将来性が暗い。ただしパフォーマンスの問題を解決できる。

折衷案として、IPv4とIPv6でクラスを分けるが、ただし相互に変換可能にする設計案がある。IPv6からIPv4への変換は実行時に例外を投げる。IPv4からIPv6への変換は、常に成功する。これはパフォーマンスも良く、将来性もある設計だが、複数の型が必要で、やはりRFC推奨ではない。

既存の実装であるASIOライブラリは、3つのクラスを使う設計をしている。つまり、IPv4/IPv6を表現するクラスと、そのクラスを統括するクラスだ。これはパフォーマンスの問題はない。ただし、型が複数となり、中途半端にジェネリックで、RFC非推奨だ。

AISOの設計を発展させて、3つ目の管理クラスをunionにする案。これはパフォーマンスがよく、完全にジェネリックな設計だが、型が複数となり、RFC非推奨だ。

まあ、IPV4ごときを格納するのにIPv6分のメモリを浪費したくないのはC++ならではといったところか。

N3566: C++ Standard Evolution Active Issues List
C++ Standard Evolution Closed Issues List

今回から新しくできたC++のEvolution(拡張)のissueリスト。なかなか興味深いものが並んでいる。

N3568: Shared Locking Revision 1

multiple-readers / single-writer locking patternのためのライブラリ提案の改定案。

N3570: "quoted" proposal

ストリームライブラリにquotedマニピュレーターを追加。これは空白を含む文字列をそのまま出力、入力する設定に切り替えるためのマニピュレーターである。

Boostにも同様のものが数年前からある。

Boost "quoted" I/O manipulator - 1.53.0

[やる気の失せるPDF] N3571: A Proposal to add Single Instruction Multiple Data Computation to the Standard Library

C++にSIMDライブラリを追加する提案。

SIMDとは、例えば128bitの大きなレジスタを用意し、そのレジスタを32bitの整数4つとみなして同時に4つの32bit整数に対して演算を行うことである。最近はレジスタ長も増えており、たとえばIntelのAVXは256bit長だし、Intel MICは512bitもの長さのレジスタがあるという。

ベクトル演算はうまく使えば素晴らしい性能をひき出すが、そのうまく使うという事が難しい。直接アセンブリを書くのが最も手っ取り早いが、アセンブリ言語は書きにくく、移植性も低い。

コンパイラーでコードを解析し、ベクトル化できる部分を自動的にベクトル化するということは、もうだいぶ昔から行われている。しかし、残念ながらいまだにコンパイラーはそれほど賢くない。

独自拡張のプリプロセッサマクロやキーワードなどを使い、ベクトル化のためのヒントを記述する実装も多数ある。しかし、これは移植性が低い上に、やはり使いづらい。

Compiler Intrinsicといって、レジスターを型として提供し、SIMD命令を薄いC言語の関数として提供する機能もある。これは厄介なレジスター管理をコンパイラーに任せることができるが、すべてが薄い関数のラッパなので、コードがとても分かりにくい。C++らしいライブラリが求められる。

このため、C++でSIMD演算用の薄いラッパークラスとしてのライブラリが提案されている。これはBoost.SIMD(現時点では、まだBoostに採用されていない)を元にしたものである。

このライブラリは、SIMDレジスターをクラステンプレートで提供する。クラステンプレートはテンプレート引数から適切な長さのレジスター(あるいは複数のレジスター)を使う。演算子オーバーロードにより、演算は通常の式で書ける。

また、SIMD版のtransform/accumulate(イテレーターの代わりにポインターを取る)アルゴリズムも提供される。

また、このSIMDライブラリ用に適切にアラインされたストレージを確保するアロケーターも提供される。

このライブラリの実装には、コア言語側に追加の機能は必要ない。ただし、Compiler Intrinsicのような、コンパイラー側のサポートが必要となる。

保守的で職人気質な人間の多い動画業界では、Compiler Intrinsicはいまだに鼻で笑われているが、まあ、彼らはHD動画をリアルタイムでエンコード、デコードできるよう強い圧力を受けているので、しかたがないのだろうか。あるいは、Intrinsicから生成されるコードは、まだまだ手書きよりはるかに劣るのか。

N3572: Unicode Support in the Standard Library

Unicodeライブラリの提案。エンコード情報付きの文字列クラスや、コードポイントの種類取得(数値、文字、シンボル、句読点などなど)の機能を提供するライブラリとなっている。

N3573: Heterogenous extensions to unordered containers

どうも説明不足でよくわからない。unorderedコンテナのキーとして別の型を使えるようにする変更と、ハッシュ関数を検索ごとに変えられるようにする変更のようだが。

N3574: Binding stateful functions as function pointers

関数オブジェクトを関数ポインターに変換するthunkライブラリ、bound_functionの提案。このbound_functionはextern "C"リンケージのC関数ポインターへの変換関数を持つ。bound_functionの提供するC関数ポインターを呼び出すと、bound_functionのコンストラクターに渡されたオブジェクトをthisとしてoperator ()を呼んでくれる。

どうしても既存のC関数ポインターが必要なライブラリで、statefulな関数オブジェクトを使いたい需要は相当にある。BoostのVaultに、ユニークな型を渡せば、ユニークなstatic変数にオブジェクトのアドレスを格納して関数ポインターを提供するクラステンプレートが転がっていたが、このライブラリはそのような必要もなく、しかもextern "C"リンケージの関数ポインターを変えす。

おそらく、実装にはコンパイラーマジックが必要になると思うのだが。

N3575: Additional Standard allocation schemes

一般的に使われている各種の特性を持つ、汎用ではないストレージの確保解放のためのライブラリの提案。これはアロケーターではなく、アロケーターを実装するための低レベルのライブラリとして設計されている。

まずはheap。heapオブジェクトは、確保されたストレージを所有する。heapオブジェクトを破棄すれば、そのheapオブジェクトから確保されたストレージはすべて解放されたことになる。

また、このheapのthread unsafeに使うためのアダプターであるunserialized_heap_allocator。このアダプターを経由してheapを使った場合、スレッドセーフにするための排他的な保護が行われない。これにより動機のオーバーヘッドを回避できるが、複数のスレッドから同時にunserialized_heap_allocatorを使った場合の挙動は未定義である。

小さく同一のサイズのストレージを大量に確保、開放する利用例に最適化されたobject_poolと、object_poolの同期処理を無効化するアダプターであるunserialized_pool_allocator。

そしてarena。これは連続して確保した一連のストレージを一気に開放するワーキングメモリーような用途に最適化された戦略を取る。関連するストレージの境目は、gateクラスのオブジェクトを破棄することで指示する。そして、gateを作らずに解放しないストレージを確保するarena_allocator(これはよくわからない)。また、arenaはデフォルトでthread unsafeらしく、スレッドセーフな確保をしたければアダプターのconcurrent_arenaを使う必要がある。このconcurrent_arenaには、lock_freeというstaticデータメンバがあり、concurrent_arenaの実装がロックフリーの環境であればtrueとなるそうだ。そしてconcurrent_arena_allocator。

論文では、標準ライブラリのコンテナーのデフォルトアロケーターの指定を取り除く提案もしている。これにより、実装はコンテナーごとに異なる戦略のアロケーターを使うことができ、パフォーマンスの向上につながる。ただし、std::allocatorがデフォルトであることをあてにした既存のコードを壊してしまうという互換性の問題があるのだが。

[PDFの悪夢再び] N3576: SG8 Concepts Teleconference Minutes - 2013-03-12

会議の議事録。static ifの作業を少し送らせて、当座はconcept liteに注力することや、concept lite関連の議論が主な内容。concept liteの目標をC++14にするのかC++17かTSかという、とても気になる議論は、まだ決められるわけがないということで投票もなし。

[HTMLが恋しくなるPDF] N3577: Fall 2013 JTC1/SC22/WG21 C++ Standards Committee Meeting

2013年の9月にシカゴで行われる会議の会場となるホテル設備の案内。

[PDFである必要はあんまりない] N3578: Proposing the Rule of Five

ユーザー定義されたデストラクター、ムーブコンストラクター、ムーブ代入演算子があるときは、コピーコンストラクターとコピー代入演算子は暗黙に生成されない。ただし、C++11ではdeprecated扱いながら生成される。

C++14では、このdeprecatedな挙動を規格から完全に取り除こうではないかという提案。規格1つ分の間、警告したのだから、十分な準備期間を与えたはずだ。

C++では、昔から三原則(The rule of Three)と非公式に呼ばれている原則があった。これは、デストラクター、コピーコンストラクター、コピー代入演算子のうち、どれか一つでも明示的に定義したならば、残りの二つもおそらく定義する必要があるだろうという原則だ。

この原則に、ムーブコンストラクターとムーブ代入演算子も加わるので、五原則となる。

N3569: A type trait for signatures

与えられた仮引数の型を実引数に与えて呼び出した場合に呼び出される関数型を返すtype traits、std::signatureの追加。

struct C
{
  int operator()(double d, int i);
  int operator()(double d1, double d2);
} ;

このような関数オブジェクトCがあったとして、std::signature<C(int, int)>::typeは、int (double,int)になる。何故か論文ではint(double,double)を返すと書いているが、これは誤りである。

いわば、オーバーロード解決の結果の関数型を返すメタ関数だと考えれば良い。このメタ関数は、よりパーフェクトなforwardingを実装するのに使える。たとえば、std::threadやstd::asyncのようなライブラリは、リファレンスはコピーして渡す。これは、現行のC++にstd::signatureがなく、実際に呼び出される関数の仮引数の型を取得できないためである。std::signatureがあれば取得できるので、よりパーフェクトなforwardingが実現できる。

この提案は思わず笑ってしまった。こうきたか。ぜひとも入って欲しい。

[PDF死んでくれ] N3580: Concepts Lite: Constraining Templates with Predicates

この論文のサンプルコードはコンパイラーで検証されていないらしく、文法誤りが多すぎる。

C++11には却下されたConcept機能のうち、テンプレート利用者側が与えるテンプレート実引数の要件チェックだけに焦点を絞ったConcept Liteの提案。

一言で言ってしまえば、コンパイル時計算の結果次第で、実体化されたテンプレートの特殊化を利用できないようにする機能だ。

C++11で提案されたコンセプトはとても膨大だった。テンプレート利用側のテンプレート実引数の型が要件を満たしているかどうかの検証はもちろんのこと、テンプレートコードが要件を満たしているかどうかの検証や、要件を満たさない型に対して、要件を満たすようにマップするConcept Mapや、仕様を直接コードとして記述するaxiomを含む大掛かりなもので、しかもテンプレート自体を、旧来のコンセプト検証を行わない、いわばuncheck template(unconstrained template)と、コンセプト検証を行うcheck template(constrained tempalte)に分断するというものだった。

今回はConcept Lite(軽量コンセプト)と題して、すでに書いたように「テンプレート利用者側が与えるテンプレート実引数の要件チェック」だけを提供する機能となる。一気に入れるのではなく、段階的に完全なコンセプトをいれる予定だ。

この説明だけではわからないだろうから、簡単な例で説明しよう。

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

template < typename T >
void f( )
{
    T t ; // デフォルトコンストラクターとデストラクターが必要
}

int main()
{
    f<X>() ; // エラー
}

このコードがエラーとなる理由は、関数テンプレートfで、テンプレート仮引数Tにはデフォルトコンストラクターとデストラクターを要求しているのに、クラスXにはデフォルトコンストラクターがないからだ。

残念ながら、このコードをコンパイルしても、わけのわからないコンパイルエラーが表示される。

なんとかして、テンプレート仮引数Tには、デフォルトコンストラクターとデストラクターが必要であるという事を明示できないか。そうすれば、エラーメッセージはもっとわかりやすくなる。

それを可能にするのが、今回のConcept Liteだ。以下のように書ける。

template < typename T >
constepxr bool DefaultConstructible()
{
    return std::is_default_constructible<T>::value ;
}

template < typename T >
constexpr bool Destructible()
{
    return std::is_destructible<T>::value ;
}


template < typename T >
requires DefaultConstructible<T>() && Destructible<T>()
void f()
{
    T t ;
}

このように、boolを返すconstexpr関数で、デフォルトコンストラクターとデストラクターが必要だということを明示的に記述できる。この機能があれば、コンパイルエラーは、「テンプレート仮引数Tに対するテンプレート実引数XはDestructibleを満たさない」という、非常に分かりやすいものにできる。

また、これはコンパイルエラーにするための機能ではない。関数テンプレートに適用した場合は、オーバーロード解決の候補から外すという機能となっている。そのため、汎用的すぎて誤爆してしまう関数テンプレートを候補から外すこともできる。これはライブラリ実装者にはとても重宝する機能である。従来のタグディスパッチや意図的にtemplate substitutionを失敗させる変態的な技法を使わずともすむのだ。しかも、コンパイル速度は従来の技法より速くなる。

もちろん、これは全てコンパイル時に行われるので、実行時には何の影響もない。

クラステンプレートやエイリアステンプレートにも、この機能は使える。また、変わったところでは、クラステンプレートのメンバーにも使える。

template < typename T >
struct X
{
    requires hoge<T>()
    void member1() ;

    requires hage<T>()
    void member2() ;

} ;

また、簡単なコンセプトは、requiresを使わず、直接テンプレート仮引数のclass/typenameキーワードの代わりに書ける。

template < DefaultConstructible T >
void f() ;

ところで、そのコンセプトの要件定義に使うconstexpr関数だが、まともに書こうとするとつらい。例えば、operator ==とoperator !=で等価比較できる型の要件定義は以下のようになる。

template<typename T>
concept Equality_comparable()
{
    return has_eq<T>::value && is_convertible<eq_result<T>, bool>::value
        && has_ne<T>::value && is_convertible<ne_result<T>, bool>::value;
}

明らかに、このconstexpr関数はとてつもなく書きにくいし読みにくい。has_eqはTにoperator ==が適用できるときにtrueを返すメタ関数であり、has_neはそのoperator !=版だ。さらに、is_convertibleでoperator ==や!=の式を評価した結果の型がboolに変換可能かどうかを確かめている。

要件定義をもう少し自然に書けるよう、requires expressionが提案されている。

template<typename T>
constexpr bool Equality_comparable()
{
    return requires (T a, T b) {
        bool = {a == b};
        bool = {a != b};
    };
}

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

requires式は、文法要件を記述するための式である。便宜上の型のオブジェクトを定義し、そのオブジェクトに対して、任意の式を記述できる。その式が、テンプレートが実体化されて実際に使われた場合に合法である場合のみ、trueを返す。requires式内のすべての式がtrueの場合のみrequires式はtrueを返す。

もちろん、式が妥当かどうかを検証するのみならず、式を評価した結果の型が、ある型に変換可能かどうかも検証できる。この例では、boolに変換可能かどうかを調べている。

また、エイリアステンプレートを使うことで、ネストされた型名があるかどうかも調べられる。

template < typename T >
using Value_type = T::value_type ;

template < typename T >
constexpr bool Readable()
{
    return requires ( T t )
    {
        typename Value_type<T> ;
        const Value_type<T> & = { *t } ;
    } ;
}

このコードは、エイリアステンプレートValue_typeを介して、T::value_typeがあるかどうか、そして、T型のオブジェクトにoperator *を適用した結果が、T::value_typeへのconstなlvalueリファレンス型に変換可能かどうかを調べている。

この提案は、完全なConceptをC++に提供する第一歩となる。

論文によると、将来的には完全なコンセプトを提供するにとどまらず、セマンティックも言語で表現できるようにしようという野望があるらしい。またaxiomみたいなのを提案するのだろうか。

論文ではconstrained template同士の優先順位や同一性の比較方法を論じているが、いやはや、たしかに、宣言やpartial orderingやオーバーロード解決で使われる以上考えなければならないが、とてもむずかしい。最終的には、atomic propositionに分けて比較。constrained templateは、nonconstrained templateより優先されるらしい。

template<Floating_point T>
    class complex; // #1

template<typename T>
    requires Floating_pont<T>()
class complex; // #2

template<typename T>
    requires is_same<T, float>::value
          || is_same<T, double>::value
          || is_same<T, long double>::value
class complex; // #3

この例で、#2は#1の再宣言である。もし、Floating_pointの定義が#3のatomic propositionsに分解できるならば、#3も同じ宣言だということになる。

異なるconstraintsは別の宣言になるので、たとえばdistanceをイテレーターカテゴリーに応じて宣言することも可能になる。

template<Input_iterator I>
ptrdiff_t distance(I first, I last); // #1
template<Random_access_iterator I>
ptrdiff_t distance(I first, I last); // #2

クラスはもともとオーバーロードできないが、特殊化は複数記述できるので、似たようなことができる。

template<Arithmetic T>
class complex;
template<Floating_point T>
class complex<T>; // #1
template<Integral T>
class complex;
// #2
complex<int> g; // Selects #2

オーバーロード解決のルールも変わる。constrained templateの場合は、インスタンス化の前に要件チェックが入るし、best viable functionを選択する際にも、もっともconstrainedなテンプレートが選ばれる。partial specilizationも概ね似たような形になる。

もちろん、型だけではなく、非型にもコンセプトが適用できる。

論文ではさらに実装経験について触れている。GCCベースの実験的な実装では、is_sameなどのtype traitsをコンパイラー支援を受けたIntrinsicを導入し、<type_traits>を大幅に書き換えたところ、25%のコード量削減を実現したそうだ。コンパイル速度については、まだ計測していないが、インスタンス化の回数が減ったことにより、向上するのではないかと見込まれている。

ちなみに、この論文で提案されているConcept Liteの実験的な実装が以下から手に入る。GCCを土台にした実装となっている。

http://concepts.axiomatics.org/~ans/

N3581: Delimited iterators

デリミタ付きでostreamに流しこむdelimited_iteratorの提案。ostream_iteratorは、デリミタではなくてサフィックスなので、最後の要素にも出力されてしまう。

N3582: Return type deduction for normal functions

普通の関数にも戻り値の型推定を行う提案の改定案。前回のN3386からの変更点は、

decltype(auto)の追加。これにより、リファレンスなどを正しく扱える。

template < typename T >
auto f( T const & x ) -> decltype(auto) &
{
    return g(x) ;
}

なお、このdecltype(auto)は、一貫性を保つため、autoが使える文脈ならばどこでも使える。

trailing-return-typeでautoの使用

[]() -> auto & { return f() ; }

std::initializer_listの推定の禁止。

std::initializer_listはautomatic storage durationをもつストレージ上に確保されるので、関数の戻り値に使うというのはありえない。そこで、推定されないようにする。

[PDFを使う理由が微塵も感じられない] N3583: Exploring constexpr at Runtime

constexpr関数は、コンパイル時にも実行時にも使える。しかし、これはとても深刻な問題を引き起こす。

一つには、コンパイル時に検出できるはずのエラーが、実行時エラーになってしまうということだ。

constexpr int inverse( int v )
{
    return 1 / v ;
}

int main()
{
    // コンパイル時エラー
    // ゼロ除算
    constexpr int c = inverse(0) ;
}

このコードは、ゼロ除算を行うためにエラーとなる。もちろん、inverseはコンパイル時に実行されるので、コンパイル時にエラーになる。しかしもし、変数の定義がconstexprではなくconstだったらどうなるだろうか。

// GCC 4.7とClang 3.2でコンパイル成功
// 実行時エラー
const int r = inverse(0) ;

今のところ、GCC 4.7でも、おそらく4.8でも、Clang 3.2でも、このコードはコンパイルが通ってしまう。理由は、inverseの実行は実行時に遅延されるからだ。これは規格準拠の実装である。constの場合は、constexpr関数をコンパイル時実行しなくてもいいのだ。もちろん、規格はこの場合においてコンパイル時実行することも許容しているが、保証していない。現在知る限り、このコードをコンパイル時実行するC++コンパイラーは存在しない。

コンパイラーがいずれもこのコードをコンパイル時実行しないのには理由がある。コンパイル時間の短縮のためだ。実際に評価するまでコンパイル時定数になるかどうかも分からないのに、わざわざコンパイル時定数ではなくてもいい場所で、コンパイル時実行する価値はない。

しかし、inverse(0)という式自体は、コンパイル時にエラーかどうか判定できる式である。これは問題だ。

もうひとつは、constexpr関数は、必ずしも実行時実行して優れた実装であるとは限らないことだ。例えば、平方根を計算するconstexpr関数を考えよう。平方根の計算は、一般にBabylonian methodと呼ばれる方法で計算する。

本の虫: 平方根のアルゴリズム

これをconstexpr関数で実装すると、再帰関数になるが、それは実装上の詳細なので省略する。

問題は、このconstexpr関数実装のsqrtは、ほとんどの環境で、標準のstd::sqrtより格段に遅いということだ。なぜかというと、多くの実装のstd::sqrtは、実行環境独自の命令などを使い、高速に平方根を計算する実装になっているからだ。まともに計算して勝ち目はない。

現状では、コンパイル時sqrt関数と、実行時sqrt関数は、別の名前をつけて使い分けなければならない。

このふたつの問題をなんとか解決できないものか。それには、constexpr関数をコンパイル時にしか評価できないように制限するとか、あるいはコンパイル時と実行時で実装を選択できるようにするとかの機能が必要だ。では、どのような機能があればいいのか。

案A:qualifierやattributeによるコンパイル時実行の強制

まず思いつくのは、qualifierやattributeによって、constexpr関数をコンパイル時実行のみに限定する機能だ。これは少なくとも、コンパイル時にエラーを検出したいというinverseの問題は解決してくれる。

ただし、やはりコンパイル時と実行時で別の名前を使わなければならないので、sqrtの問題は解決できない。しかも、constexprコンストラクターは、別名をつけることが出来ないので、コンパイル時と実行時で別名のクラスを使わなければならない。

案B: 末尾再帰保証

constexpr関数の再帰を、末尾再帰のみ許すように制限して、規格で実装に末尾再帰の最適化を保証させる。これはSchemeに影響されている。

sqrtの問題を直接解決は出来ないものの、末尾再帰保証によって、汎用的な処理ならば、実行時関数と同等のパフォーマンスを持つ実装にできる。しかし、下位互換性を失ってしまうし、問題の本質的な解決にはならない。

それに、論文では触れていないが、constexpr関数を拡張して、ループを含む任意の文を記述できるようにしようという議論がある。

休憩:constexprのコンパイル時実行の優先保証

constexpr関数をコンパイル時実行できる文脈ならば、かならずコンパイル時実行されるように規格で保証してはどうか。これは、この次の二案を実装するにあたり、重要になる。

案C: traitsやconceptによる、実装選択

traitsやconceptによって、コンパイル時と実行時で実装を選択できるようにしてはどうか。たとえば、コンパイル時実行されている場合にtrueを返す、std::evaluated_during_translation()のようなものを追加してはどうか。

constexpr int inverse( int v )
{
    static_assert( std::evaluated_during_translation(),
        "inverse may not be used at runtime. "
        "Please declare the target variable as constexpr." ) ;

    return 1 / v ;
}

と、このようにコンパイル時にstatic_assertなどでエラーにすることもできるし、コンパイル時に実装を選択することもできる。

しかし、こんなtraitsを導入したら、当然SFINAEで使うやからが出るにきまっている。

template <typename T>
constexpr
typename std::enable_if<std::evaluated_during_translation(), T>::type
sqrt(T value)
{ /* コンパイル時実装 */ }

// 実行時実装
template <typename T>
typename std::enable_if<! std::evaluated_during_translation(),T>::type
sqrt(T value)
{ std::sqrt(value); }

問題は、現在主流のコンパイラーのテンプレートの実装は、一箇所だけでインスタンス化して、その結果を保持して使いまわすというところにある。これでは擬似コイントスをしているに過ぎない。コンパイル時と実行時のどちらの文脈でインスタンス化されるかによって結果が変わるのであって、利用箇所すべてで結果が変わるわけではない。

しかも、このようなコードはODR違反となる。

そのため、このようなtraitsを導入するならば、SFINAEの文脈での扱いについて、相当の考慮が必要だ。

案D: constexprをオーバーロード可能なシグネチャにする。

現在、constexpr qualifierは、非staticメンバー関数のみで、オーバーロード可能だ。

// Overload non-static non-const class member on constexpr compiles
struct test_a {
    bool is_constexpr() { return false; }
    constexpr bool is_constexpr() { return true; } // OK
};

といっても、これは実は、constexprというシグネチャでオーバーロードしているのではない。constexprは、非staticメンバー関数のconst qualifierもかねるので、それでオーバーロード出来るだけだ。

// Overload member on both const and constexpr fails
struct test_b {
    bool is_constexpr()
    { return false; }
    bool is_constexpr() const
    { return false; }
    constexpr bool is_constexpr()
    { return true; } // エラー
}

constexprかどうかでオーバーロードできるようにすれば、コンパイル時と実行時の実装が分けられて都合がいいのではないか。

論文では、オーバーロードかつコンパイル時実行できる文脈でのコンパイル時実行保証が一番お気に入りのようだが、明確な結論は出していない。思うに、この問題は、もっと議論が必要だろう。

N3584: Wording for Addressing Tuples by Type

Tupleの要素に型でアクセスできるようにする提案。

tuple<string, string, int> t("foo", "bar", 7);
int i = get<int>(t); // i == 7
int j = get<2>(t);  // 上と同じ。j == 7
string s = get<string>(t); // コンパイル時エラー、曖昧。

なんだかTupleがコンパイル時setみたいだ。

[PDF地獄] N3585: Iterator-Related Improvements to Containers (Revision 2)

イテレーター関連のライブラリの細かい改良提案の改訂版。前回のN3450との違いが書いてないが、とくに提案の違いはなさそうだ。

もう一度紹介すると、コンテナの最後の要素を返すメンバー関数last()、特定のコンテナのオブジェクトには紐付けられていないnullイテレーター、mapイテレーターで、pairのかわりに、keyだけ、valueだけを返すイテレーターアダプターのselector、イテレーターとインデックスの相互変換機能。

[PDF煉獄] N3586: Splicing Maps and Sets

associative containerとunordered associative containerにsplice機能を追加する提案。

spliceというのはstd::listの機能で、listのオブジェクトのノードを、別のlistのオブジェクトに挿入できる機能である(ただし、アロケーターが等しくない場合の挙動は未定義)。listはその仕組み上、アロケーターの互換性さえあれば、内部のノードの所有権を別のlistのオブジェクトに動かすことができる。

これはムーブとは違う。ムーブとは代入元を破壊する代入であり、クラスがポインターやハンドルなどのリソースを参照するようなメンバーを持つ場合にはパフォーマンス上の利点があるが、クラスそのもののサイズが大きい場合には、役にはたたない。たとえ、クラスのサイズはそれなりでも、何百万個もオブジェクトを作るような場合には、確保解放を回避するというのは、パフォーマンス上とても重要である。emplaceは、オブジェクトを構築するときにはいいが、やはりオブジェクトの境を超えるとストレージの確保、解放が発生する。list同士ならば、spliceでノードの所有権を融通し合う事で、とても効率のいい移動が行える。

と、これがlistのspliceだ。しかし連想コンテナの場合はどうだろうか。まあ、連想コンテナでも内部のノードの融通はできる。しかし、それ以上のことがしたい。

たとえば、mapのキーを変更したいとする。従来は、新たにキーと値のペアを作って挿入し、古いペアは消すしか方法がなかった。なんとか確保したストレージはそのままに、その上でキーを変更できないだろうか。

このために、連想コンテナとunordered連想コンテナに、removeというメンバー関数を新たに追加する。このremoveは、eraseに似ていて、選択したノードのunlinkとそれに伴う内部実装のツリーのリバランスを行うが、ノードのストレージを解放しない。開放する代わりに、node_ptrというunique_ptrに似た独自のスマートポインターにノードの所有権を移し、戻り値として返す。このnode_ptrは、ノードへのポインターと、アロケーターのコピーを保持する。これにより、元のコンテナーの寿命が付きた後でも、ノードだけ生き延びることができる。また、insertにもnode_ptrのオーバーロードを追加することにより、node_ptrのコンテナへの挿入は、node_ptrが所有するノードの所有権を移す処理になる。node_ptrとして一旦とり出せば、ストレージの再確保なしで、キーや値を変更をして、元のコンテナーに割い挿入できるし、別のコンテナーに挿入することもできる。

なお、ノードが見つからなかった場合は、空のnode_ptrが返される。空のnode_ptrを挿入しようとしても、何も起こらない。

たとえば、mapのあるノードを別のオブジェクトに移す場合は以下のようになる。

map<int,string> src, dst;

src[1] = "one" ;
src[2] = "two" ;
dst[3] = "three" ;

// イテレーター版
dst.insert(src.remove(src.find(1)));
// キー版
dst.insert(src.remove(2));

コンテナの前要素のマージも可能だ。

set<int> src{1, 3, 5};
set<int> dst{2, 4, 5};

dst.insert(src); // srcをdstにマージ
// src == {5}
// dst == {1, 2, 3, 4, 5}

キーの値を変えて再挿入もこのとおり。

struct record { ... };
typedef map<int, record> table_type;
table_type table;
table.emplace(38, ...);

auto elem = table.remove(38);
elem->first = 97;
table.insert(move(elem));

これはすばらしい提案だと思う。ぜひとも入るべきだ。

[PDFヘル] N3587: For Loop Exit Strategies

この論文の筆者は、常に以下のようなコードを書いている。

auto i = c.begin();
// Unfortunate that i is required here.
for (; i != c.end(); ++i)
{
    if (some_condition(*i)) break;
    do_something(*i);
}
if (i == c.end()) // Extra test here.
{
    do_stuff();
}
else
{
    do_something_else(*i);
}

つまり、ループを途中で終了したかどうかによって、その後の処理内容を分ける必要のある処理だ。その際、中断した場所のイテレーターも欲しい。

このコードはどうみてイケてない。イテレーターiをfor文のスコープ外で使えるようにするため、外で宣言しなければならないからだ。Range-based forは、イテレーターを外で宣言する方法がないため、もっとイケてないコードになる。そのため、筆者はこのような場合にrange-based forは使わない。

この問題は、for文を抜ける際に、通常終了したか、途中終了したかによって実行される文を指定できれば解決できる。

for ( ... )
{
// ループ本体
}
then
{
// 通常終了:ループの条件がfalseになった
}
else
{
// 途中終了: breakで抜けた
}

残念ながら、いまさらthenなどというキーワードを導入するわけには行かない。そこで、この論文では、以下のような文法を提案している。

if for ( ... )
{
// ループ本体
}
{
// 通常終了:ループの条件がfalseになった
}
else
{
// 途中終了: breakで抜けた
}

論文ではこれが実現可能な文法だと主張しているが、正直キモい。ちょっと考えたdあけで、以下のような文法が浮かんだのだが、なにか問題があるだろうか。

for ( ... )
{
// ループ本体
}
break
{
// 途中終了: breakで抜けた
}
else
{
// 通常終了:ループの条件がfalseになった
}

順番は逆になり、elseの意味も変わったが、こっちほうが分かりやすいと思うのだが。この文法ならば、breakかelseだけを記述することも可能にならないだろうか。ただし、breakなしでelseだけだとさすがに分かりにくいだろうか。それならばcontinueはどうか。

for ( ... )
{
// ループ本体
}
break
{
// 途中終了: breakで抜けた
}
continue
{
// 通常終了:ループの条件がfalseになった
}

あるいは文脈依存キーワードfinalは

for ( ... )
{
// ループ本体
}
break
{
// 途中終了: breakで抜けた
}
final
{
// 通常終了:ループの条件がfalseになった
}

何にせよ、文を何の区切りもなくふたつ続けてかくif forの文法は分かりにくい。この問題は、さらに文法上のバイク小屋議論が必要であろうと思う。

N3588: make_unique

標準ライブラリはshared_ptrのためのmake_sharedを提供しているが、unique_ptrのためのmake_uniqueを提供していない。make_uniqueは簡単な実装だが、Variadic Templateやperfect forwardingを正しく使うのは、初心者には難しい。make_unique<T[]>はもっと難しい。標準で提供すべきだ。

というわけで、make_uniqueの提案。これによりC++初心者に「new/deleteは絶対使うな」ということができる。

[PDFパーガトリー] N3589: Summary of Progress Since Portland towards Transactional Language Constructs for C++
[PDF注意] N3591: Summary of Discussions on Explicit Cancellation in Transactional Language Constructs for C++

Transactional Memoryに関する議論において、合意のとれたことを要約している。

N3592: Alternative cancellation and data escape mechanisms for transactions

Transactional Memoryのキャンセル機能の提供方法について考察している。

N3593: Proposing std::split()

N3510の改訂版。文字列をデリミタで区切って分割するstd::splitの提案。

前回からの変更点は、std::splitは型変換によらず、直接Rangeを返すようになったこと。std::splitはstd::string_refのみを返す。std::stringへの変換はユーザーの仕事。splitの結果をフィルターしたりする機能は取り除かれた。同等の機能は汎用的なRangeアダプターライブラリで提供されるべきだとしている。

N3594: Proposing std::join()

文字列を連結するstd::joinライブラリの提案。文字列の他にも、intなどの基本型や、N3609で提案されているstring_viewに対応した型を文字列として変換して連結できる。また、型から生成される文字列の書式を実装できるFormatterという機能も提供している。

とうとうストリームから独立した型安全のprintf代替ライブラリがでてきたか。

[うざいPDF] N3595: Simplifying Argument-Dependent Lookup Rules

またもやADL改造論。今回は、「そもそもテンプレート実引数の型が属する名前空間がassociated namespaceに追加されるのはおかしいだろ、何の役に立つんだよ? やめろよ」ということで、廃止する。誰もまともな使い方を挙げられなかったそうだ。たしかに、まともな使い方がわからない。

もし、従来のADLの挙動がほしければ、attributeで指定する。

// N3595提案

template < typename T >
struct X { } ;

template < typename T >
struct [[full_adl]] Y { } ;

namespace ns
{
    template < typename T >
    void f( T ) { }
    struct M { } ;
} 

int main()
{
    X<ns::M> x ;
    f( x ) ; // エラー、fが見つからない

    Y<ns::M> y ;
    f( y ) ; // OK、ns::fを発見
}

また、テンプレート仮引数が複数ある場合、個々に設定することもできる。

// N3595提案

template < typename [[full_adl]] T, typename U >
struct X { } ;

この例では、Tの属する名前空間はassociated namespaceに追加されるが、Uの属する名前空間は追加されない。

また、現在のADLのルールはおかしいところがある。関数テンプレートのテンプレート実引数を明示的に渡すとADLが無効になってしまうのだ。

namespace ns
{
    struct X { } ;

    template < int hint >
    void f( X ) { }
} 

int main()
{
    ns::X x ;
    f<0>( x ) ; // エラー、ADL無効
}

このような場合は、ADLが有効になってほしい。このような場合、ADLが無効にならないように変更する。

inline frined関数の適正化

クラス内でfriend関数を定義した場合、見つかる順番がややこしい。クラス内で定義しようがしまいが、name lookupの挙動を同じにする。

ADLの無効化

現行規格では、関数名を括弧で囲むと、ADLが無効になる。

(f)( x ) ; // ADL無効

これは非常に分かりにくい。そのため、ADLを無効化するattribute、[[no_adl]]を追加する。

[[no_adl]] f ( x ) ; // ADL無効

さらに、ADLを選択的に無効化することもできる。

f( [[no_adl]] x, y , [[no_adl]] z ) ;

この例では、実引数xとyの型の属する名前空間を、associated namespaceに追加しないようにするが、yの型については通常通りassociated namespaceに追加する。

過去に何度もADL改造の提案は出されてきたが、はたして今回は通るのだろうか。

[キモいPDF] N3596: Code Reuse in Class Template Specialization

クラステンプレートの特殊化に関連する、コードの重複を防ぐためのふたつの機能の提案。

ひとつ、クラステンプレートの明示的特殊化や部分的特殊化は、たいていこんな感じになる。

template < typename T >
class my_class
{
public :
    using value_type = T ;
    value_type f() { /* 実装 */ }

// その他、多数のメンバーと実装
} ;

template < typename T >
class my_class< std::complex<T> >
{
public :
// 一部のメンバーの実装だけ変更
    using value_type = std::comlex<T> ;
    value_type f() { /* 特別な実装 */ }

// 残りはprimary templateと全く変わらない多数のメンバーとその実装。
} ;

この例では、std::complexに対して何か特殊化すべきことがあるが、ほとんどのメンバーとその実装は変わらないということだ。つまり、コードが重複してしまう。しかも、ただ重複するだけではなく、primary templateでTだった型が、std::complex<T>になる。つまり、その置換を手作業で行わなければならない。

さらに、primary templateのコードに変更があった場合は、すべての明示的特殊化と部分的特殊化の重複コードまで手作業で対応しなければならない。

これは人間のやることではない。必要なだけ宣言すれば上書きされて、明示的に宣言しなかったものはprimary templateの実装を受け継ぐような機能があればどうか。例えばこんな文法。

template < typename T >
class my_class < std::complex<T> > = default 
{
public :
    value_type f() { /* 特別な実装 */ }
} ;

これにより、冗長で問題あるコードの重複を省くことができる。

なお、この機能は、派生も受け継ぐ。派生の結果継承されるメンバーについて色々とあるがそれは些細なことだ。

class Base
{
public :
    void f() { } 
} ;

template < typename T >
class Derived : public Base
{

} ;

template < >
class Derived< int > = default 
{
// Baseから派生
} ;

これは欲しい機能だ。

もうひとつの機能は、Consistent Specializationと題されている機能だ。Dependent class templateから派生するクラステンプレートを書く際の頭痛の種がある。

template <typename T>
class base
{
public:
    typedef T value_type;
    value_type f1() { return value_type() ; }
protected: // 論文ではprivateだがpublicかprotectedが必要
    value_type x;
} ;

template <typename T>
class derived
// 論文ではaccess-specifierがないがpublicかprotectedが必要
    : public base<T>
{
    int f2()
    { // 論文ではbaseだが誤り
        typename base<T>::value_type y; // #1
        y= this->f1(); // #2
        this->x+= y; // #3
    }
} ;

#1では、base<T>::が必要である。単にvalue_typeと書いたのでは、dependent nameかどうか分からないからだ。そして、typenameも必要である。value_typeは型か非型かもわからないからだ。#2, #3でも、this->が必要である。というのも、f1はxだけではdependend nameかどうかわからないし、それにoperator ()やoperator +=を適用できるかどうかも分からない。

これは例えば、baseに以下のような明示的特殊化があるかもしれないからだ。

template < >
class base< int >
{
public:
    int value_type ;
    int f1 ;
    using x = int ;
} ;

さて、このような明示的特殊化は合法なC++だが、実際にこんな元のテンプレートとはかけはなれて、しかも名前だけ同じ特殊化を書く人間がどこにいるのだろうか。書けるからといって、こんなことになにかまともな利用方法があるのだろうか。

もし、元のテンプレートから変わらないと保証できれば、わざわざdependent nameにしたりtypenameを書く必要がなくなるのだ。

そこで、primary templateから名前と型、非型が変わらないという保証をつける文法を追加してはどうか、例えば以下のように。

template <typename T>
class [[consistent]] base
{
public:
    typedef T value type;
    value type f1() {}
private:
    value type x;
};
template <>
class base<float>
{
    float value type() {} // エラー、typeは型でなければならない
};
template <>
class base<int>
{
    typedef double∗ x; // エラー、xはデータメンバーでなければならない
};

このように、特殊化でprimary templateから外れた宣言を書くとエラーになる機能を提供すれば、もう派生先からdependent nameやらtypenameやらを明示的に書く必要がなくなる。なぜならば、primary templateの宣言を尊重するからだ。

まあ、悪くはない機能だと思うが、どうもニッチすぎる機能な気がする。Dependent nameから派生するようなクラステンプレートを書く人間なら、この程度のことに躓くわけがない。それに、論文のタイトルである「再利用」とはちょっと違う気がする。

N3597: Relaxing constraints on constexpr functions

constexprの制限を大幅に緩和する提案。ほぼ普通の実行時関数のように書けるようになる。

もちろん、ローカル変数の書き換えもできるし、for文だって使えるようになる。constexprコンストラクターもメンバーの変更ができるようになる。

もちろん、夢はさらに大きく持つべきだ。例えば、constexprデストラクターの可能性についても触れられている。lambdaは難しいし、例外はやってできなくはないが、特に利用例も思い当たらないのでサポートしないほうがいいだろうとしている。

N3598: constexpr member functions and implicit const

constexprメンバー関数は暗黙にconst修飾されるが、これには問題が多い。というのも、コンパイル時と実行時の両方で使える便利なconstexprメンバー関数が書きにくい。

もう一度決めてしまったことを、今更変えるのはとても難しいのだが、constexprメンバー関数の暗黙のconst修飾を廃止できないか。あるいは、新しいconstではなくする修飾子を追加できないか。

それとも現状維持で、ユーザーには恥ずべきconst_castの利用を強いるべきか。

個人的には、どうせconstexprメンバー関数を書くような人間は初心者ではないし、下位互換を壊しても問題ないのではないかと思うのだが。

N3599: Literal operator templates for strings

現行規格には、文字列をテンプレート実引数をして受け取るリテラルオペレーターテンプレートはない。当時はそれなりの理由で却下されたのだが、今思えばそれほど問題でもないし、需要もあるし、付け加えてはどうか。

ちなみに、今あるのはこのようなテンプレートだ。

template < char ... pack >
void operator "" _str() { }


int main()
{
    123_str ; // call operator ""_str< '1', '2', '3'>
}

これを、"abc"_strと使えば、 operator ""_str< 'a', 'b', 'c'>が呼び出されるようにしたいということだ。

ただし、この論文で提案されているテンプレート宣言は、たんにchar型の非型Variadic template parameterではなく、以下のようになる。

template<typename CharT, CharT ...String>

一つ目のテンプレート仮引数に文字型が入り、続いて文字がVariadic templateで渡される。

N3600: C++ Latches and Barriers

標準ライブラリにlatcheとbarrierを追加する提案。

latcheとは、ある操作が完了するまでスレッドをブロックする仕組みである。latcheのオブジェクトは使い捨てであり、再利用できない。

barrierはlatcheに似ているが、オブジェクトを再利用できる。

N3601: Implicit template parameters

N3405の改定版。現在、任意の型の非型テンプレート仮引数を取りたい場合、ユーザーに型を明示的に指定させなければならない。そのため、template<typename T, T t>という形のテンプレート宣言が使われる。しかし、ユーザーに型を指定させるのは面倒なので、型を推定させる機能を追加する提案。

template < typename T, T t >
struct temp { } ;

struct X
{
    void f( ) ;
} ;

using t1 = temp< decltype(&X::f), &X::f > ;

のかわりに、

template < using typename T, T t >
struct temp { } ;

struct X
{
    void f( ) ;
} ;

using t1 = temp< &X::f > ;

と書けるようになる。Tの型は、tに対するテンプレート実引数から推定される。

あれば便利な機能だ。

N3602: Template parameter deduction for constructors

これもN3405の改訂版。これも昔から要望されている機能で、クラステンプレートのコンストラクターに渡された実引数から、テンプレート実引数を推定する機能。

例えばこのコード。

std::tuple< int, int, double > t( 1, 2, 3.0 ) ;

何をどうしようと、コンストラクターへの実引数の型は、コンパイル時に決定できる。ならば、このようなテンプレート仮引数の型の実引数をコンストラクターで受け取るようなクラステンプレートならば、コンストラクターの実引数リストから、テンプレート実引数リストを推定できるはずだ。ぜひともそうしようではないか。

std::tuple t( 1, 2, 3.0 ) ; // std::tuple< int, int, double >

これもあれば便利な機能だ。

N3603: A Three-Class IP Address Proposal

先ほど解説したIPアドレスを表現するライブラリの設計に関する議論N3565で提案されている。3クラス設計による提案。

N3604: Centralized Defensive-Programming Support for Narrow Contracts

実行時チェック用のライブラリを追加しようという提案。どの程度チェックするかとか、チェックに引っかかった場合の挙動を実行時に変更できるようにするだとかの機能がある。

提案では、高機能版assertプリプロセッサーマクロのようだ。

プリプロセッサーの廃止をこの目で見たい私としては、プリプロセッサーを使ういかなる提案にも反対する。

N3605: Member initializers and aggregates

メンバー初期化子とアグリゲート初期化を同時に使えないという制限を緩和する提案。

struct X
{
    int a ;
    int b ;
    int c = 3 ;
} ;

int main()
{
    X x = { 1, 2 } ; // エラー、コンストラクターがない
}

メンバー初期化子を使うとアグリゲート初期化できなくなる理由は、メンバー初期化子の存在するクラスはアグリゲートの定義から外れるからだ。Xがアグリゲートではない以上、上記はアグリゲート初期化ではなくリスト初期化となり、リスト初期化用のコンストラクターを探すが、発見できずill-formedとなる。

しかし、単純にメンバー初期化子の非存在をアグリゲートの条件から外すと、アグリゲートとは何ぞやという哲学上の問題に発展してしまう。

それでも、この論文では、アグリゲートの定義を緩和して、メンバー初期化子が存在を許す変更の提案をしている。

[htmlもheadもbodyもないふざけたHTML、一応、html要素がないのはHTML5としては合法だが] N3506: Extending std::search to use Additional Searching Algorithms

N3411の改訂版。内容は、検索アルゴリズムとして有名なBoyer-MooreとBoyer-Moore-Horspoolを追加する提案。

N3607: Making non-modifying sequence operations more robust

std::equal, std::mismatch, std::is_permutationは、一組のイテレーターと、先頭要素へのイテレーターを取る。

これは、アルゴリズムの呼び出し元が、呼び出す前に、ふたつのrangeの範囲チェックを行わなければならない。

template < typename Iter1, typename Iter2 >
bool checked_equal( Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2 )
{
    if ( std::distance( first1, last1) != std::distance( first2, last2 ) )
    {
        return false ;
    }
    else
    {
        return std::equal( first1, last1, first2 ) ;
    }
}

呼び出し元で毎度毎度範囲確認をさせるぐらいなら、最初から、このアルゴリズムで二組のイテレーターを取ればいい話なのだ。二組のイテレーターを取るオーバーロードを追加しようという提案。つまり、このcheckd_euqalのようなオーバーロードが追加される。

N3608: exchange() utility function, revision 2

N3511の改訂版。非アトミック版の汎用的なアルゴリズム、exchange関数テンプレートを追加する提案。これは第一実引数として渡したオブジェクトに第二実引数を代入し、第一実引数の代入前の値を返す関数テンプレートである。

// 実装例
template< typename T, typename U = T >
T exchange( T & obj, U && new_val )
{
    T old_val = std::move( obj ) ;
    obj = std::forward<U>( new_val ) ;
    return old_val ;
}

前回との変更内容は、デフォルトテンプレート実引数の追加。

N3609: string_view: a non-owning reference to a string, revision 3

文字列を表現するクラスの提案。

文字列を受け取る多くの文脈では、文字列であることが重要なのであって、その文字列の内部表現がchar *だろうがstd::stringだろうが、どうでもいいことである。このような内部表現の差異を吸収して、文字列としてみせてくれるためのクラス、string_viewライブラリの提案。

どうも任意の型の文字列としての表現クラス、いわゆるto_stringライブラリではない模様。

N3610: Generic lambda-capture initializers, supporting capture-by-move

汎用的に使えるlambdaキャプチャー初期化子の提案。これはムーブキャプチャーのサポートも含むが、汎用的とあるように、ムーブキャプチャー以外にも使える。

ムーブキャプチャーは、C++WGの日本支部としても2009年にNBコメントを出したが、名前付き変数からのムーブは危険な機能だとして却下され、フィンランド支部も2010年にNBコメントを出したが、もはやこの段階での変更は手遅れとして却下された。

この提案では、ムーブキャプチャーをするには以下のように書かなければならない。

void f()
{
    std::vector<int> v ;

    [ v { move(v) }] { } ;
}

もちろん、これは汎用的とあるように、ムーブ以外の一般的な用途にも使える。

int plus_one( int value ) { return value + 1 ; }

int f()
{
    int x = 0 ;
    [ x = plus_one( x ) ] { } ;
}

どうも使い道がよくわからないが、まあともかく、上記のコードは誰もが期待するように動く。plus_oneはlambda式の記述時点で呼び出される。

なぜ&&を使わないのかというと、日本支部のNBコメントへの拒否理由と同じく、論文の主張するところでは、名前付き変数をlambda式の記述場所でムーブするのに&&のような一見無害そうに見える文法は危険だからという事らしい。

ムーブは明示的に書くべきだというのはわかるが、ただでさえキャプチャは冗長なのに、私はこんなに冗長なlambda式を書きたくないのだが。

現時点で、文面案はまだ出来上がっていないようだ。

N3611: A Rational Number Library for C++

有理数ライブラリの提案の改訂版。

[PDFチョベリバ] N2612: Desiderata of a C++11 Database Interface

C++でデータベース用の標準ライブラリに関する議論の声も上がっており、C++らしいライブラリのインターフェースはどのようになるのかという実験的な設計が行われている。この論文では、既存のデータベースライブラリのインターフェースを比較して、共通の必要なインターフェースを抽出する考察をしている。

[PDFチョムカ] N3613: "Static If" Considered

冒頭から、

最近C++に案されたstatic if機能は根本的に欠陥であり、採用は言語に壊滅的な被害をもたらす。この機能は一種類の文法で使われる文脈により異なる三種類のセマンティックを提供する。このセマンティックの主目的は、使わないブランチのパースを取りやめることである。これはプログラムの読解と保守とデバッグを困難にする。また、コンセプトのような機能の開発の将来性にも影響を与える。さらに、この機能の利用はC++のASTベースのツールの提供に深刻な障害をもたらし、故に支援ツールの点でC++を近代的な言語からさらに引き離す。C++を低級言語にするであろう。

この論文の目的は、static if提案がC++ソースコードに与える影響を検証するものである。

と、static ifの反対論となっている。

三種類のセマンティックとは、条件付きで取り入れられる文と、条件付きで取り入れられるインターフェースと、constrained tempalteである。constrained templateはConcept Liteで提供されるとして、その他のふたつは今プリプロセッサーに頼っている状態だ。プリプロセッサーに頼るよりは言語でサポートされたほうがいいと思うのだが。

ともかく、論文の主要は以下の通り。

一度コード中でstatic ifを使うと、それ以後その条件に依存するコードを書くたびにstatic ifを使わなければならず、コードが非常に読みにくくなる。

static_ifをサポートするならばstatic_forやstatic_while(ループ展開)もサポートしなければならないし、現にサポートしろという意見が上がってきている。。というのも、もしstatic ifだけサポートした場合、プログラマーはstatic ifとプリプロセッサーマクロを使ってstatic forをエミュレートしてしまうからだ。

すでにstatic_forやstatic_whileといった提案を耳にしている。static_switchもそう遠くはない。C++は低級となり、気まぐれなハッカーのお気に入りの遊び場に成り下がってしまう。

constrained template、つまりテンプレートに対する制約は、static ifよりもconceptで提供したほうがいい。

論文の結論は、static ifは却下すべきだとしている。

[PDFチョーMM] N3614: unwinding_exception

現行のstd::uncaught_exceptionは、実は正しくない。今現在stack unwindingが行われているかどうかを確認することはできるが、デストラクターの中から呼ばれた場合、そのクラスのオブジェクトがスタック上に構築されていてunwindingの一環としてデストラクターが呼ばれたのかどうかを知ることは出来ない。

そこで、std::unwinding_exceptionを追加する。これは、std::unwinding_exceptionがデストラクターの本体の中で呼ばれ、そのデストラクターがstack unwindingのために呼び出された場合にtrueを返す。

[PDFチョーSS] N3615: Constexpr Variable Templates

constexpr変数のテンプレート宣言を可能にする変更。つまり、constexpr変数テンプレートの提案。今までテンプレート宣言はクラスや関数やエイリアスの宣言に使えていたが、それに新たにconstexpr変数が加わるわけだ。これは、大きな変更ではなく、規格のごく一部の制限をとっぱらうだけで可能になる。

以下のように使う。

template < typename T >
constexpr bool is_int = std::is_same< T, int >::value ;

template < typename T >
void f()
{
    constexpr bool x = is_int<T> ;

}

現在、テンプレート引数を与えるコンパイル時定数というものを直接表現する方法がない。最も近いものは、

クラステンプレートのconstexpr staticデータメンバー

これは、クラスのメンバーとして使わなければならないのみならず、staticデータメンバーなのでクラス内部とクラス外部の二箇所で記述しなければならず面倒だ。

constexpr関数テンプレートの呼び出し

これは変数ではなく関数だ。

constexpr変数テンプレートは、パラメート化されたコンパイル時定数を変数として直接表現する力を与えてくれる。

N3617: Lifting overload sets into function objects

lifting operatorまたはlifting lambdaと仮に呼ぶ機能の提案。関数のオーバーロードのセットや関数テンプレートをそのままポリモーフィックlambdaに変換できる機能。

従来、関数のオーバーロードのセットや関数テンプレートを、テンプレート実引数として渡すのは非常に困難を伴うことであった。

void func( int ) ;
void func( double ) ;
template < typename T >
void func( T ) ;

void f( std::vector<int> & v )
{
    
    std::for_each( v.begin(), v.end(), &func ) ; // エラー、型が曖昧
}

これは、テンプレート実引数には具体的な型が必要だが、関数のオーバーロードのセットや関数テンプレートの場合、その具体的な型が決定できないからである。これを解決するには、明示的に望む型のポインターにキャストしなければならない。

std::for_each( v.begin(), v.end(),
    static_cast< auto (*)(int) -> void >( &amp;func )
) ;

あるいは、lambdaにラップする。この場合も、明示的に型を指定しなければならない。

std::for_each( v.begin(), v.end(),
    []( int value ) { func( value ) ; }
) ;

もし、型を特定できない場合は、メンバーテンプレートを持つクラスでラップする必要がある。

struct func_wrap
{
    template < typename T >
    void operator () ( T && t ) const
    {
        func( std::forward<T>(t) )
    }
} ;

template < typename Iterator >
void f( Iterator first, Iterator last )
{
    std::for_each( first, last, func_wrap() ) ;
}

面倒なこと甚だしい。幸い、ポリモーフィックlambdaの提案もあるが、結局自分で書かなければならないことに変わりはない。

std::for_each( first, last,
    []( auto && ... pack ) { return func( std::forward<decltype<pack>(pack) ) ; }
) ;

これをみると、関数のオーバーロードのセットや関数テンプレートから、ジェネリックlambdaへの変換は、機械的にできることは明らかである。このようなlambda式を生成するシンタックスシュガーがあればいい。そこで、そのようなシンタックスシュガーを提案する。

std::for_each( first, last, []func ) ;

'[] id-expression' という文法から、

[](auto&&... vs){ return id-expression(std::forward<decltype(vs)>(vs)...); }

というジェネリックlambda式を生成する機能。この提案は当然ながら、ジェネリックlambda式の採用を前提にしている。

これはぜひとも欲しい機能だ。

N3618: What can signal handlers do? (CWG 1441)

もともとCWG 1441だったが、ちょっと複雑になってきたので論文とした。内容はシグナルハンドラの制限を緩和する提案。現状では、シグナルハンドラはvolatileでatomicなローカル変数すら扱えない。それはさすがに変だろうという事で、atomic変数を使えるようにする。

N3619: A proposal to add swappability traits to the standard library

スワップ演算子であるoperator :=:の提案と関連して、型がスワップ可能かどうかを返すtype traitsのis_swappableとのis_nothrow_swappable追加提案。

[最後の最後までPDF] N3620: Network byte order conversion

バイトオーダー変換ライブラリの提案。エンディアンとも呼ばれている、

内容は、<net>ヘッダーに、std::net名前空間スコープに、htonとntohのuint16_tとuint32_t版をそれぞれlとsのサフィックスで別名をつけて提供。また、constexprテンプレート版のhtonとntohの提供。

Nokia、VP8が抵触する特許群を発表

IPR Details - Nokia Corporation's Statement about IPR related to RFC 6386

Nokiaが保有する特許の中で、VP8が抵触する特許の一覧を発表した。IETFがVP8について合意を得ようとしているこのタイミングにガツンと一撃食らわせた形だ。NokiaはMPEG LAに加入しておらず、またGoogleとライセンスする気もなさそうだ。

さっそくまとめて先例探しに燃えている人間がいる。

Groklaw - The Nokia Patents and VP8 - Prior Art Hunting Time ~pj Updated

2013-03-28

ますますクリエイティブコモンズが変な方向へ

「警察の萎縮効果狙う」 赤松健さん、2次創作同人守るための「黙認」ライセンス提案 (1/2) - ITmedia ニュース

ただでさえ誤解を招きやすいクリエイティブ・コモンズがますます誤解を招きやすい方向に進もうとしている。

まず、「クリエイティブコモンズ」というライセンスはない。実際には、CC-BY(著作者表記)を基本として、NC(非商用)、ND(非派生)、SA(コピーレフト)のオプションをつけて使うライセンス集であり、組み合わせ次第で非互換なライセンスになる。

このような非互換なライセンス集を、単にクリエイティブコモンズと呼ぶのは誤解を招き危険である。しかも、一部の条件であるNCやNDを使うと、それ以上の創作性が阻害される。商業利用や派生を禁止してクリエイティブとは何事か。ノンクリエイティブとかアンチクリエイティブに改名すべきである。

リンク先の記事によれば、

赤松さんとともにイベントに登壇したクリエイティブ・コモンズ・ジャパンの常務理事で弁護士の野口祐子さんは、「確かに、クリエイティブ・コモンズは漫画で採用されないと普及しない」と認め

とあるが、めでたくこの結果クリエイテイブ・コモンズが採用されたとして、それは今のクリエイティブ・コモンズが採用されたのではなく、新たに付け加えられた非互換ライセンスが採用されただけであり、問題はむしろ増加しただけだ。またもやクリエイティブ・コモンズを名乗る非互換ライセンスが増えてしまう。何で名前を変えないのか。ナアナアな文化をライセンスにしたので、ナアナア・ライセンスという名前はどうか。日本語の「なあ」はとても曖昧な意味であり、世界でも指折りの理解が難しい言葉に選ばれたそうだから、ピッタリの名前だといえるだろ、な? な?

さて、いままでナアナアでやってきた同人漫画業界に厳格な法律が持ち込まれるので、それに対応する。それは分かる。しかし、なぜナアナアな文化をそのまま引き継ごうとするのだろうか。

ライセンスは、法律の定める期間を限定した独占権、すなわち著作権を元にしている。著作権をたてにして上の許諾を与える条件としてこれこれの要求に従う契約/許諾である。それで黙認という弱い概念を定義してどうするというのか。

もちろん、海外にはfair useのような曖昧な概念を著作権法に含めている国もある。ただし、今回の提案は、そういう国に習って著作権法を改正するのではなく、現行の厳格な法律をたてにしたライセンスでやるのだから、もっと自由か不自由かに厳格になるべきだ。取締だけ厳格な法律に基づき、許諾だけはナアナアな文化のままというのは極めて危険だ。

この動きは、ますます本当の著作者を権利から引き離す動きを加速させるだけだ。

だいたい、不自由な漫画は、一部の人気作品を除いて20年後には入手不可能になる。中古市場を漁らねばでてこない。出版するほどの商業的利益が得られないのに塩漬けになっている。多くは保存されずに消えていくだろう。

私はもうそんな不自由な漫画を読みたくはない。20年後に入手できないような漫画を読んでも楽しめないからだ。

追記:この赤松提案のライセンスでは、デッドコピーの禁止という文面がある。これは思うに、未改変版(あるいは原著作物に直接手を加えたものや、原著作物の一部)の複製物の禁止ということだろう。記事中でも触れている。

 「クリエイティブ・コモンズ・ライセンスは基本的にデッドコピーを前提にしているが、原作の絵をそのままコピーされたり、切り貼りされるのは困る。キャラと設定だけ使った2次創作が認められるCCマークが欲しい」と新ライセンスを提案。

赤松健は漫画家だから、漫画をそのまま再配布されてほしくないのだろう。しかしそうなると。非互換なライセンス集であるクリエイティブコモンズの唯一の共通項である、「非商用での未改変版の再配布」を禁止することになる。

さらに考えると、デッドコピーの禁止は不思議な状況を生み出す。原著作物は赤松ライセンスで公開できない。なぜならば、著作物をデッドコピーすることはライセンス違反だからだ。たとえ原著作者であり権利を持っていたとしても、赤松ライセンスで明示的にデッドコピーを禁止している以上、赤松ライセンスで公開することは出来ない。自分で定めたライセンスに引っかかるからだ。赤松ライセンスは、原著作物ではなく、派生物のみを縛るライセンスとなる。これは、原著作物自体がそのライセンスとして公開されるクリエイティブ・コモンズとまったく性質の異なるライセンスである。

この赤松ライセンスをクリエイティブ・コモンズ傘下に付け加えることを許したならば、いよいよクリエイティブ・コモンズは曖昧で危険なライセンスになってしまう。

2013-03-27

セキュアブートと制限ブートの違いについて

mjg59 | Secure Boot and Restricted Boot.

Red Hat社員で有名なLinux開発者のMatthew Garrettが、セキュアブートと制限ブートの混同について警鐘を鳴らしている。

今週末、LibrePlanetでセキュアブートと制限ブートについての発表を行った。動画はここから手に入る。

いずれカンファレンスのWebサイトにも上げられるはずだ。スペインである団体が今朝、欧州委員会に苦情申し立てをし、Microsoftのx86クライアントPC市場におけるセキュアブートの強制は反競争的であると訴えた。この動きが成功することはないだろうと思うが、というのも、委員会はすでに発表したように、現在の実装はEU法に適合しているとの見解だからだが、私は来たるべき本当の戦いの論点を見失う危険性を懸念しているのだ。

セキュアブートの意味は、人により様々である。思うに、自由ソフトウェア財団の定義が分かりやすい。セキュアブートとは、ブート時の検証方法であり、最終的な支配権は、デバイスの所有者に帰する。一方、制限ブートとは、ブート時の検証方法であり、最終的な支配権が第三者の手に帰する。Microsoftがx86 Windows 8デバイスに要求する要件は、セキュアブートに分類される。OEMがマイクロソフトの要件を正しく実装したならば、利用者はセキュアブートを無効にすることができるし、あるいはセキュアブートを有効にするが、信頼するキーとバイナリは自分の意志により選択できる。もし、自由ソフトウェア財団が彼らの定義する自由に合致するオペレーティングシステムの署名サービスをたちあげれば、Microsoft要件ならば、利用者にシステムを設定して、不自由ソフトウェアの実行を拒否するようにできる。現に筆者のシステムは、Fedoraか自分がローカルでビルドしたものしか信頼しないように設定されている。MicrosoftがOEMに実装するよう要求したから実現した環境である。Microsoft要件を満たしたシステムは、コンピューター所有者の自由を尊重し、コンピューターのブートにおける制限についてユーザーが選択権を持つものである。

とはいえ、現状が理想なわけではない。ハードウェアベンダー間の共通のUIやキーフォーマットがない現状では、ユーザーが自由の権利を行使するために必要な手順を、OSベンダーがドキュメント化するのは難しい。Microsoftが主要な信頼キーの権威として君臨することは、果たしてMicrosoftは自社の製品と他者の製品を等しく取り締まるかどうかということに疑問を抱く余地が十分にある。一部のシステムにおける実装の不具合により、正しく署名されたオペレーティングシステムがブートしないこともあり、利用者にWindows以外のOSをインストールしたければ、ファームウェアのアップデートを強いることもある。

しかし、この些細な問題のみに注力するのでは、もっと大きな問題を見逃してしまう。x86市場は、利用者が実行するものを自分の意志で決められる環境を維持するだろうが、x86市場は縮小している。利用者はタブレット等のARMベースの極小ポータブル機を購入している。一部の利用者は、携帯電話を最優先のコンピューティングデバイスとして利用している。x86市場と違い、MicrosoftのARM市場における意向は、利用者の自由を制限することである。Windows PhoneとWIndows RTデバイスは、署名バイナリのみをブートし、利用者に署名検証の機能を無効にしたり、独自のキーをインストールすることができないよう、要件付けられている。内部の技術は同等であるものの、この違いにより、MicrosoftのARM実装は、制限ブートに分類される。ハードウェアベンダーとMicrosoftのみが、システムで実行されるべきソフトウェアを決定するのだ。利用者に決定権はない。

そして、残念なことに、これは独りMicrosoftだけではない。Appleというこの市場の最大のベンダーも、同等の制限を実装している。極一部のAndroidベンダーは、アンロック可能なブートローダーを搭載しているが、その他は、会社の意向か携帯キャリアの意向かはともかく、プラットフォームを囲い込んでいる。利用者が購入したデバイスは、セキュリティホールを利用するのでなければ、改変したシステムの実行を拒否するようになっているのだ。たとえ、システムが自由ソフトウェアを用いて作られたものであったとしても、利用者が自由の権利を行使できる保証はない。

なぜこれが問題になるのか。特定のプラットフォーム(特にWindows RTやiOSや特定のAndroidベースのデバイスだが)では、未署名のアプリケーションの実行すら拒絶するのだ。利用者は、まず厄介な制限を伴う契約に同意しなければ、自分でソフトウェアを書いて他人に配布することが不可能になるのだ。たまたま不幸の国に住んでいる利用者は、その機会すら禁止されるだろう。ベンダーは自社の製品と競合するアプリケーションをブロックし、もってイノベーションを阻害するのだろう。システムの詳細を学び、変更する機会が制限され、ユーザーが近代的なオペレーティングシステムの動作について学ぶことを困難にする。私の所有するまだ完璧に動作する携帯電話であっても、ベンダーによるアップデートが廃止されればそれで終わり、悪意あるWebサイトから攻撃を受けたり、パスワードや銀行クレジットカード等の暗証番号の漏洩の危険に晒される。しかも、たとえ他人にカネを払おうとも、問題を修正することは出来ないのだ。

利用者はこの制限により直接の被害を被る。

制限ソフトウェアに利点なしとは言わない。出荷されるデバイスのデフォルトの設定が制限されたものであってもかまわない。ただし私は、デバイス所有者には、囲い込みを受けるかどうかの選択の自由が、絶対に保証されていなければならないことを強く主張する。自由とセキュリティの間に、強制があってはならないのだ。

セキュアブートに反対する者は、我々の選択の自由を危険に晒す者である。セキュアブートに反対するものは、制限ブートにより我々の自由をさらに奪う危険を無視するものである。旧来のPC市場は廃れつつあるのだ。我々が行動を起こさなければ、自由ソフトウェアは、注意深く利用者の自由を尊重する珍しいデバイスをわざわざ探して使う少数派に成り下がってしまう。我々は10年前から、制限ブートに反対すべきだったのだ。これ以上、実際には利用者の自由を尊重する実装と戦って時間を浪費してはならない。

Microsoftのx86向けのセキュアブートの要件は、理想ではないにせよ、利用者の自由を尊重するものである。結局、出荷されるハードウェアにデフォルトで入っている公開鍵に対する秘密鍵を持つ、署名サービスの権威がMicrosoftというだけであり、Microsoft要件を正しく実装したハードウェアならば、セキュアブートを無効にしたり、あるいはMicrosoftの署名を拒否し、自分の選択する鍵だけを信頼するように設定できる。これにより、Microsoftが署名したバイナリの実行を拒否し、自由なソフトウェアのみを実行するデバイスにもできるのだ。

真に反対すべきなのは、ARMデバイスで主流の制限ブートである。これはデバイス所有者に当然あるべき、制限ブートを無効にしたり、あるいは信頼するキーを変更する自由を与えない。つまり、デバイス所有者はデバイスを支配していないのだ。かわりに、デバイスの提供者に一方的な支配力を与えるものである。このような邪悪なデバイスには、断固として反対しなければならない。

2013-03-26

要経験何年の求人はイギリスで違法

Don't specify X years of experience required - You're breaking the law - a blog post by Steve Buckley on the Hacker Jobs UK Blog

経験年数は技術力を保証しないが、そもそも経験年数を要求するのはイギリスでは違法だという。

今日はHacker Newsで経験年数の無意味さを啓蒙する記事をよく見る。

Hire talent, not five years with Java | | ?- BlogName = _.

経験年数の都市伝説はかのJeff Atwordも5年前に書いている。

Coding Horror: The Years of Experience Myth

KDEレポジトリ消失問題の記事の全訳:完璧過ぎるミラー

Too Perfect A Mirror - Me, my blog, and my Johnson

完璧過ぎるミラーと題して、今回のKDEのレポジトリが危うく消失しかけた障害について書いている記事があるので、参考のために全訳する。ただ、私にはgitの知識がないため、あくまで参考程度に。

(追記)

以下のテキストは公開時より書き換えられてはいないが、我々のバックアップ方法や失敗原因などの詳細に関する疑問に答えるために追記した。もし以前にこの記事を読んで、「おい、なんでバックアップ取ってねーんだ」と思ったならば、追記を読むといい。

当初公開した記事で説明し忘れたことがある。我々はレポジトリのtarballは持っている。tarballは数日おきに作成しているが、これは完璧なバックアップというわけではない。より詳しくは記事中で説明する。

これは、あやうくKDE大災害2013になりかけた今回の事件について振り返るために書いている。即座に書かれたブログ投稿、git.kde.orgが落ちたや、git.kde.orgが復帰、現状報告などで、事件の内容についてはすでに語られている。

以下が、事件の全容だ。

「なんじゃこりゃ?」

2013-03-22に、git.kde.orgの仮想マシンをホストするサーバーが、セキュリティアップデートのために落とされた。サーバー上で走る仮想マシンはどちらも問題なくシャットダウンされ、セキュリティアップデートがホストに適用され、マシンがリブートされた。

ホストが復帰してVMを起動した時、VMはすぐにファイルシステムが壊れている兆候をみせた。問題のファイルシステムはext4であった。ファイルシステムの問題が長期にわたって蓄積していたのか、ホストやVMの今回のシャットダウンやリブートに関連してのものなのかは、まだ分かっていないし、おそらくは永久に判明しないだろう、前者であることを伺わせる証拠があるものの、はっきり断言できるほどではない。

この文章を読んでいる者の多くが知っての通り、KDEには複数のanongitマシンがあり、その目的は、1500個ものgitレポジトリにかかるアクセスを分散してさばき、またメインサーバーのバックアップとしても機能している。しかし、anongitを確認したところ、すべてレポジトリが壊れており、ほとんどはレポジトリ自体がなくなっていたのだ。

何故こんなことが起こったのか。

完璧なミラー

あらゆるソフトウェアに言えるように、我々のミラーリングシステムにもバグがあった。そして、大方のバグに言えるように、問題が起こるまで存在に気がつかなかったのだ。

バグの原因は設計上の欠陥だ。git.kde.orgは常に信頼できる一次ソースであるという前提。この前提の理由は明白だ。git.kde.orgはしっかりと管理されており、プッシュされたコードはすべて妥当性を検証するよう独自のフックをかましていたのだ。常に正しいと仮定することは妥当な決定だった。

そして、ミラーリングシステムは、それほどの時間差なく、git.kde.orgと全く同じように振る舞うべく設定されていた。これはgitレポジトリのコードのみならず、管理上の多くのメタデータまでにも適用される。同期はおよそ20分ごとに発生し、ミラーリングシステムはanongitをgit.kde.orgと全く同じようにみせかけ、一次ソースが死んでいたり交換されたりしたならば、ミラーからレポジトリを同期するように設計されていた。

我々は、サーバーがディスクを紛失したり、あるいはサーバー自体が塵になったり、VMのファイルシステムが完全に消失した場合には備えていたものの、ファイルシステムの障害には備えていなかった。この障害の具合が、ミラーリングシステムに予期せぬ問題を引き起こしたのだ。

事象発生順序

  1. VM復帰。全レポジトリを格納するプロジェクトファイルが破損。
  2. anongit同期開始。同期のために、anongitは新しいプロジェクトファイルを取得したが、これは、興味深いことに、それぞれのanongitごとに違ったものとして見えていた。もちろん壊れていることには変わりないが。壊れ方というのが、プロジェクトファイルから、ほとんどのレポジトリが削除されたかのように見えていたのだ。
  3. 各anongitは、サーバーから削除されたレポジトリ(これは通常も起こりうる妥当な操作である)をローカルから取り除くため、プロジェクトファイルに入っていないローカルのレポジトリをすべて削除した。
  4. 原因はまだよくわかっていないが(おそらくは一部のanongitの同期間隔が他のものより頻繁だったのだろうが)、一部のanongitがレポジトリを再クローンし始めた。壊れたものをクローンしたのだ。これについては後ほど。

というわけで、障害は完璧にミラーされてしまった。いや、というより、結果的には、不完全にミラーされたという事だ。anongitの全データは失われてしまった。

ラッキーラッキーラッキーラッキーラッキーラッキーラッキーラッキー

ツイていた。

projects.kde.orgをホストし、anongitとも同期している(ただしユーザー向けではない)サーバーが、Hetznerデータセンターにあり、三年前から静的IPv4アドレスのブロックを専有していた。Hetznerは最近、IPv4アドレスの枯渇のため、IPv4アドレスブロックの利用には結構な額の利用料を徴収し始めていた。ハードウェアもだいぶ古くなったことだし、この機会に新しいより良いハードウェアで、しかもコストも同額のものにprojects.kde.orgを移行させようとしていた。

この事件が起こるちょうど一日前のこと、anongitのクローンシステムが、移行のための新しいサーバーを立ち上げていた。幸運はこれだけではない。この一台のサーバーは、ちょうど20分ごとに発生する同期すべき時刻にあたっていたのだが、たまたまサーバーのリブートが重なっていたのだ。その結果、最新のプロジェクトをフェッチするコマンドはタイムアウトし、渡されたスクリプトは、単にサーバーからレポジトリの最新リビジョンのフェッチを試みたが、これはサーバーが妥当なcustom packを返さなかったために失敗していたのだ。

本来ならば常時4,5個の完全なKDE Gitレポジトリのコピーが存在すべきはずなのに、git.kde.orgとその他のすべてのanongitが完全に死滅した中、この新しい一台のサーバーのみが、唯一オリジナルの1500のレポジトリのコピーを残していたのだ。

我々はすべてのレポジトリに対してgit fsckを走らせ、すべてを検証した。我々は安堵とともに、このレポジトリから、通常はanongitのメタデータ同期に使うスクリプトを使って、メタデータを含むgit.kde.orgを復旧できたのだった。

すべてが簡単に解決したわけではない。我々のgitoliteレポジトリも壊れてしまった。独自の変更を施した時代遅れのGitoliteを復旧するより、この機会に前々から必要とされていた、とっくの昔に行うべきだったアップグレードを行うつもりだ。これにより、有益な機能が追加されたナイスなGitoliteのバージョン3を提供できる。ユーザー向けのコマンドに、いくつか文法上の変更がある。この詳細については別のブログ投稿で書くつもりだ。

特に、cloneコマンドは現在移植されていない。このことについては後ほどGitoliteの作者Sitarama Chamartyと話し合って、移植をスムーズに行えるようにしなければならない。

Gitoliteについてもうひとつ、緊急のGitoliteの復旧中に、いくつかのトラブルに見舞われた。Sitaramは最優先で我々を助けてくれた。彼はKDEの素晴らしき友であり、ここに謝意を表する。

復旧

さて、問題については分かったので、解決しなければならない。残念ながら、解決はそれほど簡単ではない。

簡単な変更もある。すぐに行ったこととしては、プロジェクトファイルにチェックをかけるようにした。もし、新しく生成されたプロジェクトファイルが、以前のものより1%以上違っていた場合は、以前のファイルも保持するようにした(1500ものレポジトリがある環境では、これは3分以内に15個ものレポジトリを新たに作成するか削除するかしなければならない。これは通常ありえないことである)。このチェックはanongitにも適用する必要があるが、それは長期的な変更の一貫として検討中だ。

大きな問題は、障害の検出だ。プロジェクトファイルは大幅に変わらなかったとしても、ちょっとした障害で、たとえばすべてのanongitからふたつのレポジトリが削除されたりする。たとえ検出して再クローンしたとしても、anongitは壊れた方のクローンを所持することになる。つまり・・・

Gitは思ったほど安全じゃない

(追記:この項目について執筆した後、Git開発者達は、私がテストに使っていた手法について検証した。その結果、私のやり方に誤りも発見された。--no-localのかわりに--no-hardlinksを使っていた。これはふたつの挙動の混同に起因する誤用である。そういうわけで、この項目の情報は完全に正しいというわけではない。とはいえ、疑いなく問題が発生したのに、gitがcloneでexit code 0を返す場合もあるということだ。詳しくはこちらを参照。元の文章は以下の通り)

Gitはかなり安全である。しかし、問題のある操作を行えてしまうこともある。これは、通常のユーザーやシステム管理者には、一見問題のないようにみえる。私はちょっとしたテストを行い、なぜanongitによる再クローンが壊れたのかについて調べた。以下がその内容である。

Corruption of Commit Objects

commit objectが壊れても、何の警告もなくレポジトリをミラークローンできてしまう(しかもexit codeはゼロだ)。そしてツリーを辿ろうとすると、いずれコミットが壊れているエラーに突き当たる。しかし、こ個々が重要なところだが、エラーになるのは、その問題のコミットを含むツリーをたどった時だけだ。何もロケット工学を持ちだしてまで調べるほどじゃない。これが問題になる理由は明白であるが、これはつまり、妥当性を検証したい場合は、ツリーのすべてのrefを辿らなければならないという事だ。

リーナス・トーバルズのGoogle Tech Talkの発表から引用する。

ディスク障害があろうと、メモリー障害があろうと、どんな問題があろうとも、gitなら検出できる。疑念の余地はない。保証されているのだ。悪意ある人間がいようとも、決して成功することはない。たったの20バイト知っていればいい。ツリーを表す160ビットのSHA-1さえ知っていれば、上から下までの完全な記録としてツリーを信頼できる。10年分の記録があろうと、100000個のファイルがあろうと、何百万のリビジョンがあろうと、すべてを信頼できるのだ。なぜならば、gitはとても信頼性があり、基本的なデータ構造はとてもとても単純だからだ。チェックサムすらチェックしている。

リーナスは正しい。ツリーをたどって上から下までの全履歴を信頼できる。ただし、Gitレポジトリは巨大なrefを含むことがあり、その場合はすべての検証しなければ、レポジトリ全体を信頼することはできない。

git fsckで問題を検出できる。

Corruption of Blob Objects

これも問題は同じだが、ひとつ重要な違いがある。この状況では、レポジトリのミラークローンが警告なしに通ってしまうが、ツリーを辿ろうとすると、最初のコミットまでさかのぼるrevlistnにぶちあたる(このブロブをみるのはref/treeだけだ)

これも、git fsckで問題を検出できる。

Mirror clones

ミラークローン(--mirrorフラグをつけてクローンすること)は、クローンをupstreamのレポジトリのrefまで含めて最新にすることである。新しいrefはdownstreamのレポジトリにpullされ、forced updateまで含むすべての変更が、downstreamレポジトリにミラーされる。

しかし、どうもミラークローンはクローンとは別の仕組みらしく、custom packではなく、オブジェクトが単に愚直にコピーされるらしい。その結果、ミラークローンはレポジトリの安全検証を素通りしてしまう。upstreamでの障害はdownstreamの障害になり、exit codeはゼロだ。

将来に向けて

もちろん、KDEシステム管理チームは現在、この災害を将来回避するための方法について議論している。

真先に施された改良としては、anongitはごく最近の障害に備え、24時間前の同期結果を保持するようにしたことだ。これにより、レポジトリを相当最近のリビジョンまで復旧できる。projects.kde.orgの移行先のマシンはZFSファイルシステムを使っており、同期するたびにスナップショットをとる。スナップショットの数は十分な量確保される。これにより、障害からごく近い時点まで復旧できるはずだ。

しかし、これはどちらも付け焼刃的な対応だ。だいぶ前にサーバーで発生した障害は、anongitに伝播するのに十分な時間があり、新しい変更で古い変更を上書きしてしまったり、何らかの理由で全消ししてしまったりする。同期するレポジトリの数を、前回から変更のあったものだけに限定することも検討しているが、これは単に、問題の伝播速度を遅らせるだけだ。同期する前に発生した障害にはどうしようもない。これにより、anongitに伝播した障害ならば巻き戻せるが、これは今回対処した、Gitoliteのref logを使う障害のひとつに対応したに過ぎない。(もちろん、我々は頻繁に障害を起こしているわけではないが、いずれ起こすだろう。同期と同じ頻度でバックアップしない理由はないし、念のためにスナップショットもとっておく)

ミラークローンの利用をやめるということも考えられる。もともと、ミラークローンは使われていなかったのだが、ミラーではないクローンをanongitで使うと、その他のlegitimate, authenticated force pushes, ref deletionsなどの問題もあり、色々と都合が悪かったのだ。それにrefspecを静かに通すのでは、あまり意味がない。ミラーではないクローンと、それに続くミラーモードクローンで、レポジトリ全体をpackで検証するような、ハイブリッド的なアプローチも検証する価値があるだろう。

各レポジトリへの変更を記録して、同期による巻き戻しが妥当なものかどうかを検証することもできたかもしれない。これは可能な話だが、やたらに複雑で、問題も多いだろう。

git fsckをサーバーとanongitで頻繁に走らせるという事もできたかもしれない。projects.kde.orgのマシンのように、スナップショットを取るときに、スナップショットの保持期限が過ぎる前にgit fsckを走らせれば、すべてのレポジトリで整合性あるスナップショットが取られ、ロールバック可能であることが検証できる。ただ、これは障害を検知するために頻繁に行うにはかなり重い処理だ。

projects.kde.orgのマシンで使っているように、git.kde.orgにもZFSを使うことも考えられる。ZFSにはチェックサムがあり、ハードウェアとファイルシステムレベル両方の障害を検出可能だ。これはディスクやメモリーはいずれは故障するという前提にたつ設計であり、ひそかに長年ビット反転を起こしていたような不良品を検出できることで有名だ。ZFSのRAID-Z機構はRAID Write Holeの問題もないし、十分なメモリがあれば、リード性能も素晴らしく、手軽に使えるCopy-on-Writeスナップショットも備えている。KDE Gitレポジトリの容量はたったの25GBであり、スナップショットをとってもそれほどの容量を圧迫せず、サーバーサイドでは悪くない。今すぐにでも使いたいぐらいだ。ただ、ここ数年Linuxですばらしい体験をした身としては、私は今ではZFSオタクの一人に過ぎないし、git.kde.orgを走らせているSUSEでどの程度サポートされているのかもわからない。(私はGentoo、Debian、Ubuntuで問題なくZFSを使えているものの)

とにかく、検討すべきことが山ほどあるのだ。

KDE、あやうくレポジトリを失いかける

Too Perfect A Mirror - Me, my blog, and my Johnson

追記:上記記事の全訳
本の虫: KDEレポジトリ消失問題の記事の全訳:完璧過ぎるミラー

追記:この記事は上記のブログ記事にざっと目を通して素早く書いたものであり、詳細を欠く。上記の記事は全訳しているので、より正確で詳細な内容のために、目を通すべきである。

2013-3-22に、git.kde.orgをホストしている仮想マシンをセキュリティアップデートのために一旦落とした。アップデート後に復帰させてみると、ファイルシステムが壊れていたらしく、KDEの1500以上ものレポジトリが消えていた。

問題は、この問題が気づかれぬまま復帰したので、ミラーサーバーが誤りをそのままコピーしてしまったことだ。

ミラーは正しいバックアップではない。

とてつもなく幸運なことに、この問題が起こる一日前、ミラーサーバーのたったの一台に、更新作業中でミラーできる状態でないものがあった。このミラーサーバーは20分毎にマスターからフェッチするようになっていたが、たまたま再起動がかかったために、ミラーを完了せずに中断されていたのだ。

通常ならば、世界に4,5個は存在するはずのKDEレポジトリの完全なコピーが、たった世界に一つしか存在しない状況にまで追い込まれてしまった。

抜本的な改良が必要とされるが、とりあえずは付け焼刃に、現在より1%を超える変更があった場合、以前のレポジトリも保持するという変更がミラーサーバーに加えられた。また、24時間毎にスナップショットをとって保存するという処理も付け加えられた。

その他、様々な教訓や今後の目標などが書かれている。

2013-03-25

SlackwareがMySQLからMariaDBに移行

The Slackware Linux Project(どうも個々のニュースに対するURLがないのでいずれ正しくなくなるURL)

SlackwareがMySQLからMariaDBに移行するようだ。

今日のSlackwareの-currentのChangeLogより

今回の大ニュースは、MySQLを消してかわりにMariaDBを入れることだ。別に驚くべきことでもない。LQでのアンケートでは、大多数のユーザーが変更を支持している。思うに、MariaDB財団の方が開発に優れ、またセキュリティ上の懸念への反応も速く、オープンソースコミュニティと積極的に協力する姿勢を見せている。現在、MySQLのコミュニティ版を商用利用するにあたってライセンス上の問題はないとはいえ、LQのスレッドでは懸念されているし、MariaDBのソフトウェア利用の自由に対する態度は明白である。移行のためのビルドスクリプトとテストを行ったHeinz Wiesingerに感謝する。彼はMariaDBとその開発者と数年に渡り協力してきた。Vincent Vattsは早くからこの議論に参加していた。Vatssは去年、MariaDBのDaniel Bartholomewと列車の中で会い、ソースコードを渡され、旅の時間つぶしに遊んだのだ。ああ、USBメモリの奇跡なるかな。そして、MariaDB本体のみならずSlackwareでの好意的な反応にも驚かされたのだ。MariaDBの開発者達は我々のいかなる懸念に対しても対応してくれるだろう。ほとんどの環境で、MariaDBは既存のMySQLデータベースと完全に互換であり、一切の変更なくして置き換えることができる。MariaDBとMySQLの違いについて解説した記事があるので、一読を推奨する。

https://kb.askmonty.org/v/mariadb-versus-mysql-compatibility/

MariaDB財団には感謝する。協力も楽しみだ。

MariaDBに関する詳しい情報については、公式Webサイトを参照されたし。http://mariadb.org

最近よく、MySQLからMariaDBへの移行の話が聞こえてくる。まずWikipediaで次にFedora、OpenSUSEもデフォルトをMariaDBに切り替えたし、そしてSlackwareだ。

本の虫: WikipediaがMySQLからMariaDBに移行中
本の虫: Fedora 19のデフォルトのDBパッケージをMySQLからMariaDBに切り替える提案

2013-03-24

Linux Mint Debianはセミローリングリリース

The Linux Mint Blog » Blog Archive » Linux Mint Debian 201303 released!

DebianベースのLinux Mintが公開されている。

DebianベースのLinux Mintは、セミローリングリリースというモデルをとっているそうだ。これは、Debianのtestingから、さらにLinux Mint開発者が多少のテストをして検証したものをアップデートパックとして配布するのだそうだ。もちろん、ユーザーが望めば、testingやunstableをソースレポジトリにすることも可能だという。

Linux MintはUbuntu互換ではないが、Debian互換であり、Debian用のレポジトリをそのまま使うことができる。

Ubuntuベースに比べた利点は、セミローリングリリースなので再インストールする必要がなく、また動作も軽快だとしている。

欠点は、ユーザーはLinuxやdpkgやaptの知識を求められる。DebianはUbuntuほどデスクトップ利用に重きを置いていないので、不便なところもあるかもしれない。EFI、GPT、セキュアブートのサポートがない。

また、インストーラーはDebianベースのために新たに書かれているそうだ。

GCC 4.8でぶっ壊れるSPECのお粗末なコード

本の虫: GCC 4.8のリリースノートとC++関連の変更で、GCC 4.8は464.h264ref: SPEC CPU2006 Benchmarkを壊してしまう。これはSPECベンチマークの規格違反によるものであると書いた。では、具体的に何なのか。それを解説している記事を発見したのでかいつまんで紹介。

Embedded in Academia : GCC pre-4.8 Breaks Broken SPEC 2006 Benchmarks
Regehr: GCC 4.8 Breaks Broken SPEC 2006 Benchmarks [LWN.net]

どうやら、SPECベンチマークは以下のようなコードを含むそうだ。

int d[16];
 
int SATD (void)
{
  int satd = 0, dd, k;
  for (dd=d[k=0]; k<16; dd=d[++k]) {
    satd += (dd < 0 ? -dd : dd);
  }
  return satd;
}

問題は、for文のd[++k]だ。このfor文では、k==15のときにdd=d[16]が実行されてしまう。dの型はint [16]であり、添字として有効なのは0から15までの間である。d[16]は未定義動作である。

さて、GCCはどう考えるかというと、まずd[++k]をみて、kの取りうる範囲は-1から14までであると判断する。なぜならば、dに対する0未満16以上の添字は違法だから、d[++k]として使われているkの範囲は当然、合法な範囲まで狭めることができる。違法なコードは存在しないという立場にたてば、この判断は正しい。とすると、ループの終了条件であるk<16は、常に真である。なぜならば、kの取りうる範囲は、解析結果から、決して16以上にはならない。ループの終了条件が真ということは、これは無限ループだと判断できる。どうせ常に真になる式で無限ループなら、真におきかえてしまえばいいのだ。わざわざk<16というコードを馬鹿正直に実行して、比較と条件分岐をするには及ばない。いざ無限ループとしてコードを生成しよう。

と、こうした理由で、for文が無限ループになり、結果として規格違反のコードが壊れる。これはお粗末なSPECベンチマークのコードのせいである。

ちなみに、静的解析と最適化オプションは結びついているらしく、GCCは-O2ではこのコードに対してコンパイル時警告を出さないが、-O3ではコンパイル時警告を出すそうだ。最適化オプションが高くないとコンパイル時警告を出さないというのは、コーディング中やデバッグ中には最適化オプションを高くしない(あるいはできない)という慣習から、気が付きにくいのではないか。

思うに、for文のオペランドにやたらに複雑な式を書きたがるC言語の慣習が悪い。

FAQ CPU2006

SPEC側は愚かにも、これはC99違反であるがC89には違反していない、故にコードは修正しないと宣言。自分たちは問題の本質を理解できないと堂々と宣言している。

このコードの挙動はC89だろうと未定義である。ということは、SPECベンチマークとは、未定義動作の実装ごとの比較を目的としたソフトウェアなのだろう。

2013-03-23

SFW: 台湾ではポルノに著作権性はない

台湾では、ポルノに著作権性は認められないそうだ。

台湾ではこれまで、1999年にポルノ動画は公序良俗に反し、著作権法の対象である文学・科学・芸術などの著作に該当しないため、その保護は受けないとされた刑事判決事例がある。

台北地検「アダルトに著作権なし」、日本販売元訴え不成立-中央社日文新聞

ちなみに、アメリカでも同様の主張をしている裁判があったはずだ。たしか、アメリカの著作権法では、著作権は科学やart(学術、芸術)の発展のために認められるものであるとされているので、その条件に当てはまらないポルノには著作権性はないという論法だったはずだ。どうなったことやら。

2013-03-22

極端な一様乱数ジェネレーターを作る

C++11の乱数ジェネレーターは、自前で実装できる。要件さえ満たしていればいいのだ。

一様乱数ジェネレーター(Uniform random number generator)は、ディストリビューションで使う最低限の要求であり、これさえ満たしていれば、ディストリビューションで使える。より広いエンジンの要求は、初期化方法とかのディストリビューションからの利用には必要ない要件を定義している。

ジェネレーターに必要な要件は以下の通り

Gをジェネレーターの型、gをGの値とする。TをGのresult_typeの型とする。

  • 式G::result_typeの結果はT、Tは符号なし整数型(コンパイル時)
  • g()はG::min()とG::max()の間の値を返す(償却定数時間)
  • G::min()はGのoperator()が返す最低値(コンパイル時)
  • G::max()はGのoperator()が返す最大値(コンパイル時)

G::min() < G::max()が成り立つこと。

つまり、以下のような極端なジェネレーターも規格の要件を満たしている。

// 0か1しか返さないジェネレーター
class binary_generator
{
private :
    std::mt19937 engine ;

public :
    using result_type = unsigned int ;
    static constexpr result_type min() { return 0u ; }
    static constexpr result_type max() { return 1u ; }

    result_type operator () ()
    {
        std::uniform_int_distribution< result_type > dist( min(), max() ) ;
        return dist( engine ) ;
    }

} ;

これは正しいジェネレーターである。実際に使ってみる。

int main()
{
    binary_generator gen ;
    std::uniform_int_distribution< int > dist( 1, 100 ) ;

    for ( int i = 0 ; i != 10 ; ++i )
    {
        std::cout << dist(gen) << std::endl ;
    }
}

ちゃんと動いているようだ。ちなみに、まともなuniform_int_distributionの実装ならば、一回のdist(gen)の実行につき、最低でも7回はジェネレーターのoperator ()を呼び出すはずだ。というのも、呼び出し一回に付き1bitの乱数しか得られないので、1から100までの100種類の乱数を正しく生成するためには、最低でも7bitの乱数が必要になるからだ。

C++11の標準ライブラリのディストリビューションは、このような極端なジェネレーターにもちゃんと対応している。読者は標準で用意されているディストリビューションを正しく使い、間違っても安直な剰余を使ってはならない。

// このようなコードを書くものは、海賊船に乗れず溺れ死ぬべきだ。
// また、彼らはただ死んで終わるものではない。
// 唯一神空飛ぶスパゲティモンスターが地獄のビール休火山に投げ込む者達だ。
// 彼らの支持者も同様だ。
// 理由は経験科学に基づかない者らには経験科学に基づかない死がふさわしいからだ。
// 詳しい理由はN3551などで熟知すべし。
int main()
{
    std::default_random_engine engine ;
    // 6面ダイスのつもり。
    // 明らかに、海賊服も眼帯も身につけたことがないカナヅチの書いた祝福されぬコードである。
    std::cout << 1 + engine() % 6 << std::endl ;
}

以下のように書くこと。

int main()
{
    std::default_random_engine engine ;

    // 1から6まで一様に分布する乱数
    std::uniform_int_distribution< int > dist( 1, 6 ) ;
    // 6面ダイス
    std::cout << dist(engine) << std::endl ;
}

2013-03-21

common_typeとdecay

common_typeは規格によって実装方法がソースコードで示されている。しかし、GCC 4.8のlibstdc++では、なぜかcommon_typeにdecayを使っている。何故そんな規格違反な実装をするのかというと、現行のcommon_typeに深刻なバグがあるからだ。

C++ Standard Library Active Issues List: 2141. common_type trait produces reference types

現行の定義では、common_type<int, int>::typeがint&&になってしまう。これはもちろん大多数のユーザーが意図していない。しかも、std::unique_ptrのoperator <が壊れる。

というわけで、規格違反ながらも対策しなければならないのだ。

まともに動く実装があったコア言語機能を使うライブラリがこれだから、コンセプトがあのまま残っていた場合を考えると恐ろしい。あの当時の状況でコンセプトのライブラリがバグフリーだなんて考えられない。

C++11における空のリスト初期化

最近、C++WGの論文で空のリスト初期化の利用がとてつもなく増えている。なんでも空のリスト初期化の感がある。

T t { } ;

空のリスト初期化の利点は、アグリゲートならアグリゲート初期化の結果としてゼロ初期化されるし、クラスならばデフォルトコンストラクターが呼ばれるし、その他の型ならばとりあえずvalue初期化の結果としてゼロ初期化されるし、まあ便利な初期化だ。これまで、初期化子を指定しない場合、コンストラクターのない型でautomaticやdynamic storage duraionを持つオブジェクトは、indeterminate value(よくわからない値)になるので、空のリスト初期化を使うだけで最悪ゼロ初期化できるのだから、そりゃ使うしかない。

2013-03-20

std::make_integer_seqをO(LogN)にしろという議論

Jonathan Wakelyの書いた論文、N3493: Compile-time integer sequencesで、Variadic TemplatesとTupleを組み合わせた場合に、Tupleの各要素にパック展開でアクセスできるようにする技法のためのライブラリを、標準ライブラリで提供しようという提案がなされた。この論文は、本の虫: C++ 2013-01 mailingの簡易レビューで要約している。

これに我らが縄文土器陶芸家でメタプログラマーのボレロ村上氏が、std::make_integer_seqはO(LogN)の再帰深度を保証しろと注文をつけた。

ISO C++ Standard - Future Proposals に投稿した - ボレロ村上 - ENiyGmaA Code

私の簡易レビューでも、make_integer_seqの実装例を書いているが、これはO(N)な実装であり、氏のメタプログラミングでレイトレやPCM生成といった処理に使うには適していない。

O(LogN)の実装は、私の理解が正しければ、バイナリサーチ的な手法でインスタンス化の低減をしているようだ。

その議論の結果だが、氏がまとめる気配がないので、とりあえず紹介しておこうと思う。

O(LogN)な実装にするというのは、論文筆者を始めて、名だたるC++プログラマー達から賛同を得た。その中で、R. Martinho Fernandesが言うには、

そんな保証の要求に同意するなら、なんでユーザーコードじゃ実現不可能な保証にしないんだよ。なんでO(1)インスタンス化保証にしないんだ?

これは当然の話で、コンパイラーの支援を受ければ、O(1)は造作もないことだ。標準ライブラリに入る以上、コンパイラーの支援を受けた実装になっていても何の問題もない。

Jonathan Wakelyの回答としては、「提案は既存の手法に裏打ちされたものであるべきで、何でもかんでも袖を広げすぎると、宇宙の熱的死が訪れるまで採用されない」と答えている。それも当然の話で、じゃあライブラリじゃなくてコア言語で提供しろよとか、そもそもテンプレートじゃなくて、もっとメタプログラミング前提の機能で提供しろよとか、キリがない。

O(1)に反対しているわけではないし、この提案を議論する際にはもちろん言及するつもりだが、コンパイラーマジックを要求する前に、標準化委員会の反応をみたいのだ。この機能が受け入れられて、ユーザーに重要視されたなれば、コンパイラー側としても、より良い実装のために、O(1)実装のmake_integer_seqを提供するかもしれない。

正直なところ、汎用的すぎるgeneric integer_seq<T, T...>なんかいらないだろ、int_seq<int...>かuint_seq<unsigned...>で十分だろという反応を予想していたので、そういうことには触れず、「こいつァいい、もっと高速化しろ!」という反応には喜ばしい限りだ。

と答えている。

ところで、ISO/IEC JTC1/SC22/WG21 - Papers 2013が公開された。今回の論文は90本以上あるので、恒例の簡易レビューは時間がかかりそうだ。

2013-03-19

Mirに対するX.orgの姿勢は静観

X.Org Wiki - BoardOfDirectors/IrcLogs/2013/03-07

X.OrgのIRC上の会議で、Mirに対する態度としては、静観という雰囲気になっている。

まあ、X.orgとしては、現時点でMirに対して行うことは何もないだろうから、成り行きを見守るしかないのだが。

果たして、Mirは予定通り、12ヶ月後にデスクトップでも使えるようになるのだろうか。

2013-03-17

GCC 4.8のリリースノートとC++関連の変更

GCC 4.8 Release Series — Changes, New Features, and Fixes - GNU Project - Free Software Foundation (FSF)

GCC 4.8のリリースノートから興味深い点を紹介する。

まず、GCC 4.8は、C++で書かれるようになった。これはつまり、GCC 4.8のビルドには、C++03に準拠したコンパイラーが必要になる。

GCC 4.8は、ループ最適化の方法として、ループ回数の上限をaggressiveに解析するようになった。これは言語規格で許されている範囲の実装である。これにより、既存の規格違反のプログラムが正しく動かなくなる。たとえば、SPEC CPU 2006の464.h264ref (H.264のリファレンスエンコーダーを流用したSPECのベンチマーク)や、同じくSPECのベンチマークである416.gamessが動かなくなる。この最適化を無効にするオプション、-fno-aggressive-loop-optimizationsが追加されている。

C++としては、

thread_local

struct X
{
    int x ;
} ;

thread_local X x ; // スレッドごとに別のストレージ上に確保される

attribute(alignasも含む)

[[ noreturn ]] void f() ;

alignas(16) char a[64] ; // 16バイトにアライン
alignas(double) char b[sizeof(double)] ; // doubleのアライメント要求を満たすようにアライン。

Inheriting Constructors(継承コンストラクター)

struct A { A(int) ; } ;
struct B : A { using A::A ; }

B b( 123 ) ; // OK

名前通り、基本クラスのコンストラクターを継承することができる。

さらに、次期C++規格の実験的な実装を有効にする。-std=c++1yオプションが追加されている。現在のところ、通常の関数でも戻り値の型の推定を行う提案、N3386が実装されている。lambda式ができるのだから、普通の関数でもできるべきだろう。

// C++11の機能
// 戻り値の型がreturn文からintに推定される
[](){ return 0 ; } ;

// N3386の提案
// 戻り値の型がreturn文からintに推定される。
auto f() { return 0 ; }

また、N3386では、関数本体に複数のreturn文が使われていたり、再帰する場合でも、return文の推定ができるようになる。

[]( bool b )
{
    if ( b )
    {
        return 100 ;
    }
    else
    {
        return 200 ;
    }
} ;

この場合、どちらのreturn文もint型であるのだから、当然戻り値の型は推定できるべきだし、

auto f( unsigned int n )
{
    if( n == 0 )
    {
        return 0 ;
    }
    else
    {
        return f( n - 1 ) ;
    }
}

このような再帰関数の場合も、やはり戻り値の型はint型以外にはありえないから、当然推定できる。

また、inline namespaceは以前から実装されていたが、それを受けてGCCの独自拡張である__attribute ((strong))はdeprecated扱いになった。

また、ライブラリとしては、forward_listが実装されたり、randomの実装がSSE最適化されたりしているそうだ。