2014-02-28

2014-01-pre-Issaquah mailingの簡易レビュー Part 2

2014-01-pre-Issaquah mailingが公開された。

前回に引き続いて、C++WGの論文集を解説していく。

N3863: Private Extension Methods

プライベート拡張メソッド(Private Extension Methods, PEM)は、privateな非virtualメンバー関数を、クラス定義の外で定義できる機能だ。

以下のようなコードを考える。

// Foo.h
class Foo
{
public :
    int get_secret() ;

private :
    void set_secret( int value ) ;
    int secret ;
} ;

時に、このヘッダーファイルは、ユーザー側から利用される。そのようなヘッダーになぜ、privateなメンバーの情報を書いておく必要があるのか。よいクラスの条件とは、なるべく多くを隠匿することにかかっている。ユーザーにとって必要最小限のインターフェースしか提供しなければ、ユーザーにとって覚えることが少ないので使いやすくなるし、ユーザーがつまらない低級な実装の詳細にかかわらずにすむようになるし、実装の自由度も上がる。

privateなメンバーは、ユーザーとクラスの派生先からは、利用できない。したがって、ユーザー向けに提供するヘッダーに書いておく必要はない。しかし、クラス定義をユーザー向けとライブラリ作者向けに分けることは、ODRの都合上できない。

クラス定義には、ユーザーと派生クラス向けではなく、コンパイラー向けの要素も存在する。たとえば、コンパイラーはクラスのレイアウトを決定するために、すべての非staticデータメンバーやvirtual関数(vtableによる実装方法を取るコンパイラーにとっては、レイアウトの決定に関わる)の宣言を必要とする。この場合はsecretだ。

しかし、privateな非virtualであるstatic/非staticなメンバー関数はどうか。set_secretは、ユーザーからも派生クラスからも使えない。コンパイラーはクラスのレイアウトを決定するためにset_secretの存在を知る必要はない。とすれば、private non-virtual member functionは、クラス定義の外で定義してもいいのではないか。そうすれば、ユーザー側から存在を隠すことができる。

privateなメンバーをクラス定義に書かなければならない問題はいくつもある。

ライブラリ作者が、クラス定義のprivateなメンバーを追加、削除、シグネチャの変更をするたびに、そのクラスを使う翻訳単位はすべて、再コンパイルしなければならない。しかし、private非virtualメンバー関数は、完全にクラス作者の中だけで完結しているので、そのような再コンパイルは、本来必要がないのだ。

実装上の都合になるが、クラス定義に書くということは、シンボル名がその分増えるということである。シンボル名が増えるということは、shared libraryなどのシンボル名を解決して動的にロードするような機能に、余計に時間がかかるようになる。

内部リンケージを持つ名前をprivateメンバー関数で使うことができない。たとえそのprivateメンバー関数が、クラス作者の翻訳単位でしか使われないとしてもだ。

この問題は、private非virtualメンバー関数を、クラス定義の外で、追加的に宣言、定義できればよいのだ。すなわち、プライベート拡張メソッドの提案となる。そのために、privateキーワードを流用して、新たな文法を作る。

// Foo.h
struct Foo
{
    int get_secret() ;
private :
    int secret ;
} ;
// Foo.cpp
#include <Foo.h>

private void set_secret( int value )
{
    secret = value ;
}

また、private非virtualコンストラクターも、同様に外部定義できる。

// privateコンストラクター
private Foo::Foo( int value ) : secret( value ) { }

プライベート拡張メソッド(PEM)は、staticメンバー関数にも使えるし、内部リンケージ指定もできる。ただし・・・現在提案中の文法では、色々とややこしい。

問題は、staticというキーワードは、staticメンバーの宣言にも、内部リンケージの宣言にも使われているということだ。そのため、staticキーワードは、位置に酔って意味が変わる。

  • privateキーワードの後にstaticキーワードを書くと、staticメンバーになる。
  • privateキーワードの前にstaticキーワードを書くと、内部リンケージを持つ。
  • privateキーワードの前後にstaticキーワードを書くと、staticメンバーで内部リンケージを持つ。
// staticキーワードの利用例一覧
struct Foo{ } ;

private void Foo::f() ; // private non-static member function
private static void Foo::f() ; // private static member function
static private void Foo::f() ; // private member function with internal linkage
static private static void Foo::f() ; // private static member function with internal linkage

これはあまりにもわかりにくい。

まだC++11がC++0xと呼ばれていたドラフト段階では、一時期、内部リンケージを指定するためのstaticキーワードの使用は、deprecated扱いであった。後に覆ったが、こうしてみると、やはりstaticキーワードを内部リンケージを指定するために使うのは、間違っている気がする。

C++11では、無名namespaceが追加されたので、これを使うことにより、内部リンケージにできる。

// 無名namespaceを使って内部リンケージにする
namespace
{
    private void Foo::f() ; // private non-static member function with internal linkage
    private static void Foo::f() ; // private static member function with internal linkage
}

論文では、プライベート拡張メソッドを、デフォルトで内部リンケージにしてしまうという案も提示している。外部リンケージが欲しければ、明示的にexternキーワードを書くようにする。結局、多くの利用例はデフォルトが内部リンケージで問題ないだろうから。

// デフォルトで内部リンケージ案
private void Foo::f() ; // private non-static member function with internal linkage
private static void Foo::f() ; // private non-static member function with internal linkage
extern private void Foo::f() ; // private non-static member function with external linkage
extern private static void Foo::f() ; // private static member function with external linkage

あるいは、privateキーワードの利用自体を省いてしまうという案もあるが、これは賛成できない。

ところで、誰でもクラス定義の外でprivateメンバー関数を宣言できてしまうというと、アクセス指定の回避ができるかどうかが問題になるところだ。これは、通常は問題がない。なぜならば、誰でもprivateメンバー関数を宣言できるとはいえ、呼び出すことはできないからだ。

// クラスGodivaを定義しているヘッダー
#include <Godiva.h>

private auto Godiva::peeping_tom()
{
    return naughty_bits ;
}

int main()
{
    Godiva godiva ;
    auto dirty_view = godiva.peeping_tom() ; // ill-formed、盲目となる
}

しかし、それでも規格の抜け穴を縫うような形で、プライベート拡張メソッドを使って、合法かつアクセス指定を破れるコードが存在する。


class A {
  int n;
public:
  A() : n(42) {}
};

template<typename T> struct X {
  static decltype(T()()) t;
};
template<typename T> decltype(T()()) X<T>::t = T()();

int A::*p;
private int A::expose_private_member() { // note, not called anywhere
  struct DoIt {
    int operator()() {
      p = &A::n;
      return 0;
    }
  };
  return X<DoIt>::t; // odr-use of X<DoIt>::t triggers instantiation
}

int main() {
  A a;
  return a.*p; // read private member
}

しかし、このようなコードは、うっかりと引っかかる種類のコードではない。プログラマーが意図的に行わない限り、遭遇することのないコードだ。Herb Sutterがかつて言ったように[GotW076] 、

これは実際、問題ではない。この問題は、マーフィーの法則から保護する VS マキャベリから保護するかでしかない。つまり、うっかりミスから守るか(これは言語がとてもうまくやっている)VS 意図的な悪用から守る、かだ。結局、もしプログラマーが体制を破壊したいのであれば、例1から3で示したように、必ず方法をみつけるものだ。

訳注:
マーフィーの法則=よくあることを意味するレトリック
マキャベリ=目的のために手段を選ばないことを意味するレトリック

GotW #76: Uses and Abuses of Access Rights

その他、色々と反論に対する反論が書かれている。例えば、モジュール機能によりこの問題は解決するという反論には、モジュールはいつ入るかわからない。いま使える機能が欲しい。そのためには最小限度の変更で実装できる機能でなければならないと反論している。

また、クラス定義を再び開ける案も書かれている。これは、クラスのレイアウトを変化させるようなメンバーは宣言できないが、private non-virtualメンバー関数以外にも、nested typeなどを追加できる。

// クラス定義を再び開く案
class Foo { } ;

// 再び開く
private Foo
{
    void f() ; // private member 
    int x ; // static data member
} ;

これは興味深い提案だ。

N3864: A constexpr bitwise operations library for C++

C++にビット列操作ライブラリを追加する提案。

<cmath>と<memory>に、constexprでnoexceptなビット列操作のための関数テンプレートを大量に追加する。

ビット列操作は、古くからプログラマーの実用的テクニックでもあり、遊びでもあった。ビット列操作には、実にさまざまな技法が発見されている。

よく使われるビット列操作は、ハードウェアでサポートされていることもある。しかし、CやC++では、PDP-11ですら提供しているビット列操作のインストラクションを、直接使うべき文法が存在しなかった。ビット列操作というのは、プラットフォームごとに最適な実装方法が異なる。多くのプラットフォームに対応したライブラリを実装するには、#ifdefの塊になってしまう。

信じられないことに、この2014年になっても、CとC++には、ビット列操作の文法が極めて貧弱で、標準ライブラリも存在しない。それゆえ、プログラマーは各自に独自ライブラリを作成したり、適当にネット上で検索してコピペしたりしている。

これは極めて残念なことだ。このような汎用的な処理は、共通のライブラリを作るべきなのだ。そして、標準ライブラリということになれば、たとえばコンパイラーは最適化で、プラットフォームによっては単一のインストラクションを吐いたりできる。また、複数のインストラクションを組み合わせなければならない場合でも、文脈に合わせて最適化をかけられるため、単なるインラインアセンブリ以上のことができる。

さて、ビット列操作ライブラリを付け加えるにあたって、問題がある。ビット列操作はとても多いが、いったいどの操作を標準ライブラリに含めるべきか。また、命名はどうするべきか。

どのようなビット列操作を付け加えるべきかという問題に対しては、もちろん、ハードウェアでネイティブにサポートされているビット列操作も考慮するが、それ以外にも、たとえ現実のハードウェアでネイティブにサポートするものがなくても、よく使われる有名で基礎的なビット列操作も含める方針だ。

命名方法はバイク小屋議論となるので難しい。たとえばビット列の末尾に連続するゼロを数え上げる(Count Trailing Zeros)ビット列操作を考えよう。

まず、英語の頭文字をとった、std::ctz()が考えられる。これはアセンブラーで使われているニーモニックによく似たスタイルの命名法で、昔まだ識別子の長さがそうとう短く制限されていた時代の名残である。これは曖昧でわかりにくいが、タイプ数を節約できる。

あるいは、std::count_trailing_zeroes()だろうか。これは、たしかにわかりやすいし、曖昧性もないが、長ったらしいし、複雑な式の中で使うのは難しい。

提案された論文での命名規則は、ニーモニックよりの改良案となっている。

すべての関数名は動詞から始まる。動詞は、少なくとも3文字を使って略字とする。そのため、std::ctz(Count Trailing Zeroes)ではなく、cnttz(CouNT Trailing Zeros)となる。

さらに、0と1に対しては、0と1を用いる。ZやOや、その他の文字は用いない。これにより、cntt0(CouNT Trailing 0)となる。

名詞は、一貫性を持って再利用できる場合、一語一文字で略される。たとえば、以下のような名詞を再利用する。

  • t0: trailing 0s
  • t1: trailing 1s
  • l0: trailing 0s
  • l1: trailing 1s
  • ls1b: least significant 1 bit
  • ms1b: most significant 0 bit
  • ls0b: least significant 1 bit
  • ms0b: most significant 0 bit

ここ何年も、純粋に規格漬けになっていた筆者としては、std::count_trailing_zeros()の方が、曖昧性なくわかりやすくていいのではないかと思う。識別子の長さは、clang-completeのような名前補完ツールを使えばいいのだ。

ちなみに、ドワンゴ社内では、std::ctz()やstd::cntt0()で何の問題もないとする人間ばかりだった。思うに、これは慣れの問題だと思う。ドワンゴ社内のプログラマーというのは、ビット列操作を自前で書くし、アセンブリ言語のニーモニックを日常扱う人間ばかりなので、このような略字に慣れているのだろう。あるいは、現場のプログラマーは、補完ツールが発達した今でも、タイプ数の大小、ソースコード内の一行に収まる情報量という物理的な要素を強く意識するものなのかも知れない。

筆者は、アセンブリ言語は10年ほどやっていないし、それにやっていた時も、マクロを多用して暗号的にわかりづらいニーモニックをまともな名前にしていたし、まだ少しは読めるDSLを実装していた。そういえば昔、自作したISAバス接続の基板をつなげて、MS-DOSから入出力ポートを直接叩いて自作の基板と通信する課題で、言語は自由だったので、他の皆がアセンブリで苦労して書いている中、筆者は16bit DOS用のバイナリが吐けるDigital Mars C++コンパイラーを使って、悠々とC++で高級な入出力ポート操作用のライブラリを使って、入出力ポートを叩いていたものだ。今思えば、筆者は変わり者だったのかも知れない。

それにしてもこの論文は、ドワンゴ社内でも特に食いつきがすごかった。筆者の周辺の人間は皆、論文やGitHubに上がっているリファレンス実装を積極的に読み、これは良いの、あれは悪いの、それは疑問と盛り上がっていた。やはり、ビット列操作に多少なりとも興味を示さないプログラマーは、本物のプログラマーとはいえないだろうし、それに、ドワンゴ社内の仕事でもビット列操作は必要であり、多用されているからだ。

このような現場の知見を得られるというのは、もう何年も規格ばかりやっていた筆者にとっては、なかなか悪くない環境だ。C++標準化委員会としても、感想すらやってこないものに対応できるわけがない。何が良い、何が悪い、何がわからないということは、言わなければ伝わらない。C++標準化委員会にエスパー能力を求めてもらっては困る。本来、日本のC++を扱う企業は、もっとC++標準化委員会に知見を上げるべきなのだ。C++を使っているのに、不満点を言わなければ、いつまでたっても不満は解消されない。社内に実績あるライブラリがあれば、標準ライブラリとして提案しなければ、いつまでたってもC++の標準ライブラリは貧弱なままなのだ。日本の企業はもっと積極的にC++規格に関わる必要があると思った次第。

[PDFに改良案はない] N3865: More Improvements to std::future<T>

futureを更に改良する提案。

現在のfutureは、値と例外を同時に扱っている。


#include <thread>
#include <future>
int main()
{
    auto f = std::async( []() -> int
    {
        throw 0 ;
        return 0 ;
    } ) ;

    // 例外が投げられる
    int result = f.get() ;
} 

future/promise/asyncは、新しくC++11に追加された標準ライブラリexception_ptrを使って例外を束縛し、スレッドを超えて例外を伝えることができる。問題は、もしasyncに渡した関数オブジェクトが例外を外に投げた場合、futureのget()は、例外を投げる。

futureのgetは、値と例外を一緒くたにあつかっている。これは様々な点で不便だ。たとえば、例外を投げてほしくない場合、直ちにtry { } catch( ... ) { }などで囲まなければならないし、その場合でも、やはり例外を投げているわけで、無駄なコストが発生する。

また、futureが持っている例外をexception_ptrの形で欲しい場合でも、やはり一度投げさせてtry catchで受け止めなければならない。なんという無駄だろうか。

そもそも、future自体は、値を持っているのか例外を持っているのか、知っているわけだ。futureが今、readyで、しかも値を持っているのかどうか調べることのできるメンバー関数を追加してはどうか。

そして、値を取得するメンバー関数、get_valueと、exception_ptrを取得するメンバー関数、get_exceptionも追加する。


std::future<int> f = ...

if( f.has_value() )
{
    // 値を持っている
    int result = f.get_value
}
else
{
    // 例外を持っている。
    std::exception_ptr ptr = f.get_exception_ptr() ;
}

ところで、このようにhas_valueでしらべてからget_valueを使うのは面倒だ。もしfutureが値を持っておらず例外を持っている場合に、実引数に指定した値を返すちょっとした関数が欲しい。それがvalue_orだ。

// value_orの例
x = f.value_or(v) ;

このコードは、以下のように書くのと等しい。

// 同等のコード
x = (f.has_value()) ? f.get_value() : v ;

nextは、thenと似ているが、futureに値がセットされている時のみ関数オブジェクトが呼ばれる。

// nextの例
f.next( []( auto f )
{
    auto value = f.get() ; // 必ず値がセットされている
}) ;

recoverは、thenと似ているが、futureに例外がセットされている時のみ関数オブジェクトが呼ばれる。recoverの引数は、std::exception_ptrとなる

// recoverの例
f.recover([]( std::exception_ptr ptr )
{
// ptrは例外を束縛している
}) ;

futureに例外がセットされている時、フォールバックとしての値を使いたい時がよくある。fallback_toは、そのような利用例のためのメンバー関数だ。

// fallback_toの例
x = f.fallback_to(v) ;

これを真面目に書くと、以下のようになる。

// 真面目に書いた例
x = f.then( [&]( auto f )
{
    return f.value_or(v) ;
}) ;

make_exceptional_futureは、例外がセット済みのfutureを作れる。make_ready_futureの例外版といったところだ。

// make_exceptional_futureの例
auto f = std::make_exceptional_future<int>( 123 ) ;

たしかに、値と例外が一緒くたになっているのは不便だ。これはなかなかいい提案だ。

N3866: Invocation type traits (Rev. 2)

C++に新たなtype traits、invocation_typeを追加する提案。

invocation_typeはメタ関数で、メタ関数引数は関数型、関数型の戻り値の型にクラスを、仮引数リストに、実引数の型リストを与えると、実際に呼び出す関数型(あるいはメンバー関数型)を返す。

// invocation_typeの例
struct C
{
    int operator() ( double, int ) ;
    int operator() ( double, double ) ;
} ;

// int ( double, int )
using type1 = std::invocation_type< C ( int, int ) >::type ;
// int ( double, double )
using type1 = std::invocation_type< C ( double, double ) >::type ;

invocation_type< Fn( ArgTypes ... ) >は、Fnを実引数ArgTypesで関数呼び出ししたときに、どのような型の関数が選ばれるかを、::typeで返すメタ関数である。result_ofは呼び出した結果の型を返すが、invocation_typeは、呼び出される関数の型を返す。これにより、呼び出される最適関数のformal parameterを得ることができる。

なるほど、たしかに面白いが、いったいどのように利用するのか。これはMore Perfect Forwardingを実装するのに使う。

たとえば、以下のようなコードは、より完全な転送ができないので、コンパイルエラーになる。

// More Perfect Forwardingが必要な例
void f( int & i ) ;

int i ;

int main()
{
    f( i ) ; // well-formed
    std::thread t( f, i ) ; // ill-formed
}

見ての通り、より完全な転送が失敗している。このコードをうまく通るようにするには、threadの実装で、最適関数に選ばられる関数の実際の仮引数の型を知る必要がある。そのため、invocation_typeが必要になる。

実は、invocation_typeは、より直接的な、raw_invocation_typeのラッパーである。invocation_typeは、もし第一引数がメンバーへのポインターであり、第二引数がrvalueリファレンスである場合、decayを適用してリファレンスを外す。これは、すぐに寿命が尽きる一次オブジェクトを束縛してしまうのを防ぐためである。raw_invocation_typeは、そのような処理を行わない、生の関数型を返す。

さらに、提案では、function_call_operatorを提供している。これは、::valueで、オーバーロード解決で選ばれたメンバーへのポインターを返すメタ関数である。

// function_call_operatorの例
struct C
{
    void operator ()() ;
    void operator ()( int ) ;
} ;

int main()
{
    auto fco = std::function_call_operator< C ( int ) >::value ;
    C c ;
    c.*fco(0) ; // call C::operator()(int)
}

これはぜひとも入って欲しい。

N3867: Specialization and namespaces (Rev. 2)

別の名前空間からテンプレートの明示的特殊化と部分的特殊化ができる提案。

ユーザーがテンプレートを明示的に特殊化することを前提とした設計のライブラリがある。たとえば、std::hashだ。このテンプレートは、ユーザー側がユーザー定義型に対するハッシュの計算方法を指定するために、ユーザーに明示的に特殊化させることを想定している。

問題は、その特殊化には、同じ名前空間であることを必要とするので、面倒なのだ。クラスを定義したら、名前空間を閉じて、目的の名前空間を開き、また閉じて、さて、また元の名前空間を開いて、続きを記述しなければならない。


namespace A {
  namespace B {
    /* ... */
    class C { 
      /* ... */ 
    };
    // hashの明示的特殊化を宣言したい  
  } // Bを閉じる
} // Aを閉じる 

// stdを開く
namespace std {
  template<>
  struct hash<A::B::C> {
    size_t operator()(A::B::C const &c) { /* ... */ }
  };
} // stdを閉じる

namespace A { /* さっきまで使っていた名前空間を開き直す */
  namespace B { 
      /* ... */
  }
}

こんなコードは書きたくない。以下のように書きたいものだ。


namespace A {
  namespace B {
    /* ... */
    class C { 
      /* ... */ 
    };
      
    template<>
    struct ::std::hash<C> {
      std::size_t operator()(C const &c) { /* ... */ }
    };
    /* ... */
  }
}

このように書けるようにする提案。

N3869: Extending shared_ptr to Support Arrays, Revision 1

shared_ptrで、配列を直接サポートする提案。これまでは、カスタムデリーターを渡してやれば、配列を扱うこともできた。しかし、もっと直接的に、

// こんなこといいな、できたらいいな
std::shared_ptr< int [] > ptr{ new int [10] } ;

こう書きたいはずだ。すなわち、shared_ptr<T[]>が、直接サポートされていて欲しい。これをサポートする提案。

論文では、shared_ptr<T[N]>のサポートも提案している。unique_ptrでこれが取り除かれたのは誤りであるとし、むしろunique_ptrにもT[N]を追加すべきだと主張している。

また、論文では、reinterpret_pointer_castという関数テンプレートを提案している。これは、shared_ptr<U>からshared_ptr<T>に変換するものだ。論文はこれをあまり強く主張しておらず、もし削らなければならないのであれば、必要な犠牲として受け入れるとしている。

実装は簡単で、既存のshared_ptrのコードを劇的に変更する必要はない。

Boost 1.53のshared_ptrは、この提案通りに配列をサポートしていて、テストやドキュメントも揃っている。

N3870: Extending make_shared to Support Arrays, Revision 1

N3869と関係していて、make_shardを配列に対応させる提案。

前回の論文、N3641との違いは、寄せられた意見を元に、オーバーロードを極端に削った。今の提案がサポートしているのは、2パターンの利用例だ。

// shared_ptrで配列を確保する例

// 値初期化されたN個のT型の配列、new U[N]()と等しい
// std::shared_ptr< int [ ] >
auto p1 = std::make_shared<int>( 10 ) ;
// std::shared_ptr< int [10] >
auto p2 = std::make_shared< int [10] >( ) ;

// 指定した値で初期化するもの、std::vector<U>コンストラクターと等しい
// std::shared_ptr<int [] >
auto p3 = std::make_shared<int>( 10, 0 ) ;
// std::make_shared< int [10] >
auto p4 = std::make_shared< int [10] >( 0 ) ;

以前の提案では、二次元配列の確保などの、やたらと複雑なオーバーロードが存在した。

N3871: Proposal to Add Decimal Floating Point Support to C++ (revision 2)

十進浮動小数点数をサポートする提案。decimal32, decimal64, decimal128という名前のクラスが提案されている。

C++の標準規格では、浮動小数点数がどのように表現されているかを定めていない。したがって、規格上は十進浮動小数点数で実装することも可能であるが、既存のC++実装はすべて、速度を重視して、浮動小数点数の内部表現は二進数にしている。

十進浮動小数点数の利点は、10進数を正確に表現できることである。たとえば、十進浮動小数点数を使えば、二進浮動小数点数では正確に表現できなかった、10進数の0.1が、正確に表現できるようになる。

論文では、C++03向けに書かれた十進浮動小数点数のTechnical Reportを参照して、C++14用に修正している。この提案が規格入りすれば、C++17では、十進浮動小数点数のサポートは、規格上、必須になる。

C++03向けのTRをC++17向けに書きなおすにあたってなされた変更も興味深い。

まず、10新浮動小数点数型を、Standard layout typeかつPODにするために、明示的にdefault化された宣言を使う。

また、long longへの変換関数は、暗黙的でないほうがいいだろうということで、explicitになった。

// explicitな変換関数になった
decimal32 d = 123.0 ; // OK, doubleからdecimal32は暗黙に変換できる
long long i1 = d ; // ill-formed、decimal32からlong longは暗黙に変換できない
long long i2 = static_cast<long long>(d) ; // OK

また、十進浮動小数点数はクラスではあるが、実装により組込型扱いすることを認めるために、finalとなる。つまり、派生することができない。

ちなみに、ドワンゴの業務で十進浮動小数点数の需要があるかどうかを聞いてみたところ、「あんまりなさそうな」という答えが返ってきた。

ドワンゴ広告

この記事もドワンゴの勤務時間中に書いた。ドワンゴはC++の啓蒙活動を進めている。

ドワンゴは仕事のできるC++プログラマーを募集しています。

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

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

2014-02-27

AtomとかいうGitHub発の不自由なテキストエディターについて

GitHubが存在を公開した(流出したと言うべきかもしれないが)、Atomというテキストエディターの話題で持ちきりである。

このテキストエディターは、邪悪な不自由ソフトウェアであるので、自由の価値を重んずる読者は使ってはならないことはもちろんである。自由、不自由をさておいても(もちろん、さておくことはできない相談だが)、もちろん、敬虔なVim教徒である読者は即座に了解しているだろう。AtomがEmacsよりはいくらかは--自由の価値を重んじない浅はかな利用者にとっては--使いやすくなる可能性はないでもないが、Vimの牙城を崩すことは不可能である。Vimを倒すものはVimであり、NeoVimかもしれないが、Atomであることはありえない。その点で、我々信徒は安心できる。

さて、このAtomというテキストエディターは、自由ソフトウェアではない。その理由は、以下で言及されている。

Why is Atom closed source? - Atom

Atomはクローズドソースとはならないが、オープンソースともならない。Atomはその中間的な位置に立つ。このことによって、我々はAtomに対して金を超集することを容易にするが、ソースコードは何らかの制限的なライセンスで提供されるために、ユーザーは実装方法を調べることができる。我々はどのようにしてこれを実現するかまだ完全に決定していない。公式に発表する際に、詳細を開示できるだろう。

Hacker Newsに興味深いコメントがあった。

Why is Atom closed source? | Hacker News

georgemcbay

GitHubが自分のとこのソフトウェアをどうしようと連中の勝手だが、思考実験としては興味深い。

ここに、少なくとも二つの会社が、大規模なChromiumベースのエディターを開発している。一社はAdobeで、もう一社はGitHubだ。

一社は、エディター技術のコア部分を、完全にオープンソースモデル(もちろん、GitHubでホストされている)で開発していて、すでによく知られていて広く使われているライセンスを採用している。もう一社はやっていない(少なくとも、現時点ではその計画はない)

2年前、オープンソースな方のエディターをやってるのがAdobeだと、誰が正解できただろうか。

まあ、どうせAtomは失敗するだろう。

ところで、IntelのCPUブランドに同名のものがあって、ググラビリティに影響しそうだが、そのへんはどうなのだろう。

ドワンゴ広告

この記事はC++とは関係がないがドワンゴでの勤務中に書いた。

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

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

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

2014-02-26

アマゾンのお気に入りリストで送られてきたもの

またまたアマゾンでものが送られてきた。

ゲノム 2 (カラフルコミックス)は、古賀亮一の描いたマンガである。このマンガは、昆虫について楽しく学べる教育マンガであるので、是非読んでもらいたい。

スーパーマリオ64は、ゲーム用制限コンピューターであるニンテンドー64上で動作する不自由ソフトウェアだ。残念ながら、いま妖怪ハウスには、ニンテンドー64は置いていない。いずれ買ってこようかとも思うのだが、私はニンテンドー64には、それほど思い入れがないので、今すぐ買ってこようとも思わない。まあ、いずれ、N64を手に入れた時に、感想を書くとしよう。

ただ、ニンテンドー64といえば、風来のシレン2 鬼来襲シレン城は、名作だ。

古賀亮一のゲノムの他の巻や、他のマンガ、また、風来のシレン2なども入っている江添のほしい物リストは以下にある。何か送れば、このブログのネタになる。

Amazon.co.jp: 江添亮: 江添のほしい物リスト

2014-02-25

渋ハウスと立ちション禁止とタバコ禁止

22日に渋ハウスでパーティがあったので、行ってきた。

渋ハウスというのは、一軒家に30人以上住んでいるシェアハウスだ。いや「住んでいる」というのは間違いかも知れない。渋ハウスに正式に金を出している人間が30人以上いるだけで、毎日寝泊まりしている人は30人もいないはずだ。それから、「シェアハウス」というのも違う気がする。人が集まれる場と言ったほうが正確だろうか。

とても大勢の多種多様な人間が集まってパーティをしていた。

残念ながら、私と話題が合う人は少なかった。

渋ハウスは、以外に綺麗なところだった。こんなに大人数を読んでパーティをするところなのに、なぜ綺麗なのだろうか。その謎は、トイレに入った時に解決した。

トイレには張り紙があり、曰く、「立ちション禁止」

なんと、立ちション禁止とな。立ちション禁止とはうーむ。立ちション禁止・・・

立ちション禁止。それは男としての尊厳の否定にほかならない。およそ、男を男たらしめ、女と境するものは、立ちションの可否である。この近代的な男女平等の世の中でも、男が先天的に女より優れているのは、両性における泌尿器の先端器官の違いである。むろん、女でも、姿勢と小便器の形状を工夫すれば、あるいは立ちションも可能かも知れないが、男に比べて極めて非効率的であると言わざるを得ない。立ちションは効率的である。男用の便所が混むことはまれであるのも、立ちションのおかげである。立ちション、これこそ男と女を分かつもの。

しかし、渋ハウスで立ちションが禁止とあっては仕方がないので、ズボンをおろして便座に座って用を足した。

しかし、立ちションの禁止だけでは、トイレ以外の場所は汚れるはずだ。一体、渋ハウスはどうやってこの綺麗さを維持しているのだろうか。なんと、それはタバコが禁止であるがためだ。そうだ。渋ハウスではタバコが禁止なのだ。実に素晴らしいことだ。この点は妖怪ハウスも見習って欲しいところだが、残念ながら妖怪ハウスはまだその域に達していない。まあ、吉田寮よりはマシであると言えるか。

しかし、タバコ禁止と立ちション禁止を天秤にかけると・・・いや、タバコは絶対悪である。もし、タバコと立ちションを天秤にかけると、片方は地面にめり込み、もう片方は脱出速度に達するであろう(はたして地球の重力を使って地球の重力を振りきれるのかという疑問はさておき、たとえ非科学的にせよ、それぐらいの差だということだ)

渋ハウスはとてもいいところであった。住む場所ではないが、遊びに行く場所としては素晴らしい。それに場所も、名にし負う渋谷の駅から近く便利だ。夜中に都内中心部にいて、終電で野方に間に合わない場合に、ああいう場所にも住んでいると便利だ。渋ハウスには金を出してみたい。

さて、シェアハウスかどうかは疑問だが、またひとつシェアハウスめぐりをした。次はどこへ行こうか。しかし、開放的なシェアハウスはあといくつ残っているのか。

ギークハウス東上野

すこし間があいてしまったが、この間、ギークハウス東上野に行って、カレーを食べてきた。

ギークハウス東上野で、カタンをやれそうな雰囲気であったが、残念ながらカタンがギークハウス東上野には存在しない。なんでも、近くにあるコワーキングスペース的なところにあるらしい。しかし、自宅にカタンを持っていない程度であれば、ギークハウス東上野勢は所詮、それほどカタンが強くないのかも知れない。ただし、筆者もポータブルカタンを持っていなかったので、この機会に買っておいた。

定期的にカレーなどを作っているそうなので、また来たいところだ。

さて、筆者のシェアハウスめぐりはまだまだ続く。次はどこへ行こうか。

2014-02-24

Bram MoolenaarがNeovimに反応

Neovim - Google Groups

Vim作者であるBram MoolenaarがNeovimに対してコメントを返している。

大変な仕事になるし、すべてのシステムをサポートすることもできないし、新しいバグが生まれるし、利用者にとって何の価値があるというのだ?

完全なリファクタリングは解決ではない。今あるものを改良するほうがいいのだ。利用者にとってVimが快適になるように、小さなリファクタリングをするのならまだわかるが。

Neovimのニュースを見た時に、まっさきに思い浮かんだし、Hacker Newsでも言及されていたことに、Joel Spolskyのブログ記事がある。

Things You Should Never Do, Part I - Joel on Software

Joelは、MozillaがNetscapeがスクラッチから書き直したことを批判し、たとえどんなにコードが汚かろうと、今動いているものを少しつづリファクタリングして使うべきだと書いていた。それとかぶる。

筆者は、Neovimの動きは好ましいように思う。Vimを吸っクラッチから再実装するのではなく、今あるコードベースを、劇的ではあるがリファクタリングするのだから。

2014-02-23

興味深かった記事

Ubuntu desktop moving application menus back into application windows | Ars Technica

UbuntuのUnityは、長らくウインドウのメニューをウインドウ自体ではなく、画面上部のバーに表示するUIを採用してきた。これには賛否両論あったが、賛同する者達でさえ、問題点は皆無とは言えない状況であった。

グローバルなバーが表示するのは、現在フォーカスのあたっているウインドウのメニューであるので、ダイアログを開くソフトウェアなどが使いづらい。

Ubuntu 14.04では、どうやらメニューをウインドウごとの表示に戻すようだ。

The world’s fastest VP9 decoder: ffvp9 | Ronald S. Bultje

ffmpegによる、最初から自由なVP9デコーダーの実装ffvp9を開発し、Googleの実装であるlibvpxと比較した結果、ffvp9の方がパフォーマンスで上だったという主張の記事。

H.264を使うよりは、Googleが邪悪であるそぶりをみせないうちは、VP9を使ったほうがまだマシだという気がする。ただし、真にクソのような物理法則と関わらないにも関わらず認められている特許から解放されるためには、いまと全く違った動画の圧縮技術が必要とされる。

neovim/neovim

Neovimというレポジトリだ。

確かに、Vimは世界一素晴らしいエディターであり、Vim本体とプラグインには多数の開発者がいる。Vimより優れたテキストエディターが存在しない現状で、Vimの牙城は少しも揺るがない。

しかし、Vimのコードは汚い。20年以上もの歴史を持ち、いまだにC89を使っていて、30万行もあり、その全容を理解している者は少ないし、理解しようという勇者も少ない。

さらに、事実上唯一のメンテナーであるBram Moolenaarは、パッチの受付にとても保守的である。というのも、一度パッチを受け入れたならば、そのコードは、彼が一生保守しなければならないのだから。

Neovimは、現在のVimのソースコードを劇的にリファクタリングすることを目的としたプロジェクトで、以下の目標を設定している。

  • 保守を簡単にし、バグ修正や機能をマージする速度を改善する
  • 複数の開発者で作業を分担できるようにする
  • コアのソース部分に手を加えずに、新しい近代的なユーザーインターフェースを実装できるようにする
  • コプロセスベースの新たなプラグインアーキテクチャにより、拡張性を高める。エディターから特別にサポートしなくても、プラグインを好きな言語で書けるようにする

レガシーコードや、対象コンパイラーがC89という制限を完全に撤去する。また、いままでVimが独自にやっていたプラットフォーム別の実装も、既存のライブラリを利用する。

プラグインは、それぞれ別のプロセスで動作させ、stdin/stdoutを経由して、msgpack-rpcかjson-rpcでプロセス間のメッセージやりとりで動作するという。これにより、Vimのバイナリで言語を一つづつサポートする必要はなくなる。

このマルチプロセス設計は、GUIアーキテクチャにも適用される。Vimのコアプロセスと、VimのGUIプロセスは分離され、これもやはりstdin/stdoutを経由して、プロセス間のメッセージやりとりで動作する。

これはあくまでVimの大幅なリファクタリングであって、Vimのゼロからの再実装ではないし、VimをIDEへと変身させるようなものでもないという。

freenodeで#neovimが作られている。

2014-02-22

肉のハナマサのいい話

筆者は妖怪ハウスというシェアハウスで料理をしている。今住んでいるシェアハウスでは、住人と来客も合わせて、毎日3-5人ぐらいは食べる人がいる。料理というものは、大量に作ると効率的だ。また、食材も大量に買うと効率的だ。

そういうわけで、最近の食材の買い物は、もっぱら肉のハナマサで行っている。一気に大量に買えるので、効率的なのだ。しかも、うどんとか焼きそばとかソバの生麺の質は、近所の個人スーパーより、肉のハナマサのほうが一玉あたり安いうえ、質も上だ。もやしも2kgの袋で買うと、コストパフォーマンスが非常によい。最初は、もやしを2kgも使いきれるか心配だったが、もやしは色んな所に使いでがある。今住んでいるシェアハウスの住人は、とにかくたくさんたべるので、2kgのもやしであっても、数日もしないうちに使いきれてしまう。

というわけで、効率的な肉の華聖人はいえ、やはり新鮮な食材を常備するには、数日おきに行かなければならない。幸い、職場の近くにも肉のハナマサがあるので、帰りがけに買うこともできる。今住んでいるシェアハウスに近い肉のハナマサは、往復8kmほどかかるのだ。バスもあるそうだが、多少は運動がしたいので、歩いて買い物に出かけている。

さて、今日も肉のハナマサで大量に買い込んだが、うっかりと卵を割ってしまった。完全に筆者の責任で、10個入りの卵パックを3パック、カゴに入れて食材を物色していたところ、うっかりとかごを取り落としてしまったのだ。みると、パックあたり、2,3個の卵が割れてしまっている。やれやれ、失敗してしまった。これは、今日の夜は親子丼にでもするしかあるまいと思いながら、レジに行ったところ、なんと割れてない卵に取り替えてくれるという。完全に筆者の落ち度であると言ったのだが、それでも取り替えてもらった。

というわけで、筆者は割れていない卵パックを持って帰宅した。

肉のハナマサは偉大だ。

2014-02-19

アマゾンのお気に入りリストからドメモ

Amazon.co.jp: 江添亮: 江添のほしい物リストに入れておいたドメモが届いた。

ドメモというのは、とても簡単なゲームだ。誰でもすぐにルールを覚えることができ、とても白熱できる。1から7までの札があり、札は数字の数だけある、つまり、1の札は1枚あり、7の札は7枚ある。各プレイヤーが手札を取り、場にも公開された札と、伏せた札が置かれる。自分の手札は見えずに、相手の手札は全部見える。そして、見えざる自分の手札をあてていく。

このとき、札は書かれている数字の枚数だけあるということに注意する。例えば、7の札は7枚あるが、場に6枚しか見えなかったら、自分の所にあるか、伏せた札に入っているかだ。もし、7の札が3枚しか見えなければ、それは高確率で自分の手札にたくさん入っているということだ。

この、とてもわかりやすいルールは、全く知らない人に教えるのがとても簡単で、その場で突発的に始めるゲームとして、とても便利だ。しかも楽しい。

このようなわかりやすいゲームは、他にもいくつかある。例えば、 ダンジョンオブマンダム や、 Love Letterや、 藪の中 がある。

妖怪ハウスでは、今後もゲームを色々取り揃えておく予定だ。

2014-02-17

続アマゾンのお気に入りリスト

電気ケトルで沸かしたお湯でお茶を飲んでいると、今日もアマゾンから何かが送られてきた。何かと思えば、 YAMAHA(ヤマハ)ABS樹脂製リコーダー ソプラノ ジャーマン式 YRS-37IIIだった。

運指がジャーマン式なので、まだうまく吹けないが、一時間ほど練習して、The Free Software Songぐらいは吹けるようになった。

さて、今日の夜、風呂に入った後に、例の越中ふんどしを装備してみることにした。なかなか快適だ。ただし、色が赤であるのが気に食わない。江添亮: 江添のほしい物リストに白ふんどしを追加してみた。思いの外に快適なので、いずれ自分でも買うかも知れない。

2014-01-pre-Issaquah mailingの簡易レビュー Part 1

今回は、論文の本数が多いのと、ドワンゴに雇われているので、本気でじっくりと論文を読んで解説しているし、ライブラリの論文も読み飛ばさずに読んでいるので、いつもより時間がかかる。そのため、いくつかのパートに分けて公開することにした。

今回はドラフトの更新はなし。

今回の新機能の提案の論文には、SG10のためのマクロ名の提案が目立つ。SG10というのは、Cプリプロセッサーによる機能テストのマクロ名を標準化しようという提案のStudy Groupだ。醜悪で将来廃止されるべきCプリプロセッサーに依存する機能をこれ以上増やさないで欲しいのだが。

N3824: make_array

std::array<T>を返すmake_arrayの提案。以下のように使う。

// make_arrayの利用例
std::array<int, 3> a = std::make_array( 1, 2, 3 ) ;
std::array<long, 2> b = std::make_array( 1, 2L ) ;

どの型になるかは、std::common_typeを使って決定される。ただし、narrowing conversionは禁止されている。

// narrowing conversionは違法になる
auto a = std::make_array( 1, 2U ) ; // エラー

明示的に型を指定することで、キャストされる。

// 明示的に型を指定する例
auto a = std::make_arrary<long>( 1, 2U ) ; // OK

もちろん、現実に書くときには、auto指定子を使うべきだ。

// make_arrayの利用例
auto a = std::make_array( 1, 2, 3 ) ;
auto b = std::make_array( 1, 2L ) ;

[PDFを廃止する議論が必要とされている] N3825: SG13 Graphics Discussion

C++14に標準ライブラリとして入れるために設計を進めている軽量グラフィックライブラリの土台として、何を使うかの議論のまとめ。

C++にグラフィックライブラリを入れたいが、グラフィックライブラリを一から設計するのは、C++標準化委員会の規模を大幅に超えているので、既存のグラフィックライブラリを土台として使い、必要であればC++風にバインドして使おうというのが、これまでの議論。では、その土台を何にするかというのが、この議論の主題だ。

土台には二つの意見があった。既存の規格を使うものと、既存のライブラリを使うものだ。

既存の規格というのは、例えばSVG+Canvasのような、標準規格を参照して、その設計を元にC++風にバインドして使うということだ。

既存のライブラリというのは、例えばcairoのような、すでに実装されているライブラリを元にして、必要であればC++風にバインドして使うということだ。

どうやら、議論の方向は既存のライブラリ、それもcairoを土台とするところに向かったらしい。cairoはCで書かれているが、オブジェクト指向であり、正しくconstを使っており、またドキュメントも非常に充実している。

cairoには、cairommというC++へのバインドがあるが、どうもこのcairommは、2010年から更新されていないので、おそらくプロジェクトとしては死んだのではないかと思われる。

そこで、cairo本体を土台とし、数ページ程度に収まるような機会的な変換ルールを定義して、cairoのインターフェースをC++風に合わせてはどうか。論文では、そのための変換ルール案も箇条書している。また、cairoのドキュメントなどがISO規格として使えるような著作権許諾が得られるかなどの調整も必要だ。

本当にC++14の標準ライブラリにグラフィックライブラリが入るのだろうか。

N3827: Working Draft Technical Specification - URI

URIライブラリのドラフト

[汚いPDF] N3829: apply() call a function with arguments from a tuple (V2)

tupleの要素をすべて関数の実引数に渡すライブラリー、applyの提案。

これは欲しい。自前で実装するのは簡単だが、面倒だ。

N3830: Scoped Resource - Generic RAII Wrapper for the Standard Library

汎用的なRAIIラッパーライブラリー、scoped_resourceの提案。

RAIIというのは、Resource Acquisition Is Initializationの略である。C++では、クラスオブジェクトにリソースを所有させ、デストラクターが動くタイミングで解放処理をさせるような一連の技法を言う。

// RAIIの例
class raii_holder
{
    int * owned ;
public :
    explicit raii_holder( int * ptr )
        : owned( ptr ) { }

    raii_holder( raii_holder const & ) = delete ;    
    raii_holder & operator =( raii_holder const & ) = delete ;

    int * get() { return owned ; } 

    ~raii_holder()
    {
        delete owned ;
    }
} ;


int main()
{
    raii_holder r( new int(0) ) ;
    *r.get() = 123 ;

// 自動的にdeleteされる
} 

ポインターの場合は、すでにunique_ptrがあるが、ポインター以外のリソースを管理するライブラリは、手で書くしかない。unique_ptrは、ポインター以外のリソース(単なる整数値で表現される環境依存のハンドルなど)を管理するのは面倒だ。

問題は、このようなRAIIラッパーを手でいちいち書くのは面倒だ。これは、テンプレートで汎用化すべき種類のコードだ。C++14までのコア言語機能を総動員し、最新の設計をすれば、そのような汎用RAIIラッパーライブラリが実現できる。

// 提案中のライブラリ
int main()
{
    auto r = std::make_scoped_resource( &::operator delete, new int(0) ) ;

// 自動でdeleteされる。
}

まだ設計は変わるが、このライブラリは非常に柔軟である。例えば、解放のための処理が、単に引数をひとつとるものではない場合がある。たとえば、以下のような実装依存のリソースの確保、解放関数があったとする。

// System defined APIs:
void* VirtualAlloc(
void *pAddress,
size_t size,
unsigned int fAllocationType,
unsigned int fProtect);

bool VirtualFree(
void *pAddress,
size_t size,
unsigned int uiFreeType);

これはこのようになっているので、いまさらどうしようもない。提案中のscoped_resouceでは、これを単に、複数のリソースをまとめて管理するという形で扱う。わざわざユーザーがVirtualFreeと複数のリソースをラップする必要はない。

// VirtualFreeにもらくらく対応
int main()
{
    auto r = std::make_scoped_resource( &VirtualFree,
        VirtualAlloc( nullptr, PAGE_SIZE, MEM_COMMIT, PAGE_HARDWARE ),
        0,
        MEM_RELEASE ) ;

// 自動的にVirtualFree
}

Variadic Templatesを使えば、任意個のリソースを管理できる。

実は、管理するリソースは、0個でも良い。

// 0個のリソースを管理する例
int main()
{
    auto r = std::make_scoped_resource(
        []{ std::cout << "I'm exception safe. Thank you very much." << '\n' ; }
    ) ;

// 自動的にlambda式が呼ばれる
}

これにより、オブジェクトrが破棄されるタイミングで処理を実行することもできる。

このようなライブラリを手で書くのは簡単だが、正しく書くのは面倒なので、標準ライブラリに入って欲しい。

この提案では、あくまでunique_ptrをポインター以外にも広げたscoped_resourceを提案している。参照カウンターを使ったものは提案されていない。そのようなライブラリも実装可能だが、この論文では、それは後から検討する話だとしている。追加は簡単なので、とりあえずはこれを議論したほうがいい。

[気分を害するPDF] N3831: Language Extensions for Vector level parallelism

Clik PlusやOpenMPで提供されている、プログラムにSIMD化のためのヒントを与える構文の提案。

SIMD化できるループは、極めて厳しい制約が課される。論文ではこれをcountable loopと呼んでいる。

SIMD loopをコア言語でサポートするために、for文に変更がくわえられる。

// SIMD loop
for simd ( int i = 0 ; i < 10 ; ++i ) 
{
// 処理
}

キーワードsimdは、文脈依存キーワードなので、この文脈でなければ予約されていない。そのため、自由に識別子として使うことができる。

for文の条件式につかえる式は、非常に限られている。以下の組み込みの演算子しか使えない。

// SIMD forの条件式で使える式
relational-expression < shift-expression
relational-expression > shift-expression
relational-expression <= shift-expression
relational-expression >= shift-expression
equality-expression != relational-expression

また、for文の3つ目のオペランドにも、前置後置のインクリメントとデクリメント、+=, -=, E1 = E2 + E3, E1 = E2 - E3ぐらいしか使えない。これは組み込みのコンマ演算子で区切ることができる。その中で使える識別子で参照されているものにも、強い制約がある。

return, break, goto, switchなどを使って、ループの中から外に脱出したり、また外からループの中に飛び込んだりすることはできない。

また、SIMD関数というものもある。これは、文脈依存キーワードsimdでマークされる関数のことである。

// SIMD関数の例
int f() simd { }
[]() simd { } ; // もちろんlambda式にも使える

さて、このようにsimdキーワードでマークされたループや関数には、複数のコードが生成され、実行時にふさわしいものが選ばれる。つまり、これはコンパイラーへのヒントである。

[理論的に美しくないPDF] N3832: Task Region

C++でstrict fork-joinを実現するためのライブラリ。

こうしてみると、並列処理というのは、理論が数十年ぐらい先行していて、いま、ようやく理論をC++のような実用的な言語で使うために、現実の場に落としこんできているというのがわかる。このライブラリはかなり理論よりの内容になっているが、はたしてここまでガチガチの机上の理論に裏打ちされたライブラリが、普通のプログラマーに簡単に使えるようなライブラリに落ち着くだろうかという懸念はある。

原理としては、task_regionに関数オブジェクトを渡して、その中で並列実行したいタスクはtask_runに関数オブジェクトを渡して実行するということになっている。そして、最後にすべての並列処理をjoinして集める。

ただしこれはかなり理論的なライブラリなのでかなり制約がなくす方向で進んでいる。たとえば、関数を呼び出した時のスレッドと関数が戻ってきた時のスレッドが異なることは、従来起こりえないことだったが、このライブラリでは、起こりえてしまう。これをどうしようかということが議論されている。また、まだ完全に処理が終わらない状態で関数が戻るということもあるので、その場合をどうしようかという議論もある。

[PDF不使用原則も提案したい] N3839: Proposing the Rule of Five, v2

従来の三原則に変えて、五原則を導入する提案。

三原則とは、コピーコンストラクター、コピー代入演算子、デストラクターの、どれかひとつをユーザー定義した場合、残りの二つも、おそらくはユーザー定義が必要になるであろうという原則だ。

C++11では、ムーブという概念が、直接コア言語に組み込まれた。当初、ムーブを直接コア言語でサポートするかどうかは、決まっていなかった。しかし、ムーブのような基本的な概念は、コア言語で認識したほうがいいだろうという結論になり、従来のコピーコンストラクター、コピー代入演算子にくわえて、ムーブコンストラクター、ムーブ代入演算子が、新たにコア言語で特別なメンバー関数として認識されるようになった。

もし、C++11で、ムーブコンストラクター、ムーブ代入演算子がユーザー定義されていた場合、コピーコンストラクター、コピー代入演算子は、暗黙にdefault化される。この挙動はdeprecatedであり、将来的には廃止される見込みに、C++11ではなっていた。

さて、C++14では、十分に猶予を与えたし、そろそろ廃止してもよかろうという論調になってきた。すなわち、五原則の成立だ。コピーコンストラクター、コピー代入演算子、ムーブコンストラクター、ムーブ代入演算子、デストラクターの5つの特別なメンバー関数のうち、どれかひとつをユーザー定義した場合、残りもユーザー定義しなければならない。なぜならば暗黙にdefault化されないからだ。

// 五原則の例
struct X
{
    ~X() { }
} 

int main()
{
    X x1 ;
    X x2 = x1 ; // エラー、五原則
}

ただし、五原則を導入すると、既存のコードで、暗黙の宣言に頼っているコードが壊れてしまう。C++規格では、互換性を壊す変更は、相当に慎重になるので、すでにdeprecated扱いになったとはいえ、たやすく消すことはできない。文字列リテラルから暗黙にconst性を失わせる仕様の除去にも、15年かかった。

// 文字列リテラルからconst性をなくす例
int main()
{
    // well-formed in C++03
    // ill-formed in C++11
    char * ptr = "hello" ; 
}

この五原則は、ぜひともC++14に入って欲しい。deprecated仕様は、もう何年もの猶予を与えたのだから、十分すぎる。

[PDFは世界一おバカなフォーマット] N3840: A Proposal for the World's Dumbest Smart Pointer, v3

世界一おバカなスマートポインター、observer_ptrの提案。

生のポインターというのは、意図がわかりにくい。たとえ、所有して管理という概念がないとしても、ポインタークラスを標準ライブラリに入れたほうがいいのではないか。そうすれば、そのオブジェクトは管理されていないポインターであるという意図が明確になる。

// observer_ptrの例
#include <memory>

int main()
{
    // std::observer_ptr<int> ptr = new int(0) ; と同じ
    auto ptr = std::make_observer( new int(0) ) ;

    auto p2 = ptr ; // コピーできる

    delete ptr ;
}

observer_ptrは、ポインターを所有しないし、管理することもない。そのため、自由にコピーもできる。ポインターが動的に確保されたストレージをさす場合、その解放は利用者の責任である。

生のポインターを直接使うより、意図がわかりやすくなる。ただし、別に生のポインターでもわかりにくくならないのではないかという標準会員の重鎮もいるし、議論は分かれている。

[PDFの使用も非推奨] N3841: Discouraging rand() in C++14

std::random_shuffleとstd::randを非推奨にする文面を追加する提案。

std::randは、とてつもなく使いにくい設計であり、広く誤用されている。例えば、以下のような間違ったコードが使われている。

// 間違ったサイコロの実装
int roll( )
{
    return std::rand()%6 + 1 ;
}

このコードは間違っている。なぜならば、剰余は均一分布しないからだ。ある乱数から、特定の範囲の値を数学的に信頼できるほどの精度で均一分布させたい場合は、もっと工夫が必要になる。このような間違ったコードは、世の中のソフトウェアに乱用されていて、世の中のソフトウェアに正しくない乱数をもたらしている。このような誤用をしやすい時代遅れの設計のstd::randは、使用を非推奨にすべきである。

また、イテレーターの組の中の要素を、ランダムにシャフルするアルゴリズム、std::random_shuffleも、std::randの利用を前提にしたオーバーロードがあり、また、独自の乱数を渡す部分も、非常に汚い設計になっているので、これも同じく非推奨にする。

規格では、注記として、「randの使用は、故に、移植性がなく、予期できず疑わしい品質とパフォーマンスである」と書かれるようになった。

読者は、std::randやstd::random_shuffleのような時代遅れの設計のライブラリを使ってはならない。std::randの代わりには、C++11に新しく入った素晴らしい設計の乱数ライブラリを使うべきであるし、std::random_shuffleの代わりには、C++11で新しく追加されたstd::shuffleを使うべきである。

[標本採取されないべきPDF] N3842: A sample Proposal

sample(標本採取)というアルゴリズムの追加。このアルゴリズムは、値の集合の中から、ランダムで一部を取り出すものである。このアルゴリズムは、Knuth先生のThe Art of Computer Programming, Volume 2で、アルゴリズムS(“selection sampling technique”) とアルゴリズムR(“reservoir sampling”)と呼ばれている。

ちなみに、vol. 2でアルゴリズムBと呼ばれているのは、C++11に入った<random>にある、std::knuth_bである。

アルゴリズムS(Selection sampling)は、標本を採取する値の集合は、Forward iteratorを必要とし、採取した標本を書き出すイテレーターには、Output Iteratorを必要とする。

アルゴリズムR(Reservoir sampling)は、標本を採取する値の集合は、Input Iteratorを必要とし、採取した標本を書き出すイテレーターには、Random Access Iteratorを必要とする。

提案では、アルゴリズムS版とR版で名前を分けるのではなく、実引数に渡したイテレーターのカテゴリーにより、タグディスパッチをして自動的に適用可能なアルゴリズムを選ぶ設計になっている。


template< class PopIter, class SampleIter, class Size, class URNG >
SampleIter
sample( PopIter first, PopIter last, SampleIter out, Size n, URNG&& g )
{
    using pop_t = typename iterator_traits<PopIter>::iterator_category;
    using samp_t = typename iterator_traits<SampleIter>::iterator_category;
    return __sample( first, last, pop_t{},
                     out, samp_t{},
                     n, forward<URNG>(g) );
}

Knuth本のアルゴリズムRは、安定しているが、追加の処理が必要になる。SGI版のSTLにあるsampleアルゴリズムは、不安定だが追加の処理をしない。論文ではこの点を注記しているが、C++のアルゴリズムR版のsampleが、どのように実装されるかについては言及していない。

[江添フレンドリーではないPDF] N3843: A SFINAE-Friendly std::common_type
[江添フレンドリーではないPDF] N3844: A SFINAE-Friendly std::iterator_traits

std::common_typeと、std::iterator_traitsをSFINAEフレンドリーにする提案。

たとえば、以下のようなコードは、C++11では違法(コンパイルエラー)になる。

// C++11では違法
using type = std::common_type<int, int *>::type ;

なぜならば、int型とint *型には、共通の変換できる型がないからだ。

しかし、このようなコードが違法になると、SFINAEの文脈で、問題になる。

// SFINAEとはならない例

// 共通の型がある場合のみ候補に上がるべき関数テンプレート
template < typename T1, typename T2,
    typename COMMON_TYPE = std::common_type<T1, T2>::type >
void f( T1, T2 ) { }

// その他の関数fのオーバーロード

int main()
{
    int a1 = 0 ;
    int * a2 = nullptr ;

    f( a1, a2 ) ; // コンパイルエラー

} 

このコードでは、SFINAEを使って、共通の型がある場合のみ、オーバーロード解決の候補に上がるテンプレート関数fを宣言しようとしているが、残念ながら、common_typeは共通の型がない場合、内部でill-formedになってしまうので、このコードは通らない。

N3843は、共通の型がない場合にも、内部で勝手にill-formedにならず、しかもSFINAEの文脈で使えるよう、ネストされた型名を宣言しないように、std::common_typeを変更しようという提案だ。

これと全く同じ理由で、std::result_typeは、以下の提案論文の採択により、すでにSFINAEフレンドリーに変更されている。

N3462: std::result_of and SFINAE

result_typeと同様に、common_typeとiterator_traitsもSFINAEフレンドリーになるべきである。この提案は採択されてほしい。

ちなみに、この論文には、ブインの提督、銀天すばる (SubaruG)さんがこの問題を取り上げたブログ記事にも言及している。

A Blog Post
Although written in Japanese, the author shows an invoke function defined with result_of that fails to compile because it (result_of) is not SFINAE-friendly. It also calls out std::common_type as another trait that would benefit from being SFINAE-friendly.

FDIS の不満点 - 野良C++erの雑記帳

現状、 std::result_of や std::common_type といった「型を取得するメタ関数」は、与えられた型が前提条件を満たさない場合、クラステンプレートの内部でコンパイルエラーとなるため、 SFINAE に使うことが出来ません

[PDFは割り切れない思い] N3845: Greatest Common Divisor and Least Common Multiple

最大公約数を計算するgcdと、最小公倍数を計算するlcmを<cstdlib>に追加する提案。

最大公約数や最小公倍数のアルゴリズムは、義務教育でも習うほど初歩的なものであるが、たとえ実装が一行にせよ、コードを書いて、正しく動くかどうかテストするのは難しい。しかも、このような基本的な関数の用途はかなり広い。標準ライブラリに存在するべきだ。

提案されているgcdとlcmは、constexpr関数かつ関数テンプレートで、二つの整数型の引数を取る。

また、gcdとlcmを実装するために、constexpr関数でテンプレート関数版のabsも追加される。

その実装は、コード量だけで言えば、とても短い。この記事に引用できるほどだ。

// N3845のリファレンス実装例
// 仮にclib名前空間内に置く
namespace clib {

// 任意の整数型で動くテンプレート版のabs
template< class T >
constexpr auto abs( T i ) -> enable_if_t< is_integral<T>{}(), T >
{
    return i < T(0) ? -i : i; }
}

// 二つの実引数が整数型であるかどうかを確認し、また共通の型を返すメタ関数common_int_t
template< class M, class N = M >
using common_int_t = enable_if_t< is_integral<M>{}() and is_integral<N>{}()
, common_type_t<M,N>
> ;

// 最大公約数
template< class M, class N >
constexpr auto gcd( M m, N n ) -> common_int_t<M,N>
{
using CT = common_int_t<M,N>;
return n == 0 ? clib::abs<CT>(m) : gcd<CT,CT>(n, m % n);
}

// 最小公倍数
template< class M, class N >
constexpr auto lcm( M m, N n ) -> common_int_t<M,N>
{ return abs((m / gcd(m,n)) * n); }

いくら短いとはいえ、このようなコードをコピペして使いまわすのは不適切だ。このような基本的でよく使われる関数は、標準ライブラリに存在するべきなのだ。

[PDFは拡張せずに滅びるべき] N3846: Extending static_assert

C++11で追加されたstatic_assertは、必ず文字列リテラルを指定しなければならない。

// C++11のstatic_assertの例
static_assert( true, "This can't be possible!") ;

しかし、なぜ指定しなければならないのだろうか。単にコンパイル時assertを書きたいだけで、特に文字列を指定したくない時でも、たとえ空にせよ、文字列リテラルを指定しなければならないのだ。

さらに、現在のBOOST_STATIC_ASSERTの実装は、コンパイラーにstatic_assertが実装されている場合は、以下のようになる。

// BOOST_STATIC_ASSERTの実装
#define BOOST_STATIC_ASSERT(B) static_assert(B, #B)

これはつまり、以下のように書くと、

BOOST_STATIC_ASSERT(( sizeof(int) == 4 )) ;

以下のように展開される。

static_assert(( sizeof(int) == 4 ), "( sizeof(int) == 4 )") ;

多くのプログラマーにとって、static_assertに指定した式を文字列として読め、しかもそれがデフォルトとなっているのは、便利だ。

このような文字列リテラルを指定しないstatic_assert、デフォルトで式を文字列化するstatic_assertは、何年もの間、多数の人から、要望されてきた。

実際、BOOST_STATIC_ASSERTと同等のマクロは、実に大勢のものによって、再発明されてきた。これは、明らかに利用者がこのような機能を必要としている証拠である。

とはいえ、式を文字列化するというのは、ただでさえコンパイラーの実装が難しいC++では、とても難しいのだ。Phase of Translationをかける前の文字列でなければならない。あるいは、トークン列に分割したものを再び文字列化するか。

それ故、多くのコンパイラー屋は、式を文字列化するという機能をコア言語でサポートすることに難色を示している。「文字列リテラルを取らない文法を追加して、出力される文字列は実装依存とするならば反対はしないが、式を文字列化するというのは、C++コンパイラーの実装の都合上、とても難しい」という意見だ。これはプリプロセッサーの仕事だと主張するコンパイラー屋が多い。

一方、Cプリプロセッサーは醜悪であり、将来的には廃止されるべきであり、そのような機能に依存してはならないと江添は考える。

ところで、本当に式の文字列化は有益だろうか。実際にstatic_assertに引っかかったならば、ソースコードから問題のstatic_assertとその前後を確認するはずで、static_assertの式が文字列化されて、コンパイルエラーメッセージに含まれたところで、それほど意味はないのではないか。つまり、

#define STATIC_ASSERT(B) static_assert(B, #B)

このような実装と、

#define STATIC_ASSERT(B) static_assert(B, "ouch")

この実装で、現実的になにか違いはあるだろうか?

いやまて、そもそも、文字列以外の任意個の値を表示したい需要だってあるだろう。独自拡張を含めれば、様々な値がコンパイル時に取得できるのに、コンパイル時に値を表示できないのは残念すぎる。

static_assertがprintf風のフォーマットをサポートしてたら嬉しいって誰かが言ってた。

と、論文では収集がつかない意見の噴出を紹介している。

その上で、論文は、いくつかの文面案を提案している。

案1 (Daniel Krüglerの提案)

もし、文字列リテラルが与えられない場合は、結果のdiagnosticメッセージは、定数式のテキストである。

BOOST_STATIC_ASSERT相当の機能をコア言語で直接サポートする提案

案2 (Mike Millerの提案)

もし、文字列リテラルが与えられない場合は、結果のdiagnosticメッセージは、実装依存である。

すでにトークン列化されてるものを、再び文字列に戻したくねーよというコンパイラー屋の提案。

案4

コンマで区切って任意個の値を与え、それを人間が読める形で表示する提案

文面案の全訳は面倒なので、コードで示す。

// 案4の利用例
static_assert( sizeof(int) == 4,
    "Oops, the size of int is not 4! Use this:", sizeof(int),
    " Also check the size of long, just in case :D :", sizeof(long)
) ; 

これは・・・はて、どうだろう。

何にせよ、文字列を指定しないstatic_assertは欲しいところだ。

[PDFは簡単ではない!] N3847: Random Number Generation is Not Simple!

すでに提案中の、初心者にも使いやすい乱数ライブラリ、std::pick_a_numberに対する提案。

まず、pick_a_numberを関数テンプレートにし、浮動小数点数型も扱えるようにする。

// N3847提案のpick_a_number
#include <random>

int main()
{
    int a = std::pick_a_number( 1, 6 ) ;
    double b  = std::pick_a_nubmer( 1.0, 6.0 ) ;
}

また、関数オブジェクトとして呼ぶと乱数を返すクラスも提供する。これにより、<algorithm>に渡しやすくなる。

さらに、乱数を返すイテレーターも提供する。これにより、copy_nなどのアルゴリズムに渡せる。

クラスがテンプレートでないのは気になるところだ。

N3848: Working Draft, Technical Specification on C++ Extensions for Library Fundamentals

std::optionalのドラフト文面。

N3849: string_view: a non-owning reference to a string, revision 6

文字列をラップするクラス、string_viewの提案。

多くの場合、文字列を扱うとき、その文字列がどのように実装されているかは、どうでもいいことだ。std::stringかもしれないし、null終端されたcharの配列かもしれない。重要なのは、それが文字列として振る舞うことであって、実装方法の詳細ではない。そこで、様々な実装方法の文字列をラップして、共通の操作を提供するクラスが欲しい。

このような需要は極めて一般的なもので、GoogleとかBloombergとか、ChromiumとかLLVMなどといった企業やプロジェクトで、ほぼ同等機能のライブラリが、独立して再発明され続けている。それならば、標準ライブラリとして存在すべきだ。

string_viewは、具体的な文字列の実装を参照して、std::basic_string風のインターフェースで見るためのクラスであり、変更することはできない。変更できるようにすると、文字列リテラルでstring_viewを初期化することができなくなる。それに、文字列を変更するということは、往々にして、文字列の長さも変えなければならない。それには具体的な実装による処理が絡んでくる。そのため、文字列を変更するという需要はない。事実、LLVMでも、文字列を変更する版のライブラリを追加する需要は発生していない。

[PDFはやめてくれ頼む] N3850: Working Draft, Technical Specification for C++ Extensions for Parallelism

<algorithm>に並列実行版を付け加える提案のドラフト文面

この提案では、従来のアルゴリズムに、実行ポリシー(execution policy)を取るオーバーロードが追加される。

標準規格では、三つの実行ポリシーを定義している。すなわち、std::seq, std::par, std::vecであり、それぞれ、シーケンシャル実行ポリシー(sequential execution policy)、並列実行ポリシー(parallel execution policy)、ベクトル実行ポリシー(vector execution policy)である。既存のアルゴリズムは、すべてシーケンシャル実行ポリシーである。

並列実行ポリシーは、処理の実行単位が順序を問わず、未規定のスレッドで非シーケンシャルに実行される。そのため、スレッドローカルストレージなどは、実行の単位ごとに異なる可能性があるし、ロックなども慎重に使わなければ、実行単位の間でデッドロックを引き起こす。

ベクトル実行ポリシーは、処理が未規定のスレッドで実行されるし、またひとつのスレッドで実行された場合、非シーケンシャルである。ベクトル実行ポリシーはさらに制約が強く、実行単位の中では、一切のロックを行ってはならない。

// N3850の例
#include 

int main()
{
    std::vector v ;
    // vに値を入れる

    // 従来のsort、暗黙にシーケンシャル実行ポリシー
    std::sort( v.begin(), v.end() ) ;

    // 明示的にシーケンシャル実行ポリシー
    std::sort( std::seq, v.begin(), v.end() ) ;

    // 並列実行ポリシー
    std::sort( std::par, v.begin(), v.end() ) ;

    // ベクトル実行ポリシー
    std::sort( std::vec, v.begin(), v.end() ) ;
}

並列実行ポリシーや、ベクトル実行ポリシーが、どのように実装されるかは規定されていないし、それは規格が定めるところではない。

[多次元的に複雑なPDF] N3851: Multidimensional bounds, index and array_view

連続したメモリ配列に対して、多次元配列操作を提供する、array_viewライブラリの提案。Microsoftのライブラリ、C++ AMPを土台としている。

array_viewは、連続したメモリを参照して、あたかも多次元配列を操作しているかのようなインターフェースを提供する。

// N3851時点での提案の例
std::size_t M = 32 ;
std::size_t N = 64 ;

std::vector<float> v( M * N ) ;

std::array_view<float, 2> view{ { M, N }, v } ;


view[{ 3, 3 }] = 0 ;

また、array_viewは、bounds_iteratorにより、各要素に対して、一次元的なアクセスを提供している。

// N3851時点でのイテレーター
std::bounds_iterator<2> first = std::begin( view.bounds() ) ;
std::bounds_iterator<2> last = std::end( view.bounds() ) ;

float sum = 0.0 ;

std::for_each( first, last,
[]( std::index<2> index )
{
    sum += view[index] ;
}

string_viewとは違い、array_viewはmutableである。つまり、参照先を変更することができる。これは、文字列と多次元配列という違う概念による違いだ。LLVMでも、2011年2月に追加したimmutableなArrayRefに対して、mutable版のMutableArrayRefを追加したのが2013年1月。StringRefに対しては、いまだにMutableStringRefを追加する需要がない。

N3852: C++ FCD Comment Status

前回、各国から送られたNational Body CommentへのC++標準化委員会への返答。日本は一件も送っていない。

特に興味深いNBコメントの結果だけ紹介する。

CH2: スイスは、C++14はマイナーアップデートであり、ドラフトの質に影響するような大規模な変更をやめろと意見した。これは受け入れられた。その結果、optionalとdynarrayは、標準規格ではなく、TS(Technical Specification)という形で制定することになった。いずれは、標準規格にも取り入れられるだろう。

US15: アメリカ合衆国は、N3655により、すべてのtypetrait<...>::typeは、エイリアステンプレートを使って、typetrait_t<...>と書けるようになった。さっそく、規格の文面でも置換を行おうと意見した。これは採択された。

数値区切りは、一旦却下されたものの、各国から、入れろ、考えなおせというNBコメントが相次いだために、採択された。

// 数値区切りの例
int x = 1000'000'000 ;

ES8: グローバルなoperator delete[]に、第二引数にstd::size_t型で、ストレージのサイズを取るオーバーロードが追加された。これは、従来メンバー関数にはあったが、なぜかグローバルな解放関数にはなかったものだ。ストレージのサイズが渡されることで、一部のメモリアロケーターの実装の解放時の処理が、とても効率的になる。

GB4: 文面の変更により、ストレージ確保と例外がからむと、従来は余計にストレージが確保されて、もともとメモリーリークしていたコードが、さらにリークする可能性があるというイギリスの意見に対し、core issue 1786が作られた。

ES10, US8: [[deprecated]]は採択されたが、CDに入れ忘れているぞというスペインの意見に対し、N3394をCDに適用する対応がなされた。

US9: 現行ドラフトでは、実行時サイズ配列の添字が0と評価された場合、例外を投げるとしている。これは合法なC99のコードをC++14では実行時に失敗させてしまう。既存のC99コードには、添字0のコードが山ほどある。実際に、G++で実行時サイズ配列を実装して、既存のコードに試してみると、多くが壊れてしまった。添字が負数である場合はいいが、0の場合は認めるべきであるというアメリカ合衆国が意見した。これは採択された。core issue 1768

CH5: プリプロセッサーマクロ、__func__が関数の中ではないlambda式(名前空間スコープの中の初期化子の中のlambda式)の中で使えるかどうか、文面上疑問であるというスイスの意見。これは文面を修正することに決まった。

US13: 非volatileローカル変数をreturnするときは、常にムーブでいいだろうという米国の意見。これは採用された。

ES11: forward_as_tupleがconstexprではないというスペインの意見。constexprとなることに決まった。

GB9: C11では、忌まわしき危険なgetsが取り除かれた。まだC++はC99規格を参照しているとはいえ、getsに関しては、C++でも廃止すべきであるというイギリスの意見が採用された。

FI4: これは却下されたが面白かったので紹介。戻り値の型推定は、変換関数にも適用できる。

// 変換関数に関数の戻り値の型推定を適用する例
struct Zero
{
    operator auto() { return 0 ; }
} ;

int main()
{
    Zero zero ;
    int x = zero ;
}

N3853: Range-Based For-Loops: The Next Generation

range-based for loopは、従来のforに比べて、格段に使いやすくなった。

// 昔のforループ

void once_upon_a_time( std::vector<int> & range )
{
    for (   std::vector<int>::iterator first = range.begin(),
            std::vector<int>::iterator last = range.end() ;
            first != last ; ++first )
    {
        std::cout << *first << '\n' ;
    }
}

// 今のforループ

void and_now( std::vector<int> & range )
{
    for ( int elem : range )
    {
        std::cout << elem << '\n' ;
    }
}

なるほど、これはわかりやすくなった。しかし、要素の型を指定するのが面倒だ。そもそも、要素の型はコンパイル時に決定できるわけだ。重要なのは型ではない。すると、以下のように書ける。

// auto specifierの利用
for ( auto elem : range ) ;

なるほど、これは実にわかりやすい。ただし、これには問題がある。

// Range-based for loopとauto specifierの問題点

void f( std::vector< std::string > & range )
{
    for ( auto elem : range )
    {
        std::cout << elem << '\n' ;
    }
}

これは動くが、いちいちにstd::stringオブジェクトのコピーが発生してしまう。そして、従来のC++プログラマーは、このコードによってコピーが発生しているということを、見逃しやすい。これは問題だ。

では型名を書くかというと、それも問題なのだ。例えば以下のようなコードが問題になる。

// コピーが発生してしまうコード
void f( std::map< const int, int > & range )
{
    for ( std::pair< int, int > const & elem : range )
    {
    // ...
    }
}

これにはコピーが発生してしまう。なぜならば、rangeの要素は、std::pair< const int, int >だからだ。型の変換のためにコピーが発生してしまうのだ。

型名を明示的に指定するのは問題が多い。しかし、autoでもコピーが発生してしまう。ではどうすればいいのか。

"for ( auto & elem : range )"は、まだいくらかマシだ。しかし、これもvector<bool>のようなプロクシーイテレーターに対応できない問題がある。

// プロクシーに対応できない例
void f( std::vector<int> & v )
{
    for ( auto & elem : v )
    {
        std::cout << elem << '\n' ;
    }
}

int main()
{
    std::vector<int> non_proxy{ 1, 2, 3  } ;
    f( non_proxy ) ; // well-formed

    std::vector<bool> proxy{ true, true, true } ;
    f( proxy ) ; // ill-formed!
} 

vector<bool>は、規格上、だいぶ変わった実装をしなければならない。この実装はプロクシーと呼ばれてる技法が使われている。イテレーター一つ一つに対応する要素へのオブジェクトはない。そもそも、boolはたったの1ビットの情報で表現できるのだ。であれば、charの配列のようなストレージを用意しておき、その1bitづつに、要素一つを対応させればいいのだ。そして、イテレーターで変換を行う。

問題は、そういう場合に、bool一つ一つに対応するオブジェクトがないので、イテレーターはかなり不自然な挙動をする。もちろん、規格の要件通りなのだが、やはり不自然なことは不自然だ。

"for( auto const & elem : range )"は、ほとんどのプロクシー実装に対応できるが、これでは書き換えることができない。

実は、この問題を解決できる最高の方法があるのだ。"for ( auto && elem : range )"である。

auto &&は、どんなものにでも対応できる、万能の指定子なのだ。

しかし問題は、auto &&を使うには、プログラマーはrvalue referenceやauto specifierやTemplate Argument Deduction(とくにテンプレート名にrvalue referenceが用いられた場合の不思議な挙動)について精通していなければならない。それは初心者にはいかにも無理だ。

簡単に使えるように作られたはずの機能が、実際には簡単に使えないときている。これは簡単に使えるように、言語を拡張すべきだ。すなわち、新しい文法、"for ( elem : range )"の提案だ。

"for ( elem : range )"は、自動的に、"for ( auto && elem : range )"と書いたものと同様にみなされる。これにより、初心者も詳細を理解せずに使うことができる。文法上必要なゴミもなくなり、とてもわかりやすい。

N3854: Variable Templates For Type Traits

C++14では、エイリアステンプレートを用いて、従来のtype traitsの使いやすいラッパーを追加している。

// 新旧比較
std::add_pointer<int>::type old = nullptr ;
std::add_pointer_t<int> latest = nullptr ;

余計な、ネストされた型名、::typeがいらなくなるので、とても書きやすくなる。

type_traits_tは、エイリアステンプレートを用いたラッパーであり、以下のように書くことができる。

template < typename T >
add_pointer_t = std::add_pointer<T>::type ;

C++14には、変数テンプレートも追加されたので、これの値版を追加する提案。すなわち、

// 新旧比較
bool old = std::is_integer<int>::value ;
bool latest = std::is_integer_v<int> ;

::valueが必要なくなる。

この実装は、例えば以下のようになる。


template < typename T >
constexpr bool is_integer_v = is_integer<T>::value ;

これは入って当然の提案だ。

[PDFは忘却されるべき] N3856: Unforgetting standard functions min/max as constexpr

これは異例に短い論文。PDFである理由が全くわからない。

中身は、min/maxをconstexprにする提案だ。ついうっかり入れ忘れていたらしい。

岡山の某陶芸家がおお喜びする様が目に見えるようだ。

[PDFは改良の余地なし] N3857: Improvements to std::future<T> and Related APIs

非常に基礎的な機能しかなかった貧弱なfutureを大幅に改良する提案。便利な機能が追加される。この提案は、次にN3858と対になっている。

まず、futureの基本的な使い方をみてみよう。

// C++11のfuture
#include <future>

int main()
{
    auto f = std::async( [](){ return 123 ; } ) ;

    auto result = f.get() ;
} 

futureは、基本的にはこれだけしかできない。futureは、値をgetで取得する。そして、getは、値がfutureに対応するpromiseに設定されるまでブロックする。それだけだ。

then

futureに値が決定された後に、処理をしたいことはほとんどだろう。そもそも、値が決定されたら、自動的に処理をして欲しいはずだ。また、その処理は、再び非同期で実行されて欲しいかも知れない。すなわちfutureを返したい。C++11では、以下のように書かなければならない。

// C++11の例
#include <future>

int main()
{
    // まあ、これはいいか
    auto f1 = std::async( [](){ return 123 ; } ) ;
    auto r1 = f.get() ;// え、俺がやるの?

    // またかよ・・・
    auto f2 = std::async( [&](){ return r1 + 456 ; } ) ;
    auto r2 = f2.get() ; // で、また俺がやるの?
} 

たったの一回の後処理だけでこれなのだ。続けて何度も非同期な後処理をしたい場合、記述が煩雑になり面倒だ。

このような煩雑なコードは、thenにより簡略化できる。

// then
#include <future>

int main()
{
    auto f2 = std::async( []() { return 123 ; } )
                .then( []( auto f ){ return f.get() + 456 ; }
              )
    auto r2 = f2.get() ;   
} 

thenならば、futureの結果非同期に実行される後処理を、future.then(..).then(...).then(...)と続けて、簡潔に書ける。

unwrap

futureのネスト、すなわち、future<future<int>>>のようなことは、起こりうることである。では、ネストしたfutureを取り出すにはどうすればいいのか。getを使うとブロックしてしまうおそれがある。非同期に中身を取り出したい。しかし、futureから中身を取り出すために非同期処理を書くのは面倒だし、例外も面倒を見なければならない。非同期処理をした結果を得るのに非同期処理を自前で書くという、わけのわからないことをしなければならない。

unwrapは、そのような非同期のfutureの展開をしてくれるものだ。

// unwrapの例
#include <future>

int main()
{
    // outer_futureはstd::future< std::future< int > >
    auto outer_future = std::async( []{
        return std::async( [] {
            return 123 ;
        } ) ; // inner
    } ) ; // outer

    auto inner_future_proxy = outer_future.unwrap() ;

    inner_future_proxy.then([]( auto f ) {
        auto result = f.get() ;
    } ) ;
} 

is_ready

C++11のfutureには、getしかない。getは、まだ値が決定されていない場合、問答無用で待ちに入る。しかし、多くの場合、値が決定されているかどうかをブロックせずに確認だけしたいはずだ。こんな初歩的な機能が、C++11になかったのは、どうしようもないことなのだが、その機能が入る。is_readyだ。

// is_readyの例
#include <future>

void do_something_or_other( std::future<int> f )
{
    if ( f.is_ready() )
    {
        int value = f.get() ; // ブロックしない
    }
    else
    {
        // getはブロックするので、なにか別のことをする
    }
}

時間を指定する、wait_forやwait_untilはあったが、なぜこれはなかったのか。

when_any

複数のfutureのどれか一つでも完了した場合に値が決定するfutureを返す。

これには2バージョンあり、future<vector<future<T>>>を返すタイプと、future<tuple<future<T>>>を返すタイプがある、複数のfutureをイテレーターで渡すとvector版が、実引数で渡すとtuple版が返される

// when_anyの例
#include <future>

int main()
{
    std::future<int> futures[] = { 
        std::async( []{ return 1 ; },
        std::async( []{ return 2 ; },
        std::async( []{ return 3 ; }
    } ;

    // std::future< std::vector< std::future<int> > >
    auto vec_future = std::when_any( std::begin(futures), std::end(futures) ) ;
    // std::future< std::tuple< std::future<int>, std::future<int>, std::future<int> > >
    auto tuple_future = std::when_any( futures[0], futures[1], futures[2] ) ;

    vec_future.then([]{
        // どれかが完了している
    } ) ;
} 

when_all

複数のfutureのすべてが完了した時に完了するfutureを返す。

make_ready_future

futureを作るとき、すでに値が決定されていることが、しばしばある。しかし、値を設定済みのfutureを作るのは、C++11では意外と面倒だ。まず、promiseを作り、そのpromiseに値をセットして、そのpromiseからfutureを得なければならない。

// 値をセット済みのfutureを作る
template <typename T >
std::future< typename std::decay_t<T> > make_ready_future( T && value )
{
    std::promise< typename std::decay<T>::type > p ;
    p.set_value( std::forward<T>( value ) ) ;
    return p.get_future() ;
}

こんな基礎的なことを、わざわざ自前で書きたくない。間違いの元だ。最初からこれが標準ライブラリにあればよい。

[PDFを継続する必要はない] N3858: Resumable Functions

Resumable Functionの提案。

Resumable Functionとは、ブロックする実行を途中で中断して、関数の呼び出し元に、制御を返し、後で自動的に実行が再開されて、結果を得ることができる関数だ。futureにメンバー関数thenを付け加えるのもいいのだが、resumable functionがコア言語でサポートされていると、非同期コードが、とても簡潔に書けるようになる。

future.thenを使うと以下のように書かなければならないコードが、

// future.thenの例
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();
        });
}

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

// resumable functionの例
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 functionのサポートは、コードの記述をとても簡単にする。

resumable functionの文法と機能について簡単に解説すると、以下のようになる。

resumable functionの宣言は、かならずresumable指定子を使わなければならない。resumable指定子とは、resumableというキーワードだ。

// resumableキーワード
std::future<int> f() resumable ;
[]() resumable {} ;

resumable指定子の位置は、関数とメンバー関数の場合、リファレンス修飾子の後、例外指定の前だ。

// 関数のresumable指定子の位置
struct S
{
    std::future<int> f() & resumable noexcept ;
} ;

lambda式の場合、mutableの後、例外指定の前になる

// lambda式のresumable指定子の位置
[]() mutable resumable noexcept -> std::future<int>{ return 0 ; }

もし、resumable指定子のあるlambda式で、戻り値の型が省略された場合、戻り値の型は、Tをreturn文から型推定された型とすると、std::future<T>になる。

// lambda式かつresumable functionの戻り値の型推定
// 戻り値の型はstd::future<int>
[]() resumable { return 0 ; }

resumable functionには、いくつかの制約がある。大きな制約を抜き出すと、

  • resumable functionの戻り値の型は、std::future<T>か、std::shared_future<T>でなければならない。Tがvoid型のときは、値を返さないresumable functionとして認識される。
  • C言語からある可変引数(...)は使えない。Variadic Templatesの関数パラメーターパックは使える。

resumable functionの関数の本体では、await演算子が使える。これも新しいキーワードで、文法上、式になる。

await expr

したがって、条件を満たす式ならば、どこにでも使える。awaitを使った時点で、関数の実行は中断され、その時点で呼び出し元に処理が戻る。そして、await演算子のオペランドが終了した時点で、resumable関数の実行が再開される。

//awaitの例
std::future<void> f() resumable
{
    // 事前の処理
    int r1 =  await std::async( []{ return 0 ; } ) ;
    // 事後の処理
    int r2 = await std::async) [] { return 0 ; } ) ;
    // さらに処理

    return ;
}

await演算子の特徴で、特に興味深いものを抜き出すと、

  • await演算子は、resumable functionの中か、decltype式の中でしか使えない。
  • await演算子のオペランドの式を評価した型は、future<T>かshared_future<T>か、あるいは、このどちらかの型に暗黙に変換可能でなければならない
  • await演算子は、例外ハンドラーの中では使えない。
  • mutexのロックを取得している状態でawait演算子を実行してはならない。
  • await演算子を評価した結果の型は、futureかshared_futureをgetした結果の型になる。もし、型がvoid型の場合、await演算子は他の式の中では使えない。

論文では、resumable functionの実装方法の例についても言及している。

とても簡単な実装は、サイドスタックと呼ばれる方法だ。これは、resumable functionが発動する際に、専用のスタック用のメモリーを確保し、resumable functionに入る前と後で、スタックポインターのすげかえを行う。これにより、ローカル関数を始めとしたスタックに依存するものが、問題なく動くようになる。ただし、スタック用のメモリは、ある程度の大きさの連続したメモリ空間を必要とするため、効率が悪い。

より効率的な実装としては、メモリをヒープ上に動的に確保して、参照カウンターで管理する方法がある。しかし、この方法は、実装が難しい。

論文では、将来の拡張の可能性についても言及している。

まず、汎用化だ。futureやshared_future以外の型も、メンバー関数getを持つとか、thenを持つとか、is_readyを持つなどすれば、認めるという案だ。これにより、汎用的に書ける。

そして、ジェネレーターだ。C#やPythonではすでに実用化されているパラダイムだが、値の集合を一つ一つ遅延して計算したいときなどに、ジェネレーターを使うと、とても自然に書ける。このために、yieldというキーワードを導入し、yieldが実行されるたびに関数から処理を戻すような機能を提供する。

ジェネレーターは、非同期やスレッドとは関係がないが、とてもおもしろくて便利なパラダイムだ。ぜひともジェネレーターは、将来、議論されて欲しい。

[空間の間に消えてほしいPDF] N3859: Transactional Memory Support for C++

トランザクショナルメモリー(Transactional Memory)の提案。

トランザクショナルメモリーとは、並列実行における複数のストレージへの排他的なアクセスを、より簡単に行うための機能だ。

複数のスレッドから、複数のストレージに書き込む場合は、mutexなどの方法で、排他的なロックをかけなければならない。

// mutexの例
int x = 0 ;
int y = 0 ;
// x, yにアクセスする際は、かならずmをロックすること
std::mutex m ;

void f()
{
    {
        std::lock_guard< std::mutex > lock(m) ;

        ++x ;
        ++y ;
    }
}

しかし、このような明示的にロックするコードは、書きにくい。わざわざロック、アンロックしなければならないし、mutexオブジェクトの管理も面倒だ。もっと簡単に書けないものか。

そこで、Transactional Memoryの出番だ。これはコア言語の文法として提供される機能なので、とても簡単に使えるようになっている。

// N3859提案のTransactional Memoryの例
int x = 0 ;
int y = 0 ;

void f()
{
    synchronized
    {
        ++x ;
        ++y ;
    }
}

Transactional Memoryを使えば、自前でmutexオブジェクトを管理せずに、とても簡単に複数のオブジェクトへの排他的なアクセスを書けるようになる。

これは同期ブロック(synchronized block)と呼ばれている。その文法は、キーワードsynchronizedに続けてブロック文を書く。すべてのスレッドのすべての同期ブロックは同期する。つまり、同期ブロックの評価は、あたかもひとつのスレッドの中で逐次実行したかのように振る舞う。つまり、正しく同期ブロックの中から読み書きすれば、複数のスレッドからであっても、競合は一切起こらない。

Transactional Memoryには、もうひとつ。アトミックブロック(Atomic Block)というものがある。これには、三種類ある。

// N3859提案のAtomic Block三種類
atomic noexcept { /* ... */ }
atomic commit_except { /* ... */ }
atomic cancel_except { /* ... */ }

アトミックブロックの文法は、キーワードatomicに続けて、noexcept/commit_except/cancel_exceptのいずれかのキーワードを書き、その後にブロック文を書く。

atomic noexceptは、アトミックブロックの中から外に例外を投げないことをユーザーが保証する。

// atomic noexcept
atomic noexcept
{
    try
    {
        // ...
    }
    catch( ... )
    {
        // 絶対に外に例外を投げないように握りつぶす
    }
}

atomic commit_exceptは、アトミックブロックから例外によって抜けだした際に、トランザクションをコミットして、例外を投げる。つまり、それまでの副作用をブロックの外から観測できるようにする。

// atomic commit_except
int x = 0 ;
int y = 0 ;

void f()
{
    try
    {
        atomic commit_except
        {
            ++x ;
            throw 0 ;
            ++y ;
        }
    } catch( ... ) { }

    // この時点で、他のスレッドを考慮に入れなければ
    // xはインクリメントされている
    // yは初期値のまま
}

atomic cancel_exceptは、アトミックブロックから例外によって抜けだした際に、それがTransaction-safeな例外であれば、トランザクションをキャンセルして、例外を投げる。つまり、それまでの副作用をなかったことにする。

// atomic cancel_except
int x = 0 ;
int y = 0 ;

void f()
{
    try
    {
        atomic cancel_except
        {
            ++x ;
            throw 0 ;
            ++y ;
        }
    } catch( ... ) { }

    // この時点で、他のスレッドを考慮に入れなければ
    // xは初期値のまま
    // yは初期値のまま
}

transaction-safeな例外というのは、N3859提案の段階では、bad_alloc, bad_array_length, bad_array_new)length, bad_cast , bad_typeid , スカラー型となっている。議論の上で、この制限を緩和することも考えているそうだ。

アトミックブロックの中の副作用は、アトミックブロックを抜けた際に、一斉に観測できるようになる。つまり、

// 副作用は一斉に見えるか見えないか
int x = 0 ;
int y = 0 ;

void f()
{
    atomic noexcept
    {
        ++x ;
        // #1
        ++y ;
    }
    // #2
}

#1の時点では、アトミックブロックの中では、xはインクリメントされているようにみえるが、他のスレッドからは、xは初期値のままにみえる。

#2で、すべてのスレッドから、xとyが一斉にインクリメントされたように見える。この点では、同期ブロックもアトミックブロックも変わらない。また、アトミックブロックも、観測できる範囲では、同期ブロックと同じく、順序だって評価されているように見える。

では、アトミックブロックは、同期ブロックとどう違うのか。同期ブロックというのは、単なるプログラム全体で単一のmutexを共有するような想定だが、アトミックブロックというのは、何らかのハードウェア、ソフトウェアによる効率的なトランザクショナルメモリーの実装が行われることが期待されている。もちろん、規格上、どのように実装されるかという詳細は規定していないのだが。

また、規格ではTransaction-safeという概念を提唱し、Transactional Memoryのブロック内で行える処理に制限を設けている。また、関数をtransaction-safeとtransaction-unsafeに分けている。これらはキーワードで明示的に指定することもできる。既存のSTLコンテナーなどの標準ライブラリはtransaction-safeで使えるべきで、対応すべきだという意見も提示している。

[読みづらいPDF] N3861: Transactional Memory (TM) Meeting Minutes 2013/09/09-2014/01/20

トランザクショナルメモリーに関する会議の議事録

[PDF廃止に向けて] N3862: Towards a Transaction-safe C++ Standard Library: std::list

GCC 4.9に実装されたTransactional Memoryの実験的実装を使い、libstdc++のstd::listを、実際にtransaction-safeにしてみる実験。変更は最小限ですんだそうだ。

そのコードはGitHubに上がっている。

mfs409/tm_stl: A scratchpad for working on making some of the C++11 STL code from gcc 4.9 transaction-safe

残りの論文も、追って解説する。

ドワンゴ広告

この記事はドワンゴの勤務時間中に書かれた。

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

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

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

アマゾンの欲しい物リストで送られてきたものと、付随した変なもの

先日、アマゾンで江添のほしい物リストを公開した。以前から、アマゾンのウィッシュリストは公開してみたかったのだが、一人暮らしの身では、なにか面白いものが送られてきても、反応に困るという問題があった。今住んでいるシェアハウスならば、大抵のものは処理できる。必要な人間がいるだろうし、あるいはいい笑いの種になる。以前、実際にかの悪名高い天竜川の洗い砂が送られてきたこともあったらしいが、それも土地を持っている住人の庭にまくという方法で、問題なく処分した。

そういうわけで、様々なジャンルからほしい物、面白いが金を出して買う程でもないものをぶち込んだお気に入りリストを公開した。

まず送られてきたのは、 ガベージコレクションのアルゴリズムと実装だ。そして、私はリストに追加していなかったが、注文に付け加える形で送られてきたのが、ECMA-262 Edition 5.1を読むだ。GC本は前から欲しかった本だ。ECMAScriptの規格書の方は、私はJavaScriptの規格ならば、C++ほど読み込んではいないものの、原文を読めるので、翻訳は必要ない。翻訳するということは、翻訳作業に起因する問題が生じるので、なるべくやりたくないことだ。ECMAScript規格翻訳本をどうするかは、今検討中だ。おそらく、誰か人に譲るのがいいのだろうが、しかし、やはり原文を読むべきではないか。

次に送られてきたのは、 T-fal 電気ケトル ジャスティン プラス カカオブラック 1.2L KO340870だ。電気ケトルは、すぐに湯を沸かせる便利な文明の利器で、大変重宝している。まずはお茶をいれるためのお湯を沸かすのに使った。

問題は・・・電気ケトルに付随して送られてきたものだ。

なにやら赤い布切れが入っている。これは・・・ 【日本製】 褌(ふんどし) 赤色無地だ。赤い越中ふんどしが入っていた。さて・・・コレは一体・・・どうするべきか。

筆者は日本人であるが、いまだにふんどしというものをはいたことはない。筆者は服には機能性を求めるので、しめるのにひと手間かかるふんどしを履くことはなかった。しかも、赤いふんどしというのは、日常に使うには不向きだ。そもそも筆者は越中ふんどしのしめ方を知らない。幸い、越中ふんどしは、褌の中でも締めやすい方ではある。これが六尺褌だったら、面倒なことになっていたはずだ。

さて、褌だけしめて洋服というのもおかしい。この機会に、作務衣や和服、足袋、雪駄のたぐいも江添のほしい物リストに追加することにした。

さて、ふんどしだけかと思ったら、まだなにか小さな小箱が入っていた。これは・・・ ISHOKUYA(衣飾屋) ユニーク タイピン タイ止め タイバー 纏 まといだ。

さて、ユニークという言葉の使い方が間違っている。なぜか唐傘のような意匠のタイピンだ。しかし、私はふだんネクタイという現代版の奴隷の鎖で首枷をする習慣はない。奴隷が鎖を自慢することが愚かであるように、私も鎖の自慢はしない。いわんや鎖を固定するにおいてをや。

そして、同封の紙にかかれていたメッセージは、

就職おめでとうございます!ビジネスマンたるもの見えない所の身なりもきっちりしないといけませんね!

なるほど、真のビジネスマンは赤ふんに唐傘ネクタイピンを装備するものなのだな。いやぁ、社会人経験の浅い筆者には、社会人の常識というものが欠けていたものと見える。これからはしっかりと社会人の常識を身に着けねばならない。ただし、聞くところによると、この社会人の常識というものは、実行環境ごとの互換性が一切ないシロモノらしいのだが。

江添のほしい物リストは、まだまだなにか面白いものを追加する予定だ。

2014-02-13

ドワンゴに入社した

そう。タイトル通りだ。筆者、江添亮はドワンゴに雇用された。一体、どのような経緯でドワンゴに入社するに至ったのか。また、どんな仕事をしているのか。それを説明するには、時系列を追って書いたほうがいいだろう。

2013年8月21日

ふとみると、以下のようなサブジェクトのメールが届いていた。

【ご相談】ドワンゴ主催の C++11, 14 に関する勉強会にスピーカーとしてご参加頂けないでしょうか

C++11? C++14? なんと、日本にC++14などという単語を知っている企業があったのか。しかし・・・ドワンゴ?

SPAMだろうか。いや、こんなにピンポイントなSPAMがあるわけがない。

それにしても解せないメールだ。ドワンゴといえば、もちろん、あの有名なニコニコ動画の企業だ。ニコニコ動画と言えばWebサイトだ。ニコニコ動画やその関連サービスの開発にC++を使っているのだろうか。いやまて、たしか子会社にチュンソフト(現スパイク・チュンソフト)がいたはずだ。ゲームならばC++はありえる。しかし、今の日本のゲーム業界は、とてもC++11/14の勉強会を開ける状況とも思えないのだが、一体どういうことだ。いや、いくら子会社とはいえ、ドワンゴの名前で来るだろうか。あるいは、単に宣伝目的で勉強会を主催したいのだろうか。

ともかく、C++14であれば、ドラフト規格はもうほとんど固まっている。今回は、マイナーアップデートとはいえ、C++03と違って、コア言語にも小粒な変更をいくつも含んでいる。C++14で追加されたコア言語機能の紹介をすればよさそうだ。しかし、釣りではあるまいな。

2013年11月14日

そうして、歌舞伎座.tech#2が開催された。

当日のスライド資料:C++14の新機能

当日のTwitterのまとめ:歌舞伎座.tech#2「11/14なのでC++11/14のお話」ハッシュタグ#kbkz_tech保管庫 - Togetterまとめ

技術評論社の記事:第7回 エンジニアなら,一生学び続けるのは当たり前――社内勉強会を重視する社風から生まれたドワンゴ主催「歌舞伎座.tech」:IT勉強会を開催するボクらの理由|gihyo.jp … 技術評論社

勉強会の告知では、平日の夜なのにも関わらず、100人枠が一日で埋まっていた。さすが東京といったところか。

この勉強会のドワンゴ社員の発表によれば、ドワンゴのバックエンドにはC++が使われているという。それは知らなかった。

さて、勉強会の次の日に、懇親会という形で食事に誘われた。その食事の席で、話の流れで、私がドワンゴで働く可能性についての話題に向かっていった。

しかし、働くといっても、今の私は、C++の標準規格という、とても狭い範囲のみを深く学びすぎてしまった。特に、この数年間、C++の参考書の執筆をしていたのは影響が大きい。この数年、私はいかに簡潔なコードで、C++のある特定の機能の説明をするかということのみに注力してしまった。残念ながら、今仕事用のコードを書くといっても、未経験者とそれほど変わらない程度の能力しかない。それに、私は不自由なソフトウェアを使いたくないし、書きたくない。

あるいは、C++の教育を行うというのも、企業に雇われる形では難しい。というのも、たいていの企業に雇われた教育者というのは、単に社内の閉鎖的な環境に引きこもり、表に出てこない。私は、表に出せないC++の教育などしたくはない。というのは、そのような企業内の教育者は、C++標準化委員会に存在しないからだ。C++の規格を真面目に理解しようとすれば、必ず表に出てきて、議論に参加しなければならないし、いずれC++標準化委員会に籍をおくことになる。

それに、C++の教育を公にできれば、ドワンゴ社内にとらわれずに、皆がC++を学ぶことができ、日本のC++プログラマーの能力の向上にもつながる。それは、やがてはより優秀なC++プログラマーが労働市場に存在することにもなる。

そもそも、私が常に変わり続けるC++の標準規格の変更についていけるのは、圧倒的に自由な時間があるからだ。もし、雇われた後もC++の規格についていくためには、私はいつでも好きなときに好きなだけ、C++WGのドラフト規格や論文を読めなければならない。さもなければ、私は雇われた時のC++規格から先にはついていけない。

しかし、ドワンゴによれば、それでいいのだという。さらにドワンゴは、会社としてできることをするという。たとえば、C++標準化委員会の委員(現時点で、筆者はエキスパートメンバー、委員はスポンサーが必要)にしてくれるとか、C++の国際会議に出してくれるなどという話も出た。

私の仕事は、ブログを書くことや、C++の講演会をすることといった、C++の啓蒙活動になるという。

にわかには信じがたい話だ。しかも、いま話している場所は、非公式の食事の席である。とにかく、来月12月に、もう一度、講師として来てくれということで、その日は終わった。

2013年12月18日

さて、前回の勉強会から一ヶ月後に、また講師として呼ばれた。今回は、社内の講習会で、C++の歴史について話してくれということだった。ドワンゴの川上会長も見に来るのだという。幸い、私はC++の歴史にも興味があり、特にプログラミングの魔導書 ~Programmers’ Grimoire~ vol.1を書くときにも相当に調べたので、好都合だ。また、この機会に、C++WG JPの設立当初の人に、当時の話を取材した。

その結果書いたのが、C++の歴史というスライド資料だ。

さて、当日の私は、わざわざ外部から講師を招くぐらいだから、ドワンゴ社員の数十人もみるのだろうと考えていた。しかし、何故か当日通されたのは、机を囲んで椅子が七席あるだけの小規模な会議室。そこに、今の私の上司が座り、ドワンゴ社員が数名座り、そして川上会長が来て、いわゆる「社内講習会」が始まった。

C++の成り立ちから、C++の標準規格の制定方法までを解説し、そして、間に少し挟んだC++の機能解説を始めた。

川上会長がテンプレートに興味を示したので、テンプレートの機能を解説した。しかし、どこまで詳細に解説してよいものか。事前に、川上会長はC言語は分かると聞いているのだが、どこまでC++の詳細を理解してくれるだろうか。

どうやら、川上会長の興味は、テンプレートがどのように実装されるのかという低級な方向に向いているようだった。そこで話がコンパイラーの実装に進み始めた。ただし、なぜか川上会長の興味が低級層にあるためか、話の方向がどんどん低級よりになっていった。

江添「・・・ですから、オーバーロード関数やテンプレートといった、同じ名前で、異なるコードが生成される機能を、古典的なコンパイラーとリンカーの上に実装するには・・・」
川上会長「ああ、シンボル名に埋め込む必要があるわけか」
江添(ほう・・・)

どうやら、川上会長は名前マングリングの必要性をすでに理解しているようだ。それならば話は早い。何も遠慮するには及ばない。名前マングリングの必要性がパッと出てくるぐらいならば、他の詳細も全力を出して解説しても理解できるだろう。

しかし、テンプレートにODRの例外が認められているというところで、説明が停滞した。この部分は、かなり複雑な歴史と、コンパイラーの実装方法の経緯と進歩が関わっているので、口頭で説明するのはとても難しい。結局、この部分を理解してもらえたかどうか自信がない。

具体的な説明は、魔導書Vol.1にも書いたのだが、いい機会なので、改めて以下に書いておいた。

本の虫: テンプレートの実体化の実装方法とODR違反について

さて、数人しか聴衆のいない「講習会」が終わったあとで、別室に通されて一時間ほど話した。

私の仕事は、「C++の啓蒙」だという。このブログを書く、C++の勉強会を開くなどといった、C++の啓蒙活動だけすればいいという。

江添「私は不自由なソフトウェアを使いたくないのですが」
ドワンゴ「いいですよ、私物のPCで作業してもらえれば」
江添「私は不自由なソフトウェアを書きたくないのですが」
ドワンゴ「いいですよ、社内外へのC++の啓蒙だけすれば」

そして、C++の国際会議に出してくれるなどといった話も出た。

たしかに、もし本当にこの仕事内容であれば、私は毎日出社する必要はない。ブログは自宅でも書ける。もし本当にこの仕事内容であるとするならば、そのような極端な言葉が出てきても不思議ではないのだが、果たして。

内定通知書が届けば、ドワンゴに内定したことは公にしてもいいという話ではあった。しかし、筆者は、ドワンゴに実際に入社して、一週間ほど働いてみてから、公開しようと考えた。というのも、この口頭で交わした条件が、実際に守られるかどうかは、実際に働いてみるまでわからないのだから。

そうして、引越と引越し先の環境整備をしているうちに、とうとう、ドワンゴの入社日がやってきた。

2014年2月3日

歌舞伎座タワーに入っているドワンゴ本社に行き、必要な書類に署名をし、社内の出退勤記録などの社員管理のシステムをわずかに説明された後、私に割り当てられた机に案内されて、そして放置された。その日は少しだけC++WGの論文を読み進め、社員管理のシステムの操作方法を覚えるなどして終わった。

2014年2月4日

ドワンゴの周辺でまともな飯屋がないので、弁当を作ってから出社した。この日も論文と環境整備だけで終わった。

2014年2月5日

この日は、結構論文を読んだ。昼に食事をしながらカタンをした。

2014年2月6日

今日は、ためしに朝早く出てくることにした。なんと、午前10時に出社した。ほとんど人がいなかった。ドワンゴでは、午前10時というのは早朝なのだ。

論文を読み進める。

2014年2月7日

今日は、三叉の電源コードが欲しかったので、安いジャンク品を求めに朝から秋葉原に行った。ついでに、秋葉原を散策してみた。

ドワンゴには昼過ぎに出社した。

また論文を読む

ドワンゴの労働環境

ドワンゴの正社員には、真の裁量労働制が行われている。コアタイムはない。会議などであらかじめ予定が決まっている時以外は、何時に出社してもいいし、何時に帰ってもいい。たったの一時間会社にいるだけでも、その日は出社したとみなされる。

また、社内の労働も、相当に裁量がある。社内でゲームをしていたり寝ていたりする人までいる。

さて、そのような裁量が与えられた環境で、果たして人は真面目に働くのだろうか。これは、プログラミングという仕事においては、どうやらうまく動くようだ。

そもそも、毎日同じ時間に集合して労働するという集約労働は、単調作業には向いているかも知れないが、プログラミングという作業には向いていない。プログラミングには集中力が必要であり、その集中力は毎日決まった時間に発揮できるわけではない。そして、最高の集中状態を保てる時間というのは、それほど長くはない。

そもそも、本物のプログラマーは、余暇にも趣味のプログラミングを行うものである。本物のプログラマーが働きやすい環境を整えてやれば、自然に働くものだ。

ただし、プログラマーに真の裁量性を与えると、自然と昼過ぎに出社して夜遅くまで働く形になってしまうようだ。ドワンゴでは、11時から昼過ぎにかけて出社し、午後9時頃まで働く人が多い。

さて、こうして一週間出社したが、どうやら事前の話の条件は守られているようだ。

そもそも、今はちょうど2014-01 pre-Issaquah mailingが公開されたばかりだ。このブログで恒例の簡易レビューを書かなければならないが、今回は、50本以上もの論文があり、多くがC++17への提案論文なので、相当に時間がかかる。また、仕事としてやる以上、いつもより丁寧に読みたいし、「私はコア言語が専門だからライブラリは知らない」というわけにもいかない。今まで読み飛ばしていたライブラリの論文も読んでいるので、今回の簡易レビューはかなり時間がかかる。

そういうわけで、ブログを書くという話で入社して一週間、まだC++の記事は何も書いていない。たまたまC++WGの論文集が出る時期にあたったからだが、私はひたすら論文を読んで簡易レビューを書き進めているだけなので、単に机に座っているところだけを見ると、仕事をしているのかサボっているのかさっぱりわからないように見えるはずだ。

ところで、ドワンゴ社内の環境は、ブログ執筆のためには、自宅に劣らないほどよい。というより、今の自宅は、まだまともな椅子と机とディスプレイを揃えていないので、ドワンゴ社内に劣っている。早く自宅も快適な環境にしなければならない。

仕事

筆者のドワンゴにおける仕事は、C++の啓蒙活動だ。

具体的には、ブログを書くこと、C++の講演会をすること、ドワンゴの宣伝、そして、最大の目標が、「日本のC++の教育を再び活性化させること」だ。

この最大の目標をどうしたものか。今や、教育機関に、C++標準化委員会に籍をおいている者がいない。C++の規格に詳しいものが現状でいない以上、C++の教育など絶望的だ。教育を活性化させるには、C++プログラマーの働き口を増やさなければならない。それはドワンゴ一社のみでは難しいので、日本の企業全体を巻き込まなければならない。しかし、それも難しいのだ。現状の日本では、C++が使われている職場では、C++未経験者を雇って、やっつけ仕事をさせることが常態化している。C++の文法がわからず、何時間もコンパイラー(それも不自由で規格違反だらけのコンパイラー)にあてずっぽうで適当なコードを通そうとやっきになっている。残念の極みである。

日本では、標準規格の価値が理解されておらず、閉鎖的な独自仕様で囲い込んだ挙句、標準規格を作った海外勢に押されて負けるという歴史を繰り返している。ああ、いつになったら学ぶのか。

この現状を、どうやって変えていけばいいのだろうか。目標があまりにも巨大で漠然としていて、どこから手を付けていいのかわからない。とにかく、できることをしなければなるまい。さしあたっては、C++の解説記事を書くことと、C++の講演会を開くことにする。

また、このブログ記事を使いやすくするために、近々GitHub Pagesでも同一内容を公開しようと思う。

ドワンゴ広告

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

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

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

2014-02-10

東京は住みにくい

さて、東京に引っ越して数週間たった。結論から言うと、東京は住みにくい。

東京に来てまず気がついたのは、メシのまずさだ。どの飯屋に入っても、京都より数百円高い上に、クソまずい。私は未だに、東京でまともなメシ屋を発見したことがない。それは、何万円も払うような店ならば、少しは違うのかも知れないが、私はそんなごちそうを食べたいのではない。普通のメシを普通の値段で食べたいのだ。東京の飯屋は、800円や1000円も取る割に、どうやったらこんなにまずく作れるのだと疑問に思うほどまずいメシしか出てこない。

色々と考えた挙句、結局、東京には出汁という文化がないのではないかという結論に達した。東京には油か醤油の薄め液しかないのだ。

とくに、うどんとそばが最悪だ。そばはまだともかく、うどんはつゆの良し悪しで味が大きく左右されるというのに、東京の飯屋のめんつゆは、醤油の薄め液のようなものしか出てこない。パスタにケチャップをかけて食うのじゃあるまいし、一体この文化のなさはなんだろう。めんつゆというのは、極端に言えば出汁だけでいいのだ。醤油などいらないのだ。

ラーメンもマズい。思えば、京都はラーメンが美味かった。私は歳のせいか、脂っこいものはあまり食べないのだが、それでも、京都でたまにラーメンを食べると美味しく感じた。しかし、この東京では、まともなラーメンを食べることができない。

結局、東京では一見さんだけを相手に商売できるので、マズい飯屋が自然に淘汰されないのだろう。そして、文化のなさにより、まともなメシをつくる誇りすらない。

そして、売っている食材も、あまりよろしくない。野菜や豆腐のようなものは、かなり地域性がでるものだ。東京で売っている食材はマズい。

なるほど、京都に住んでいた時、就職したが東京の本社で働かなければならなかったので、やめて京都でぶらぶらしているという人間が何人かいたのは、このせいでもあるのだろう。

東京はモノがあふれていて、何でも手に入るので便利ではあるのだが、普段食べるまともな食事という点では、極めて劣っている。

電車の問題は、いまさら私が書くまでもあるまい。

VLCメディアプレイヤーをインストールするとDellの保証が無効になる

Installing VLC Media Player voids your speaker warranty! - Laptop Audio Forum - Laptop - Dell Community

Dellのフォーラムで、ユーザーが、もし詐欺商品を売りつけるDellの貧弱で欠陥ラップトップで音量を最大にして音を再生すると、内蔵スピーカーが壊れ、しかも、VLC media playerがインストールされると、保証が無効になったという報告を上げている。

以下、粗悪な欠陥品を売りつけるDellの詐欺に騙された無知な顧客のコメント

Dell技術サポートは、有名なVLC Media PlayerがDellのラップトップにインストールされていた場合、壊れたスピーカーの保証を無効にするぞ。それから、KMPlayerがラップトップにインストールされてるとサポートを拒否されるという報告もある。ラップトップの他の部分の保証は有効らしいが。

気をつけろ。使っていたかどうかにかかわらず、特定のプレイヤーをインストールしていただけで保証を拒否される。技術サポートに連絡する前にプレイヤーを消して、Windows Media Playerだけ残しておけ。

VLCは他のプレイヤーよりも音量を大きくする機能がある。どうやらVLCにはハードクリッピングを発生させて、小さなスピーカーを壊すらしい機能があるらしいのだ。DellはVLCをテストして、数時間のVLCの仕様により、スピーカーが壊れることを確認したそうだ。また、HPもVLCによる故障は保証外だと言っている。

これは、Dellのラップトップの設計上の欠陥である。Dellのラップトップでは、サウンドチップが、内蔵スピーカーの許容入力以上の出力を送ってしまっているのだ。

Installing VLC Media Player voids your speaker warranty | Hacker News

VLCの主要開発者として、この我々はこの問題を昔から知っている。これは単に、Dellがクソな部品を使っていて、他人のせいにしているだけだ。奴らにはまともな議論が通用しない。ちょっと説明してやろう。

この件では、VLCは単にWindowsのAPI(DirectSound)を使っているだけで、Windowsカーネルに16bit符号付き整数を送っているだけだ。

VLCには、デコードされた時以上に入力を増幅することができる。これはリプレイゲインとか壊れたコーデックとか録音がひどかったファイルとか、ポストプロセスによる増幅で、サチュレーションが発生するのと同じだ。

これは、単にmp3ファイルをAudacityに突っ込んで音量を上げ、WMPで再生するのと同じことだし、DirectShowフィルターで、コーデック出力の後に増幅するのと同じことだ。例えば、だいぶながいこと、VLCのac3とmp3デコーダーは、リファレンス出力に比べて、音が小さかった(-6dB)

これにより、音の強弱が失われたり、音が割れたりするが、ハードウェアを壊すことはない。

VLCはスピーカーを壊すように出力を変更することはないし、そもそも不可能だ。VLCは「公式」のプラットフォームAPIを使っているだけのソフトウェアだ。

この問題の原因は、Dellのサウンドカードが、Dellのスピーカーが耐えられないほどの出力(増幅の二乗)を出せてしまうためだ。問題のサウンドカードの最大出力は10Wだが、問題のスピーカーが許容できる入力はたったの6Wで、BIOSもドライバーも出力をブロックしない。

そして、VLCが多くのコンピューターに入っている現状で、VLCのせいにするのが簡単だったというだけだ。「相関関係は因果関係を意味しない」という概念は、貧弱なDellのサポートの頭には複雑すぎて理解できないことらしい。

たぶん、Dellはメタル音楽の再生をやめるように告知して、セリーヌ・ディオンの再生のみを許可するべきだろう。メタルは音割れが頻繁に発生するからな・・・

追記:やつらはこの問題を修正するためのBIOSアップデートまで提供してるんだぞ。もちろん、VLCの問題じゃない。

Distorted Sound From Notebook Speakers When Using VLC Media Player | Dell US

なぜだか、世間では音声の増幅というのは魔法のように行われるらしい。VLCの所謂100%以上の増幅とは、単にカーネルに流しこむビット列を変えているだけなのだ。

結論を言うと、Dellのラップトップとサポートはクソだということだ。

2014-02-09

妖怪ハウスに引っ越したことと、アマゾンの欲しい物リスト公開のこと

先月、筆者は東京に引っ越した。引っ越した先は、野方にあるシェアハウス、妖怪ハウスだ。

シェアハウスに引っ越した理由として、直接的には、今、手元に現金がほとんどないということだ。賃貸は古臭い利権と慣習に縛られており、なぜか借り手が保障費や連帯保証人などの保険のような費用をださなければならない。金を出すのはいいとして、それが月々の家賃と分離されていて、見かけ上家賃を低く見せかけているように見えることだ。これは問題だ。家賃と行った時、それは実際に払う金額の総額を言うべきであって、様々な名目で金額を分割すべきではない。たとえば、コンビニに行って100円のおにぎりを買うときに、米いくら、具いくら、包装いくら、人件費いくら、などと細かい名目で、それぞれに金をはらわないように、賃貸の世界でも、借り手が払う金額は一元化すべきである。

それはともかく、いまそのような保険費用を払う現金が手元にないし、ましてや連帯保証人なども難しいので、シェアハウスに住むのが、一番手っ取り早いと判断した。

もうひとつの理由としては、孤立を防ぐためだ。東京に係累のない筆者が、東京にやってきても、孤立するに決まっている。それに、もう一人暮らしは飽きるほどしてきたのだ。もうしばらくは十分だ。

京都にいた最後の数カ月に、吉田寮を体験したということも大きい。なるほど、こういう形で住んでいても、特に問題は起こらないのだ。

そして、去年の12月に、住むべき場所を探して、いくつかのギークハウスとシェアハウスを見学した。その結果、妖怪ハウスに住むことになった。

さて、1月の半ばに妖怪ハウスに引っ越してきた。しかし、12月に滞在したときは気にならなかったものの、実際に住みだすと気になることもあるものだ。

掃除

家の汚れだ。どうやら、ここの住人は余り総地をしないとみえて、あちこちが汚れている。特に、トイレと洗面台と風呂場がひどい。まあ、気になったものが掃除をすればいいのだ。というわけで掃除をした。それにしても、このトイレと風呂場の汚れ具合はひどい。女も住んでいるというのに、気にならないのだろうか。いや、まてしばし、この考えは男女平等の精神に反する。戒めねばならぬ。

ゴミ捨て

妖怪ハウスには、だいぶゴミが溜まっていたので、ゴミの日に捨てた。まだ燃えないゴミが、日時を逃したために残っているが、来週に捨てる予定だ。

食事

東京にやってきて驚いたのが、メシのまずさだ。どこの飯屋に入っても、京都より数百円高い上に、クソマズい。いったいどうやったらこんなにマズい作れるのだというぐらいにマズい。唯一食べられるのが、全国チェーンの安い店だ。どこでも変わらない味と値段を提供している。そんな店すらまともに思えてくるぐらい、東京の飯屋はマズい。

仕方がないので、自炊することにした。また、どうも妖怪ハウスの住人は、毎日三食定期的に食べておらず、またその食事内容もジャンクフードが多いので、やはりまともな食事を提供する必要がある。そのため、食事を作ることにした。

その他、必要なものや、やるべきことはたくさんあるのだが、とりあえずは金が必要だ。

そこで、この機会に、アマゾンの欲しい物リストを公開することにした。

Amazon.co.jp: 江添亮: 江添のほしい物リスト

アマゾンの欲しい物リストは、リストの中野商品に加えて、任意の商品を送ることができる。そうすると、とんでもなく変なものが送られてくる可能性がある。たとえば、天竜川の洗い砂などだ。一人暮らしの環境で、変なものが送られてきても、特に面白くもなんともないし、処分にも困る。ただ、このようなシェアハウスであれば、変なものが送られてきても、何かしら使いどころはあるだろうし、少なくとも笑いは取れる。ただし、どうせ変なものが送られてくるぐらいならば、最初からリストに付け加えたほうがいい。

というわけで、あらゆるジャンルから、ほしい物から、送られてきそうな変なものまで、ひと通りは付け加えた。現在、500アイテムほどある。なにか面白いものを見つけ次第に追加する。

C++に疑問がある場合や、ボードゲームがやりたい場合や、私の手料理を食べたい場合は、妖怪ハウスに来るといい。

さて、なぜ私が、にわかに東京に引っ越してきたのか。それは、東京で雇われて働くことになったからだ。では、その雇用主はどこなのか。いったい私はどんな仕事をしているのか。その謎は、来週にも明かす予定だ。

Debianの新しいinitにsystemdが採決された

Bug#727708: call for votes on default Linux init system for jessie

どうやら、Debianの新しいinitシステムは、sysvinitからsystemdになったようだ。その他の候補は、Upstartとopenrcだった。

ギークハウス南千住

昨日、歌舞伎座で心謎解色糸をみた帰りに、ギークハウス南千住に行ってきた。

昨日はあいにくと大雪で、場所を探すのに難儀した。結局、案内を乞うた。いまだに、案内なしで辿りつけたギークハウスは存在しない。

ギークハウス南千住は、主に高専生が集まって作ったシェアハウスである。シェアハウス自体で利益を出すことを目的としていないため、一人あたりの金銭的負担は小さい。

ところで、いまの住人の高専生は、もうじき卒業してしまう。卒業すると、環境が変わるので、おそらくギークハウス南千住を出て行く事になるだろう。では、ギークハウス南千住は、将来的には消失するのだろうか。この疑問を問うてみたところ、「もし、後輩が引き継ぐのであれば存続する」とのことであたt.

シェアハウスの継続は難しいものだ。

ギークハウス南千住では、高専について話を聞き、スパゲティをごちそうになった。その日は雪なので、早めに帰った。

2014-02-04

筆者近況

現在の筆者の状況について、詳細に書きたいが、色々と引越やら風邪やら新しい環境の整備や構築が立て込んでいて、ここ数週間、あまり時間がなかった。もうすべてを公表してもいいのだが、経緯などを詳細に書きたいので、要点だけを列挙する。だいたい察しがつくだろう。

今、筆者は東京に引っ越して妖怪ハウスというシェアハウスに住んでいる。住んでいる場所の詳細については、近々詳細に記事にする。

東京で仕事をしている。仕事の詳細については、近々詳細に記事にする。

すでに、2014-01-pre-Issaquah mailingが公開されている。もちろんこれは、いつものように簡易レビューするが、今回は論文の数が相当に多いし、それにせっかくの環境だから、少し丁寧に論文を読み込みたいので、いつもより時間がかかる。

このブログについても、色々と見直しが必要だ。設置してある広告についても、少し調整が必要だ。また、このブログはGFDLでライセンスされているが、記事単体を使いやすい形で提供されていない。これも、GitHubなどにも記事を上げるなど、なにか工夫が必要だ。

というわけで、色々と新しい環境での雑事に追われているので、今月はブログ記事が減るかも知れないが、これは一時的なものだ。むしろ、今年はブログ記事が増えるだろう。

しかも、これからは、このブログにおける、特にC++関連の記事が充実するだろう。同時に、ブログ以外でも、C++の啓蒙活動が活発化する。

2014-02-03

不自由なSaaSSを使った報い

BenjaminSte.in - iOS holding my phone number hostage = the worst bug I’ve ever experiencedだった

iOSが俺の電話番号を監禁している = 俺のであった中で最悪のバグ

二ヶ月前、俺はiPhoneからAndroidに移った。俺は初日からiOSのファンだったが、iOS7には我慢ならなかった(それは別の記事の話だが)

俺はVerizonのMotoXに大変満足してる。24時間以内に、今まで使ってたアプリは全部切り替えられて、Androidを俺のメインの携帯として準備することができた。

だが、よくわからないことがあったのだ。どうやら、俺はあまりテキストメッセージを受け取れていないらしい。俺のメッセージに反応がないことはイラつくし、俺も反応できないことで人をイラつかせている。

なんなんだこれは。俺の仕事用のラップトップは、まだMessages.appに設定されているので、メッセージは全部、iMessageに送られていた。ラップトップには来るが、携帯には来ないってわけだ。

こいつはイラつくが、まだ分かる。月曜日に職場に言って、iMessageを切って、それでうまくいくと思っていた。

ぜんぜんうまくいかねぇ。

これは8週間後の話で、現状はこうだ。

もし、iOS使いの友人(俺の友人の99%を占める)が俺にメッセージを送ったら、携帯は、まず、iMessage経由で送ろうと試み、失敗する。仕組みのわかってる奴らは、「テキストメッセージとして再送」というオプションを選択肢て、送ってくる。これはイラつくが、まあ、それほど最悪ってわけでもない。

もし、友人がグループメッセージに俺を含めた場合、携帯はGroup iMessageとして送る。Group MMSではないので、俺は受け取れない。ここが問題だ。静かに失敗してしまうのだ。「失敗」したという通知を受け取らない。メッセージは単に消えて、受け取ることはないし、その失敗したことを知ることもない。

Appleはこの問題を解決するために骨折りをしてくれた。残念ながら、彼らの提示した解決方法は、俺様の友人全員に俺がいたすべてのメッセージスレッドを削除するよう伝えるってぇことだ。

もういっかい状況を説明するぞ。おれはもうiPhoneを持ってないんだ。俺の電話番号は、もうAppleやiCloudやiMessageやFaceTimeに紐付けられていないんだ。だが、俺が過去五年にメッセージを送ったすべてのiOSデバイスは、俺の電話番号をキャッシュしちまってる! その携帯すべてが、俺にメッセージを送るときはiMessage経由で送ることを試みるのだ。SMSじゃねーんだ。さらに悲惨なことに、静かに失敗しちまうってこった!

しかもだな、おれが過去3年にやりとりしたメッセージは、ほとんどグループメッセージで、しかも俺の子供の写真が入ってるんだよ。俺が5年分のメッセージ履歴を削除しなきゃならないだけでも最悪なのに、俺の嫁さん、妹、大親友、それと俺の知人全員に、メッセージ履歴の削除をしろと頼めというのか? ふざけるなッ!

俺は完全に監禁された気分だぜ。Appleの奴らには解決方法がないときている。Appleが俺の電話番号を換金していて、取り戻す方法がないってのは、狂ってる。

これが、不自由で邪悪なSaaSSを使った報いである。ここまでくると、喜劇ですらある。そもそも、すでにメールやIRCといった十分に実績があり、問題が修正されてきたプロトコルがあるのに、劣化した車輪の再発明をなぜするのか。