2014-08-29

2014-07 post Rapperswil mailingのレビュー: N4070-N4079

またもや次の論文10本

N4070: Improving the specification of the vector execution policy in Parallelism TS

Parallel TSのベクトル実行アルゴリズム内で実行される関数が同期をすることは禁止されている。しかし、標準ライブラリの関数のうち、同期を行わない保証のある関数なのかということは、明記していない。

これを解決するために手始めにそのことを警告する文面を追加する提案。

N4071: Technical Specification for C++ Extensions for Parallelism, Working Draft

C++のアルゴリズムに並列実行と並列ベクトル実行版のオーバーロードを追加して拡張するTSのドラフト。

従来のアルゴリズムに、タグを指定することで、シーケンシャル実行、並列実行、並列ベクトル実行を切り替えることができる。

// N4071提案
#include <experimental/execution_policy>
#include <experimental/algorithm>

template < typename Iterator >
void f( Iterator first, Iterator last )
{
// シーケンシャル実行
    std::stable_sort( std::seq, first, last ) ;

// 並列実行
    std::stable_sort( std::par, first, last ) ;

// 並列ベクトル実行
    std::stable_sort( std::par_vec, first, last ) ;
}

N4072: Fixed Size Parameter Packs

パラメーターパックの個数を指定できる機能、固定数パラメーターパックの提案。

// N4072提案

// argsは個数3、型はint型
void f( int ...[3] args ) ;

// argsの個数は3
template < typename ... Types >
void g( Types ... [3] args ) ;

// Argsの個数は3
template < typename ...[3] Args >
void h( Args ... args ) ;

T ...[N] pは、N個のT型のパラメーターパックpである。Nは定数式でなければならない。

いったいこの機能何に使うのか。論文では、いくつかの利用例を挙げている。

指定した個数の実引数を取りたい場合を考える。

struct my_vector3 {
    // ...
    my_vector3(int x, int y, int z)
        : values{x, y, z} {}
};

こんなクラスがあるとする。my_vector4とかmy_vector10などを手で書くのではなく、my_vector<N>のように、テンプレートで個数を指定したい。

実は、これは現行のC++14の枠内で行うのは、とてつもなく面倒で長ったらしいコードを書かなければならない。しかし、固定数パラメーターパックがあれば、以下のようにあっさりと書ける。


template<unsigned int N>
struct my_vector {
    // ...
    my_vector(int...[N] v) : values{v...} {}
};

指定した個数のある型の引数を取りたい。

void foo3( int, int, int )のような関数があるとする。ここで、foo4のような関数を手で書くのではなく、foo<N>のように、テンプレートで個数を指定したい。

しかし、これをC++14で簡潔に書く方法がない。固定数パラメーターパックならば、以下のように簡潔に書ける。


template<unsigned int N>
void foo(int...[N] v);

あるいは、単に機械的なパターンを手で書く代わりに使うこともできる。


void foo(int x, int y, int z, int w) { do_magic(x, y, z, w); }

template<typename A, typename B, typename C, typename D, typename E>
void bar(A a, B b, C c, D d, E e);

のような機械的なパターンのあるコードは、


void foo(int...[4] v) { do_magic(v...); }

template<typename...[5] T>
void bar(T... v);

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

この提案では、実引数推定で、固定数を推定することができる。

例えば、以下のような例は実引数推定できる。

template < unsigned int N >
void f( int ... [N] ) { }

f( 1, 2, 3 ) ; // Nは3と推定される

template<unsigned int N> void f(int, int...[N]) {}
f(1,2,3); // Nは2と推定される

template<unsigned int N, unsigned int M> void f(int...[N], int...[M]) {}
f<2>(1,2,3); // Mは1と推定される。Nは推定できないが、明示的に指定されている

例えば、以下のような場合は、固定数パラメーターパックで実引数推定できない。


template<unsigned int N> void f(int...[N], int) {}
f(1,2,3);

template<unsigned int N> void f(int...[N], int...[N]) {}
f(1,2,1,2);

template<unsigned int N> void f(int...[N*2]) {}
f(1,2,3,4);

[暗い未来を予感させるPDF] N4073: A Proposal to Add 2D Graphics Rendering and Display to C++

C++の標準ライブラリグラフィックライブラリを追加することに対する考察と提案。cairoに機械的な変換を書けてC++風の設計にしている。

[Let PDF-writer die in agony] N4074: Let return {expr} Be Explicit, Revision 2

return {expr}の意味を、return-type var {expr} ; return var ;と同じ意味にしようという提案。

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

struct very_long_type_name
{
    explicit very_long_type_name(int) { }
} ;

very_long_type_name f()
{
    return { 0 } ; // ill-formed 
}

このコードを合法にする提案だ。

return文のオペランドを{}で囲んだ場合、戻り値の型のローカル変数を直接初期化して返すのと同等の効果にしようという提案である。

関数の戻り値の型と、その関数内のreturn文とは、関数の中だけの話だから、十分に明示的である。{}で囲むというのも明示的である。

very_long_type_name f()
{
    return very_long_type_name{0} ;
}

これはいかにも冗長だ。

[無駄にPDF] N4075: Centralized Defensive-Programming Support for Narrow Contracts (Revision 5)

防衛的プログラミングと称し、assertマクロの高級版を追加する提案。

assertに引っかかった時の挙動を指定できたり、テストできたりと、色々と高機能だが、Cプリプロセッサーマクロを使うという時点で、筆者は反対する。

N4076: A proposal to add a generalized callable negator (Revision 4)

汎用的なnegator、not_fnの提案。

not1やnot2は引数がひとつか二つのとても制約の強い関数オブジェクトしからっぷできなかった。not_fnは、Variadic Templatesや最近のメタプログラミング技法を使い、任意個の引数を取る任意の呼び出し可能なオブジェクトをラップできる。

// N4076提案
auto f() { return true ; }

int main()
{
    auto n =  std::not_fn(f) ;
    n() ; // false
}

N4077: Experimental shared_ptr for Library Fundamentals TS

TSに提案されるライブラリが使うために、実験的なshared_ptr実装である<experimental/memory>と、独立したshared_ptr, weak_ptr, owner_less, enable_shared_from_thisの提案。

詳しい経緯はN4041にかかれているが、TS用の拡張として既存の標準ライブラリを使う場合、サードパーティによる規格準拠の実装が作れないという問題がある。そこで、規格準拠な実験的実装を行えるように、必要な標準ライブラリを、同一機能であるが、別ヘッダー、別名前空間の別ライブラリとして定義する提案。

N4078: Fixes for optional objects

Issaquah会議で指摘された、Optionalのドラフト文面に存在する問題の修正。どれも小粒な文面上の修正となっている。

N4079: C++ Standard Library Issues Resolved Directly In Rapperswil, 2014

rapperswil会議で修正された標準ライブラリに持ち上がっている問題集。

些細な文面上の誤りの修正が多い。

ドワンゴ広告

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

今日は都合で、いつもとは違い丸ノ内線に乗って通勤したが、明らかに周囲の背広集団から浮いている変わった服装と目立つ話し方をする人間を見かけた。銀座駅で降りて会社に向かったが、エレベーターの中で再開した。みると、ドワンゴの社員証を首から下げている。

さては同僚であったのか。

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

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

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

あるポルノサイトのアクセス解析によるOSシェア率によれば、GNU/Linuxユーザーは1.7%

この記事は大真面目に書いた。

[リンク先に危険な画像はない] OS Battle – Porn by the Platform

Phoronixに載っていて知ったのだが、有名なポルノサイトであるpornhubが、Webサイト上のトラフィックのOSに特化した解析結果をブログで大真面目に公開している。その例に習って、筆者も大真面目に内容の紹介と、大真面目な感想を書こうと思う。

まず、デスクトップOSのシェア率は、Windowsが85.5%、Macが10.9%、GNU/Linuxが1.7%だそうだ。

いかにWindows優位な市場とはいえ、Macのシェア率が世間一般より低いように思われる。Macユーザーはあまりポルノを必要としないのであろう。

Windowsの具体的なバージョンは、Windows 7が一番多く、ついでWindows XPである。なんと、Windows NTやWindows 2000を使っているものもいるらしい。また、月数千人は、Windows 95や98の利用者がいるそうだ。

モバイルOSのシェア率は、Androidが48.34%で、ついでiOSが40.60%だそうだ。

これも、実際の市場のシェア率とは異なり、iOSが低すぎるように思う。やはりiOSの利用者はあまりポルノ閲覧を必要としないに違いない。

タブレットOSでは、iOSが圧倒的なシェアを占めている。

これはどうみるべきだろう。Apple製品のユーザーはあまりポルノ閲覧を必要としないのではないかと思っていたが、Appleのタブレットユーザーはポルノを閲覧するらしい。あるいは、iPadの市場シェアが多すぎるためにこうなったのだろうか。

ゲーム機のシェア率もまとめられている。今のゲーム機はブラウザーを搭載している。ブラウザーを搭載しているということは、当然pornhubを閲覧できる。

ゲーム機のシェア率は、PlaystationとXBoxが二強である。Nintendo Wiiでポルノを閲覧する人はあまりいないらしい。

人気の検索クエリーで面白いのは、GNU/Linuxユーザーの検索クエリーは、インド女優の名前やインド人が多いらしい。なぜかというと、GNU/Linuxの利用者のかなりがインド人だからである。

OSごとの国別の閲覧者は、WindowsとMacではアメリカ合衆国が圧倒的であるが、GNU/Linuxでは、二位に圧倒的にインドが上がってくる。どうやら、インドのポルノ閲覧者におけるGNU/Linuxの普及率が相当に高いらしい。

ソースがソースなだけに、なかなか興味深いアクセス解析結果だ。

ちなみに、ここ一ヶ月のこの本の虫ブログ閲覧者のOSシェアは、Windowsが46.74%、iOSが20.58%、Macが13.40%、Androidが12.14%、GNU/Linuxが6.53%である。6割がデスクトップ、3割がスマートフォン、タブレットは4%ほどだ。

2014-08-25

大豆肉を入手した

アマゾンで、大豆肉を注文した。注文したのは、 マイセン まるっきりお肉フィレタイプ 200gと、 マイセン まるっきりお肉ミンチタイプ 300gである。

大豆と玄米から作られており、肉ではないが、肉のような食感を持つという。そろそろ料理に新しい刺激が欲しいので、試しに買ってみた。どのように料理するか考えている。面白そうなので人を呼んで食べさせたい。

ミンチタイプは、ミートソースにでもしようかとおもう。フィレタイプはどう料理しようか。

コンピューターを新調したいが踏み切れない

私は今、貰い物のラップトップをメインのコンピューターとして使っている。そろそろコンピューターを新調したいと思いつつも、なかなか踏み切れないでいる。

問題は、今のラップトップは、計算性能だけを考えれば、何の不満もないことだ。私の日々の作業に対して、CPUは十分に速く、メモリは十分な容量で、GPUだけはIntelのHD Graphics 3000なのですこし不満はあるが、私はグラフィック性能をそれほど必要としないので問題はない。ストレージもSSDだ。

不満は、純粋な計算性能以外の点にある。

  • 組み込みディスプレイの解像度が低すぎる。また、外部ディスプレイ出力もHDMIしかなく、規格のバージョンも古いので4k出力に対応していない。
  • WiFiも5Ghz帯に対応していない。

結局、不満点といえば、これぐらいしかないのだ。このためだけにラップトップを変えるというのはどうだろうか。しかし、ディスプレイもWiFiもその性能は致命的に低くはないものの、より優れたものが市場に出回っている現状だ。CPUの処理能力やメモリ容量がそれほど問題にならなくなっている今、純粋な演算能力以外のスペックが重要になってくる。

では、現時点で買うとしたら何であろうか。

東芝のdynabook T954は、なんと組み込みのディスプレイが4Kであるという。15.6インチのディスプレイが3840x2160の解像度を持つという。しかし、それ以外のスペックになんとも魅力がない。

Lenovo ThinkPadは、組み込みディスプレイのサイズが3kと、すこし劣るが、無線LANモジュールが選択できるという利点がある。しかし、Lenovoには悪い思い出しかないので、Lenovo製品を買うのは避けたい。不自由なnVidiaのGPUが載っていて、しかもOptimusであるのが最悪だ。

もうしばらく様子見がいいだろう。しかし、この様子見というのは問題のある態度だ。結局、我慢しようと思えば我慢出来てしまう以上、いつまでも最新のコンピューターの性能を体験できない。性能で作業効率が上がるのであれば、いい投資であるのだが。

GNU/Linuxで動くデバッガーのGUIフロントエンド比較

13 Linux Debuggers for C++ Reviewed | Dr Dobb's

drdobbs.comの記事に、GNU/Linuxで動くデバッガーのGUIフロントエンドを比較している記事があったので、メモ代わりに紹介しておく。

2014-07 post Rapperswil mailingのレビュー: N4060-N4069

[PDFも変更されるべき] N4060: Changes to vector_execution_policy

Parallelism TSに対する、ベクトル実行の定義を変更する提案。

並列実行とベクトル実行は、ループの個々の繰り返し単位を、順不同で実行するものである。このため、コードが実行される順序という保証がなくなる。また、例外や同期などの処理も行えないという制約がある。

並列実行ポリシー(parallel_execution_policy)とは、スレッド風の実行エージェントよりループを並列に実行するものである。ベクトル実行ポリシー(vector_execution_policy)とは、SIMDやGPUによる実行エージェントによりループを並列に実行するものである。

しかし、現実のアーキテクチャが提供するSIMD命令を考えると、SIMDによる1スレッド内のベクトル実行による実行エージェントとしては、現在のベクトル実行ポリシーの定義は、必要以上に制約が強すぎる。制約というのは、順序の無保証だ。1スレッド内のSIMD命令による並列実行では、先のループ単位の実行が、後のループ単位を追い越さないという保証を与えることができる。

当時、parallelism TSを議論している際には、実行ポリシーを、シーケンシャル、パラレル、ベクトル、パラレル+ベクトルに分類していた。しかし、純粋なベクトル実行に与えられるこの順序保証の需要があるのかどうか不明であったため、標準化委員会は、純粋なベクトルを廃して、パラレル+ベクトルを、ベクトルと称した。これにより、現在のベクトル実行はパラレル実行の制約をさらに強くする形で定義され、GPUなどの実行エージェントもサポートできるようになった。

ところが、Intelが最近、顧客のコードを検証したところ、ベクトル実行の順序保証を活用したコードが、すでに数多く書かれていることが判明した。コードが存在する以上、需要はある。そこで、この論文は、現在のパラレル+ベクトルとなっているベクトルの定義の変更を提案している。また、この変更は、もはやTSを変更する時間的余裕が無いため、TS本体への適用ではなく、TSに対する参考として公開する形をとっている。

提案は二つある。

最小限の変更による提案は、現在のベクトルは、実際にはパラレル+ベクトルなので、vector_execution_policyをparallel_vector_execution_policyに、vecをparvecに改名するものである。

理想の提案は、ベクトル実行を純粋にベクトル実行として、並列実行とは別に、最小限度の制限で定義するものである。

結局、こんなことになったのも、コードが表に出ないからだ。情報が表に出ないからだ。公開されていない情報は、存在しないも同義である。存在しないものには需要があるわけがない。標準化委員会が新機能を設計するにあたって需要を認識できるわけがない。C++は、長年、C++に利害関係を持つものが標準化委員会に参加し、知見を出し、規格に貢献して今の位置に至ったのだ。標準化委員会に参加しないどころか、情報すら表に出さないのでは、その分野は永久に標準化されることはない。

お前ら表にでろ。

[PDFを最小公倍数にするな] N4061: Greatest Common Divisor and Least Common Multiple, v3

最大公約数と最小公倍数を求める関数、gcdとlcmを<numerical>に追加する提案論文の第三版。

ちなみに、constexpr関数であり、さらに任意の整数型に対して使える関数テンプレートとなっている。

実装は短いが、標準でほしい関数ではある。

// 実装例
template< class T >
constexpr auto abs( T i ) -> std::enable_if_t< std::is_integral<T>{}(), T >
{ return i < T(0) ? -i : i; }

template< class M, class N = M >
using common_int_t = std::enable_if_t< std::is_integral<M>{}() and std::is_integral<N>{}()
, std::common_type_t<M,N>
>;

template< class M, class N >
constexpr common_int_t<M,N> gcd( M m, N n )
{ return n == 0 ? abs(m) : gcd(n, abs(m) % abs(n)); }

template< class M, class N >
constexpr common_int_t<M,N> lcm( M m, N n )
{ return m == 0 or n == 0 ? 0 : (abs(m) / gcd(m,n)) * abs(n); }

int main()
{
    gcd( 2, 3 ) ; // 1
    gcd( 10, 15 ) ; // 3

    lcm( 2, 3 ) ; // 6
    lcm( 8455, 99 ) ; // 837045
}

前回の論文からの変更はそれほど特筆するものはないようだ。

[PDFは平行世界に飛ばされるべき] N4063: On Parallel Invocations of Functions in Parallelism TS

Parallerism TSで、sortなどのユーザーの提供する関数オブジェクトを取るアルゴリズムを並列実行した時に、その関数を並列に呼び出せるのかどうかということについて、文面は何も規定していないということを、Hans Boehmが指摘した。この問題を議論するために、論文を書き、Rapperswil会議で議論した結果の改訂版。

BinaryPredicate, Compare, BinaryOperationは、実引数を変更してはならないという文面が付け加えられる。これにより、安全に並列に呼び出すことができる。

N4064: Improving Pair and Tuple (Revision 2)

tupleとpairで、以下のようなコードが動かないので、動くように変更する提案。

std::tuple<int, int> pixel_coordinates() 
{
  return {10, -15};  // Oops: Error
}

struct NonCopyable { NonCopyable(int); NonCopyable(const NonCopyable&) = delete; };

std::pair<NonCopyable, double> pmd{42, 3.14};  // Oops: Error

これは当然動いてもいいコードで、実際、このコードが動くようにtupleやpairを実装することが可能である。なぜ動かないのか。

実は、当時ライブラリを設計していた時の議論で、危険な暗黙の型変換を防止するために、必要以上に制約を加えてしまったのだ。

更に調査を進めると、tupleは、TR1の複数のテンプレート仮引数を使う設計から、標準規格のVariadic Templatesを使う実装に変更する際に、極めて重要な規程が抜け落ちてしまっていた。

template <class T1 = unspecified ,
          class T2 = unspecified ,
          ...,
          class TM = unspecified >
class tuple
{
public:
  tuple();
  explicit tuple(P1, P2, ..., PN); // iff N > 0
  […]
};

コメント部分の、"iff N > 0"という制約が、tupleのVariadic Template版の文面を作成する際に、抜け落ちてしまったのだ。

そして、論文では、危険すぎる暗黙の型変換は防ぎつつ初期化が行える、Pefect Initializationというテクニックを紹介している。

#include <type_traits>
#include <utility>

template<class T>
struct A {
  template<class U,
    typename std::enable_if<
      std::is_constructible<T, U>::value &&
      std::is_convertible<U, T>::value
    , bool>::type = false
  >
  A(U&& u) : t(std::forward<U>(u)) {}

 template<class U,
    typename std::enable_if<
      std::is_constructible<T, U>::value &&
      !std::is_convertible<U, T>::value
    , bool>::type = false
  >
  explicit A(U&& u) : t(std::forward<U>(u)) {}
  
  T t;
};

非explicitコンストラクターとexplicitコンストラクターを、SFINAE技法で制約を加えることにより、以下のように、危険な暗黙の型変換は防ぎつつ、自然に初期化ができるようになる。

struct Im{ Im(int){} };
struct Ex{ explicit Ex(int){} };

A<Im> ai1(1); // OK
A<Im> ai2{2}; // OK

A<Im> ai3 = 3;   // OK
A<Im> ai4 = {4}; // OK

A<Ex> ae1(1); // OK
A<Ex> ae2{2}; // OK

A<Ex> ae3 = 3;   // Error
A<Ex> ae4 = {4}; // Error

なぜ複数の引数を取るコンストラクターがexplicitではないとまずいのかということを、以下のような面白いコードで説明している。


#include <tuple>
#include <chrono>
#include <iostream>

using hms_t = std::tuple<std::chrono::hours, std::chrono::minutes, std::chrono::seconds>;

void launch_rocket_at(std::chrono::seconds s)
{
  std::cout << "launching rocket in " << s.count() << " seconds!\n";
}

void launch_rocket_at(hms_t times)
{
  using namespace std;
  launch_rocket_at(get<0>(times) + get<1>(times) + get<2>(times));
}

int main()
{
  using namespace std;
  launch_rocket_at(make_tuple(1, 2, 3)); // #1: ヤバイ
  launch_rocket_at({1, 2, 3});           // #2: もっとヤバイ
  using namespace std::chrono;
  launch_rocket_at(make_tuple(hours(1), minutes(2), seconds(3))); // #3: すっげーわかりやすい
  launch_rocket_at({hours(1), minutes(2), seconds(3)});           // #4: これもわかりやすい
  launch_rocket_at(hms_t{1, 2, 3});                               // #5: これでもいい
}

explicitではないと、暗黙の型変換が仕事をしすぎてしまうので、型による警告ができない。

N4065: make_array, revision 2

arrayを返すmake_arrayの提案

// N4065提案
// std::array< int, 4 >
auto a = std::make_array( 1, 2, 3, 4 ) ;

前回のN4031からの変更点として、型を明示的に指定する場合、make_arrayではなく、array_ofを使うようになった。

// N4031提案
auto a = std::::make_array<long>( 1L, 2L ) ;

// N4065提案
auto b = std::array_of<long>( 1L, 2L ) ;

文字列リテラルをcharのarrayにするto_arrayに変更はない。

// N4065提案
// std::array< char, 6 >
auto a = std::to_array("hello") ;

実装例がGitHubに上がっている。

Factory function of std::array

N4066: Delimited iterators (Rev. 3)

ostream_iteratorにはデリミターを指定できる。ただし、このデリミターは、実際にはサフィックスというべきである。

std::vector<int> v = { 1, 2, 3 } ;

std::copy( v.begin(), v.end(), std::ostream_iterator<int>( std::cout, ", " ) ;

このコードを実行した結果の出力は、"1, 2, 3, "となる。

この論文は、正しくデリミターとして働く、つまり要素の間にしかデリミターを出力しない、ostream_joinerというライブラリを提案している。上記コードでostream_iteratorの代わりに使うと、"1, 2, 3"となるクラスだ。

N4067: Experimental function etc.

標準ライブラリに対する拡張TSである、N4023: Working Draft, C++ Extensions for Library Fundamentalsでは現在、std::functionなど、標準規格の標準ライブラリを直接参照している。これはTSをサードパーティが実装するのに都合が悪い。そのため、function, promise, packaged_taskと同等のものを、std::experimental名前空間に存在させるための文面変更案。

N4068: Toward More Expressive Iterator Tags

Rapperswil会議で、N3976で提案されている多次元配列ビューを議論したところ、この論文で提案されているイテレーターの一部は、random access iteratorであると自称しながらも、実はforward iteratorの要件すら満たしていないことが指摘された。

これは、現在のイテレーターカテゴリーの要件が粒度が荒すぎるのが問題である。もっと細かい要件に分割して、要件を組み合わせて指定できるべきである。そのための方法としては、議論ではコンセプトが最適であるという方向で一致したが、とりあえずイテレータータグで対応する提案。

論文で提案されている細かい粒度のイテレータータグは以下の通り。

struct referene_tag { } ;

*iterがプロクシーではなく、実際にvalue_type&であり、その値はイテレーターによりキャッシュされたものではないこと。

struct lvalue_tag { } ;
struct rvalue_tag { } ;

*iterが変更可能なlvalue、もしくはrvalueであること(両方であることもあり得る)

struct equality_comparable_tag { } ;

iter1 == iter2がwell-formedであること

struct multipass_tag { } ;

iter1 == iter2であれば、++iter1 == ++iter2、かつ、&*iter1 == &*iter2であること

struct decrementable_tag { } ;

--iterがwell-formedであること。

struct random_move_tag { } ;

iterが任意の距離を移動でき、less-than comparableであること

さらに、これらのタグをまとめて指定できるbasic_iterator_tagを以下のように定義する。

template < typename ... Tags >
struct basic_iterator_tag { } ;

すると、既存のイテレータータグは、以下のように定義できる。

    typedef basic_iterator_tag<lvalue_tag> output_iterator_tag;

    typedef basic_iterator_tag<rvalue_tag, equality_comparable_tag>
              input_iterator_tag;

    typedef basic_iterator_tag<reference_tag,
                               lvalue_tag,
                               rvalue_tag,
                               equality_comparable_tag,
                               multipass_tag>
              forward_iterator_tag;

    typedef basic_iterator_tag<reference_tag,
                               lvalue_tag,
                               rvalue_tag,
                               equality_comparable_tag,
                               multipass_tag,
                               decrementable_tag>
              bidirectional_iterator_tag;

    typedef basic_iterator_tag<reference_tag,
                               lvalue_tag,
                               rvalue_tag,
                               equality_comparable_tag,
                               multipass_tag,
                               decrementable_tag,
                               random_move_tag>
              random_access_iterator_tag;

これを使えば、例えばvector<bool>のイテレータータグは以下のように定義できる。

    typedef basic_iterator_tag<lvalue_tag,
                               rvalue_tag,
                               equality_comparable_tag,
                               multipass_tag,
                               decrementable_tag,
                               random_move_tag>
              vector_bool_iterator_tag;

ドワンゴ広告

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

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

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

2014-08-21

ファイルシステムの破損?、付記using宣言とusingディレクティブの違い

ここ最近、筆者の使っているコンピューターは不可解な挙動に見舞われてきた。再現性なく機能停止に陥るのだ。

機能停止、というのはなんとも大雑把な言葉だが、そうとしか表現しようのない状態になる。

何の前触れもなく、急に画面が真っ暗、あるいは単色になり、そのまま止まってしまう。ターミナルに切り替えることもできず、SysRqすらきかない。

しかし、Ubuntuのバグ報告をみても、それらしき現象はない。こんなにも頻繁に遭遇しているのに何故だろうか。ハードウェアの故障であろうか。

ところで、今日、不思議な現象に出くわした。bashのglob上からみると存在するはずのファイルが、なぜか実際には存在していないという現象である。つまり、echo *とlsで表示されるファイルが異なる。globからしか見えないファイルがある。不思議だ。例に再起動してみたが、やはりglobでしか見えないファイルがある。

はて・・・、これはいったい。

fsckでもかけてみようか。GNU/Linuxで起動時にfsckをかけるには、ルートディレクトリにforcefsckというファイルを作成すればよい。

sudo touch /forcefsck
shutdown -r now

fsckは一瞬で終わった。再起動後、glob経由でしか見えないファイルはなくなっていた。

いったい何だったのだろうか。さて、再現性なく発生する機能停止はなくなるだろうか、しばらく様子を見よう。

ドワンゴ広告

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

今、N4064のPerfect Initializationを理解するのに手間取っている。

ドワンゴ社内の近所の席から、GCCでusing宣言が動かないという声を耳にして、そんなバカなとコードをみたところ、

using namespace std::vector ;

なるコードが書かれていた。これはusing directiveだ。

usingディレクティブ

using directiveとは、書かれたスコープに、指定された名前空間スコープ内の名前を持ち込むものである。

// usingディレクティブ
namespace NS { int x ; }

void f()
{
    using namespace NS ;
    x ; // well-formed, NS::x
}

当然、usingディレクティブで指定する名前は、名前空間名である。stdは名前空間名である。std::vectorは名前空間名ではない。

using宣言

using declarationとは、書かれたスコープに、指定された名前を持ち込むものである。

// using宣言
namespace NS
{
    int x ;
    int y ;
}

void f()
{
    using NS::x ;
    x ; // well-formed, NS::x
    y ; // ill-formed. 名前yは見つからない
}

当然、using宣言で指定する名前は、名前空間名であってはならない。

確かに、多くのプログラマは言語を表面的な理解だけで使っているし、それでいい。普通のプログラマーは規格の細部に関わるよりも、動くコードを書くことに注力するべきである。

実際、usingディレクティブとusing宣言は、どちらもusingキーワードを使うし、その意味も、スコープに名前を持ち込むという似通った性質を持つので、混同されやすいのだろう。

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

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

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

fork()は失敗するんだぜ、覚えときな

fork() can fail: this is important

あー、fork()のことね。プロセスがもっとプロセス作るためのやつな。いや、他にもプロセス作る方法はあるけどな。ま、面白い話がもうひとつあるから聞かせてやるよ。

forkは失敗するんだぜ。分かってるか? マジで分かってるか? マジだぜ。forkは失敗するもんだ。mallocと同じさ。失敗することもある。そんなに頻繁にってわけじゃないけどさ、でも失敗したら、無視できっこないぜ。ちっとは脳みそ働かせなきゃならん。

forkが0を返したら、そいつは子プロセスで、親なら正数を返すってことは、みんな知ってるよな。その値は子のpidだ。こいつを保存しといて、あとで使うってわけだ。

失敗を確認しない場合どうなるか知ってるか? そうだよ。お前多分、"-1"(forkのエラー通知)をpidとして扱ってるんだろ。

さて、問題の始まりだ。本当の問題は、シグナルを送るときにやってくるんだ。たとえば、お前、子プロセスを終了させたいとするだろ。

おまえkill(pid, signal)してるか? いや、kill(pid, 9)してるかな。

お前、pidが-1だったときどうなるか知ってるか? マジで知ってるべきだぜ。ヤバイからな。マジモンにヤバイぜ。

...

...

...

ほら、俺のLinux機からkill(2)のmanページをコピペしてやるよ。

もし、pidが-1と等しい場合、呼び出したプロセスがシグナルを送る権限を持つすべてのプロセスにsigが送られる。ただし、process 1(init)を除く

お分かりか? "pid -1"をkillするってのは、シグナル送れるすべてのプロセスを虐殺するってことだぜ。お前がrootなら、ほとんどすべてだ。お前は生き残るし、initも生き残る。だがそれだけだ。他は全部死ぬ。

お前、プロセスを管理するコード書いてるか? お前、テキストコンソールのgetty/login(initによって再起動される)とプロセスマネージャー以外ぜんぶ死んだ経験はないか? お前カーネルのoomkillerの責任だと思ったか?

そいつぁ濡れ衣かもしれないぜ。kill -1してないか今すぐ確認しろ。

Unix: この業界を飽きさせないために十分な数の落とし穴とトラバサミが仕掛けられている

ドワンゴ広告

この記事はドワンゴ出勤前に自宅で書いた。やはり作業はL字型の広い机で行うに限る。

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

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

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

2014-08-20

Sparc上のNetBSDでejectするとパニックに陷る問題の修正

CVS commit: src/sys/arch/sparc/dev

Modified Files:

src/sys/arch/sparc/dev: fd.c

Log Message:

fd(4)をオープンすることによるpanicを修正。違うポインターをmemset()に渡していたことが原因。

なんでこの18年もののバグがこれ以前に問題を引き起こさなかったのか謎だが(少なくとも、オレの昔の5.99.23カーネルでは動く)、おそらくはgcc 4.8がメモリ確保をよりアグレッシブに行うために表面化したのではなかろうか。

この問題はNobuyoshi Satoがfd(4)にeject(1)を試みた結果、発見された。

2014-08-19

Haskellの入門書を読んだ

先週、一週間丸々有給とリフレッシュ休暇を取って、都合9日間の休暇を作り出し、京都の吉田寮に遊びに行った。まだまだ読むべき論文がたまっているが、たまにはプログラミングもネットも一切忘れて息抜きをしてもよいだろう。吉田寮でだらだらと過ごすうちに、ふと、吉田寮に転がっていたHaskell入門書を読んでみた。

その結果、Haskellは、なかなか興味深い言語であることがわかった。

ここ数年、C++に提案されている新機能の多くに、Haskellにすでに存在する機能を強く連想させるものがある。なるほど、知り合いのC++プログラマーが、数年前にHaskellに手を出していたのは、そういう理由があったのか。

Haskellという言語に対する感想であるが、あまりに理想的すぎる印象を持った。なるほど、確かにコンパイラーの最適化が究極に賢ければ、Haskellのパフォーマンスは素晴らしい物になるだろう。しかし、現実にはコンパイラーがそこまで賢くなることは、近い将来には期待できない。

かつ、Haskellの入門書にかかれているサンプルコードは、極めて簡潔で理論的で言語的に美しいものであるが、その処理方法をよく考えてみれば、極めて効率が悪い処理である。

プログラミングの世界は、理論が数十年先行している。理論的には決着がついているものを、メモリやアドレスやキャッシュやパイプラインや同期といった制約のある現実のハードウェアに落としこむ必要がある。かつては、オブジェクト指向もそのような理論の一つだった。

Haskellの実用度は疑問ながら、その理論には痛く感心した私に、吉田寮にいる生物博士が言うのである。「次はCommon Lispの番だよ。遅かれ早かれC++はマクロを取り入れなければならないから、Common Lispはやらなければならない」と。C++風にまともなマクロを取り入れるのであれば、静的リフレクション機能として入るだろう。

完全にプログラミングから離れて遊ぶための休暇だったが、何の制約もない時間を確保できたことによって、かえって普段は手を出すことのない分野を学ぶことができた。やはり休暇は重要だ。

さて、Haskellの入門書を一冊読んだものの、まだ実際にコードを書いてはいない。やはりコードも書かねばなるまい。しかし、GNU/Linux環境でのx86-64アセンブリも書いてみたいし、C++の論文もたまっているし、C++の入門書も執筆してみたい。そういえば、勉強会のスライド資料作成もしなければならない。やることは多いが、とりあえず論文を片付けよう。

ドワンゴ広告

この記事はドワンゴの仕事をサボるために書いた。

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

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

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

2014-08-18

2014-07 post Rapperswil mailingのレビュー: N4052-N4059

>2014-07 post Rapperswil mailingをレビューしていく。次の論文集の発表までに終わらせたいものだ。

N4052: WG21 2014-06-06 Telecon Minutes

2014年6月6日に行われた電話会議の議事録。

N4053: WG21 2014-06 Rapperswil Minutes
[PDF?] N4054: PL22.16 2014-06 Rapperswil Minutes

2014年6月の16日から21日にかけて行われたRapperswil会議の議事録。二つの議事録の内容はほぼ同一のようだ。

N4055: Ruminations on (node-based) containers and noexcept

vectorを、ムーブコンストラクターがnoexceptな要素と、そうでない要素でパフォーマンスを比較したところ、極端な違いがある。これは、vectorは強い例外保証のために、内部のストレージのサイズを拡大するときに、要素の移動にstd::move_if_noexceptを使ってムーブを試み、noexceptではないムーブコンストラクターを持つ要素の場合は、コピーにフォールバックするからである。

これは、std::vector< std::list<int> > のようなネストされたコンテナーの場合、より極端になる。

なぜlistのようなノードベースのコンテナーのムーブはnoexceptではないのか。listはその設計上、要素の最後に番兵が必要だ。なぜならば、end()はデクリメントできなければならず、デクリメントできるだけの何かを指していなければならないからだ。

さて、この番兵用のノードをどのように確保するかという問題がある。動的に確保するのは普通の方法だ。しかし、どうせ一つしか必要なく、またlistクラスのオブジェクトの生存期間中、常に必要になるので、クラスのオブジェクトのデータメンバーに含めてしまうというのはどうか。これにより、番兵用のノードを動的確保する必要がなくなる。

しかしその場合、listをムーブやswapした時に、番兵用のノードまでムーブやswapできなくなってしまう。つまり、番兵をオブジェクトに含める自走では、listのend()が絡むイテレーターのペアが、ムーブやswap時に無効化されてしまう。listの素晴らしい点は、イテレーターが無効化しないという強い保証にあるというのに。

実は、当初から、listをswapすると、end()イテレーターが無効になるかもしれないと、規格上は定義されていた。そのため、swapした結果、end()イテレーターが無効になっても、規格準拠の標準ライブラリの実装である。問題は、既存の主要なSTLの実装に、swapでend()が無効化しない実装があり、利用者はその挙動に依存してしまっている。それに、先程もいったように、いくら原稿規格が許しているからといって、listでイテレーターが無効になるというのは、listの強いイテレーター保証に対して違和感がある。

結局これは、ムーブ後の標準ライブラリのオブジェクトの状態は、そのオブジェクトは規定されているように使い続けられるという保証があるためにおこる。vectorのストレージ拡大の場合は、そもそもムーブ後のオブジェクトは即座に破棄するので、そもそも必要ない。

論文では、いくつかの改善案を示しているが、どれも根本的に問題を解決するものではないように思われる。

[PDF注意] N4034の破壊的ムーブも、この問題を解決するために提案されたのであろう。

N4056: Minimal incomplete type support for standard containers

STLのコンテナーを再帰的なデータ構造で使えるようにする提案。

// N4056提案
struct X
{
    std::vector<X> vec ;
} ;

のようなコードが書けるようになる。具体的には、コンテナーの要素型として、不完全型を認める制限緩和の提案になる。クラスは定義終了である}をもって初めて定義されたとみなされるので、クラス定義の中では、クラス名は不完全型なのだ。

既存の主要なSTLの実装は、かなりのコンテナーで要素型に不完全型を認めている。不完全型でも問題がないかどうかは、実装次第なのだ。Clangのlibc++の実装は、他の実装ならば認めているコンテナーでも、不完全型を認めていないコンテナーがある。

この論文の初版では、すべてのコンテナーに不完全型を認めようという提案であったが、Rapperswil会議で、ひとまず手始めにvectorとlistとforward_listに対しては不完全型を認める制限緩和をしようというコンセンサスが得られたらしいので、そのような提案になっている。

libc++の実装は、この提案を受けて変更が入っている。

[PDFを伝播する必要はない] N4057: A Proposal to Add a Const-Propagating Wrapper to the Standard Library

constメンバー関数は、ポインター風データメンバーの非constメンバー関数を呼び出すことができてしまう。

struct A
{
    void bar() const { } // #1
    void bar() { } // #2
} ;

struct B
{
    B() : ptr( std::make_unique<A>() ) { }

    void foo() const { } // #3
    void foo() { } // #4

    std::unique_ptr<A> ptr ;
} ;

int main()
{
    B b ;
    b.foo() ; // #4, #2

    B const cb ;
    cb.foo() ; // #3, #2
}

つまり、メンバー関数のconst性は、ポインターやポインター風のデータメンバーに伝播しない。

これは、C++の文法上、当然のことなのだが、論理的にはひっかかるところもある。そのために、論文では、const性を伝播させるライブラリ、propagate_constを提案している。


struct B
{
// ...

    std::propagate_const< std::unique_ptr<A> > ptr ;
} ;

このようにpropagate_constライブラリを使うことで、メンバー関数のconst性を正しく伝播させることができる。

[PDFを使うのは賢くない] N4058: Atomic Smart Pointers

アトミックに操作できる、atomic<shared_ptr<T>>, atomic<weak_ptr<T>>, atomic<unique_ptr<T>>の提案。

これらのスマートポインターに対して、特別にアトミックな特殊化の実装が使われる。

C++では、もはや生のポインターを扱うのはバカのやることである。unique_ptrは生のポインターを扱うのと同等のパフォーマンスと安全性を提供してくれるし、unique_ptrが対応できないような状況にも、shared_ptrは対応できる。しかし、現在のC++で、依然として生のポインターを扱わなければならない分野が存在する。アトミック操作を使ってロックフリーな処理を実装する場合である。このため、atomicに既存のスマートポインター用の特殊化を追加して、アトミックに操作できるようにする提案。

すでに、shared_ptrにはアトミックに操作する関数があるが、クラス外の関数で非常に使いづらい。

なかなかいい提案ではないかと思う。

N4059: Spring 2015 C++ Standards Committee Meeting

2015年4月の4日か9日にかけてに開かれるC++国際会議の案内パンフレット。

Perceptive Softwareがホスト、場所はミズーリ州カンザスシティ。

ドワンゴ広告

先週、土日と有給と、ドワンゴ独自のリフレッシュ休暇を組み合わせて9連休を作り出して、京都に遊びに行った。

休暇中に、なんとなしに吉田寮に転がっていたHaskell入門書を読んでみると、C++にここ数年提案されている新機能は、かなりHaskellの影響を受けているようであった。そろそろ理論を現実に落としこむ時期に来ているのだろう。

ちなみに、あるいかにも研究者らしい生物博士と雑談したところ、次はCommon Lispの番だという。いずれはLispのマクロを導入しなければならず、当然我々はLispをやるべきなのだという。

Cプリプロセッサーのような単なるトークン列によらない、文法やスコープを尊重するマクロは、入るとするならば、おそらく静的リフレクション機能の一つとして入るだろう。コード情報をコンパイル時に扱うことができ、またコンパイル時にコード生成を行えるような機能だ。

完全に遊ぶつもりの休暇であったが、色々と面白いことがあった。その次第は近日中に書く。

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

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

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

2014-08-08

2014-05-pre-Rapperswil-mailingのレビュー: N4040-N4051

[PDFとはお先真っ暗] N4040: Working Draft, C++ Extensions for Concepts

軽量コンセプトのドラフト文面案

Concerns with changing existing types in Technical Specifications

Issaquah会議で、TSはstd名前空間のライブラリを変更してよいと決定された。つまり、std::experimentalなどの実験的な別の名前空間の中にそっくりライブラリをコピーして変更し、またTSの文面もすべてをコピーした上で変更ではなく、TS文面は差分だけで良くなったし、std名前空間を直接書き換えてよくなった。

これは、色々と懸念事項もある。

ある実装がTSの実験的実装をしたとして、コンパイラーオプションなどで有効にできるようにしていたとする。TS機能を有効にした場合、ライブラリがABI互換性を静かに壊してしまう可能性がある。

また、std名前空間は標準ライブラリの実装にしか使う権利がないので、TSに完全準拠したサードパーティ実装ができなくなる。

TSの機能は、実際に実装されて、現実の利用者による利用経験が積まれなければ、規格に正式に取り入れるかどうかの判断ができない。

論文では、この懸念に対する明確な結論を出していない。

[PDFからまともなフォーマットへの安全な変換が望まれる] N4042: Safe conversions in unique_ptr<T[]>

現在、提案されているunique_ptr<T[]>は、派生クラスへのポインターから基本クラスへのポインターの変換は、危険なので禁止している。

struct base { } ;
struct derived { } ;

int main()
{
    // ill-formed
    std::unique_ptr<base[]> p( new derived[10] ) ;
}

しかし、この制限は強すぎて、以下のような安全な型変換も禁止してしまう。


unique_ptr< Foo const [] > ptr1( new Foo[10] ) ; // ill-formed
unique_ptr< Foo [] > ptr2( new Foo[10] ) ;
unique_ptr< Foo const [] > ptr3 = move( ptr2 ) ; // ill-formed
unique_ptr< Foo const [] > ptr4 ;
ptr4.reset( new Foo[10] ) ; // ill-formed

これは明らかに安全であるので、これは認めようという提案。

このような細かい条件でill-formedにするかどうかを切り替える方法には、static_assertを使う方法と、SFINAEを使う方法がある。static_assertはエラーメッセージも読みやすくはなるのだが、ハードエラーになってしまうという問題がある。そのため、論文では、規格の他の場面でもよく使われている、SFINAEによる方法を提案している。

この制限緩和は然るべきであると思う。

N4043: Dynarray Allocation Context

dynarrayで問題になったように、明示的なデストラクターの呼び出しとplacement newは問題になる。この論文はその問題に対処するために、デストラクター呼び出しとplacement newで再構築されない自動ストレージに関しては、実装依存の最適化を許す文面を追加することで解決しようとしている。

そういうここからここまでは実装依存と定義するだけの簡単な問題ではないと思うのだが。

N4044: A Three-Class IP Address Proposal, Revision 1

IPアドレスを表現するライブラリの提案

前回の論文からの変更点で、特にある人物の興味を引きそうな変更:「多くの関数がnoeceptと指定された」

[またPDFだ] N4045: Library Foundations for Asynchronous Operations, Revision 2

コールバックとfutureの両モデルをサポートする設計の紹介論文。

[PDFで書かれた論文の価値は低い] N4046: Executors and Asynchronous Operations

タスク単位を自動的に並列実行してくれる並列実行ライブラリの論文。

[暗い未来を予感させるPDFフォーマット] N4047: A Module System for C++

#includeの代替機能、モジュールの提案。

C++にはライブラリをコンポーネント化する近代的な機能が欠けている。C++にあるのは、Cから受け継いた#includeである。#includeとは単なるテキストデータをその位置に挿入するものである。コンパイラーからしてみればコピペだ。

#includeはC++の長いビルド時間の原因にもなっている。#includeされるソースコードの意味は、すでに定義されているマクロによって変わるし、#includeの後に続くソースコードの意味も、#includeされるソースコードで定義されているマクロによって変わる。

C++には、もっと近代的なライブラリの分離のための機能が必要だ。その機能はモジュールと名付けられ、設計が進んでいる。

この論文の提案は、既存のプリプロセッサー自体には何も変更を加えない。過去に、プリプロセッサーに付け焼刃的な変更を加えて害悪を軽減しようという提案はいくつもあったが、どれも失敗に終わっている。ぷりプロセッサーには長い歴史があり、短期的に廃止できるものではない。したがって、新しい機能は、プリプロセッサーと共存しなければならない。

提案では、新しいキーワードとして、moduleとimportのふたつを追加する。

モジュールを使うソースコードは、モジュールをimportする。

// N4047提案
import std.vector ;

int main()
{
    std::vector<int> v ;
}

モジュールは、最初の宣言でモジュール名を宣言する

// N4047提案
module std.vector ;

export
{
// 外部に見せる名前の宣言
}

export { }で囲まない名前は、外部に公開されない。ローカルな定義になり、他の翻訳単位でその名前を使った定義があったとしても、ODR違反にはならない。

// 1.cpp
module one ;

export { void f() { } }

void g() { }
// 2.cpp
module two ;

export { void g() { } }

void f() { }
// main.cpp
import one ;
import two ;

int main()
{
    f() ; // oneのf
    g() ; // twoのg
}

この三つの翻訳単位からなるプログラムをコンパイルしてもODR違反とはならない。モジュールでは、export { ... } で囲んだ名前のみが外部に公開され、それ以外の名前は外部に公開されず、ODR違反にもならないようになるからだ。

モジュールは相互の参照もヘンテコな前方宣言なしに書くことができる。

// A.cpp
module A ;

export {

import B ;

class A
{
    B * ptr ;
} ;
}
// B.cpp
module B ;

export {
import A ;

class B
{
    A * ptr ;
} ;

}

早くモジュールが実用化されて欲しい。

[PDFも改良してほしい] N4048: More Improvements to std::future<T> - Revision 1

N3865の改訂版。optionalのより汎用なライブラリ、expectedに対応した変更があるようだ。

[無PDF原則も提案したいところ] N4049: 0-overhead-principle violations in exception handling

例外において、ゼロオーバーヘッドの原則が尊重されていない例の提示。

例外にはオーバーヘッドがある。標準ライブラリは例外に強く依存しているので、例外を使ったコードを吐く。また、生成されるコードも、例外に対応するためのバッファーなどを持っている。これらはたとえ例外を使わなかったとしてもメモリ使用量を増加させる。そして、既存のリンク時コード除去やLTOは、これらのコードやバッファーを取り除くほど賢くない。

GCCには例外を使わないオプション-fno-exceptionsがある。しかし、これはフロントエンドで例外を使わないという指定だけであって、ライブラリは依然として例外を使うし、バックエンドが吐くコードも、例外のコードを吐くし、例外をサポートするためのコードや、例外が緊急時に使うバッファーなども確保する。これらはメモリ使用料を増加させる。

これらのオーバーヘッドを取り除くにはツールチェインに手を入れて、本当に例外を使わないようにするしかない。

論文は、現時点でのGCCの実装を示して、問題提起をするだけで、何も結論を出していない。

N4050: Dynarray Semi-Editorial Issues

dynarrayに対する文面上の些細な修正。とはいっても、単なる文法ミスや誤字脱字よりはすこし真面目な問題を修正している。

N4051: Allow typename in a template template parameter

テンプレートテンプレートパラメーターにtypenameキーワードを利用できるようにする提案。

今までは、

// 現状
template < 
    template < typename T >
    class U // classキーワードが必要
    >
class C ;

このように、テンプレートテンプレートパラメーターは、文法上の制約でclassキーワードを使わなければならなかったが、この提案では、typenameキーワードも使えるようになる。

// N4051提案
template < 
    template < typename T >
    typename U // typenameキーワードが使える
    >
class C ;

些細な変更だが、プログラマーの無用の混乱を防ぐために、入って欲しい変更だ。

ちなみに、Clangが-std=c++1zでN4051提案の実験的実装をしている。

Clang - C++1z, C++14, C++11 and C++98 Status

これで、2014年5月分の論文集が終わった。次は7月分だ。

ドワンゴ広告

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

来週は遠慮無く有給休暇を取ったので丸々休みだ。久しぶりに京都に帰省する。

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

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

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

2014-08-07

2014-05-pre-Rapperswil-mailingのレビュー: N4030-N4039

さて、5月分の論文集も残すところ後わずか。

N4030: Feature-testing recommendations for C++

C++の新機能が実装によってサポートされているかどうかを確かめる標準のCプリプロセッサーマクロの提案。具体的なマクロ名などは論文を参照。

// N4030提案
#if __cpp_binary_literals
int const packed_zero_to_three = 0b00011011;
#else
int const packed_zero_to_three = 0x1B;
#endif

__cpp_binary_literalsは、実装が二進数リテラルに対応しているかどうかを調べるマクロである。

筆者はCプリプロセッサーを使ういかなる機能にも反対の立場である。

N4031: make_array, revision 1

make_tupleやmake_pairのようなインターフェースで、std::arrayを返すmake_arrayの提案。

これにより、arrayのテンプレート実引数を推定でき、わざわざ手書きする必要がなくなる。

// 面倒
std::array< int, 5 > a = { 1, 2, 3, 4, 5 } 

// 簡単
auto a = std::make_array( 1, 2, 3, 4, 5 ) ;

// 型を明示することもできる
auto b = std::make_array<int>( 1 ) ; 

// array<char const *, 1>
auto c = std::make_array("hello") ;

// array<char, 6> d = { 'h', "e", 'l', 'l', 'o', '\n' } ;
auto d = std::to_array("hello") ;

narrowing conversionは禁止されている。

// エラー、narrwoing conversion
auto a = std::make_array(2, 3U)

なかなか悪くない小粒なライブラリだ。

N4032: Comments on continuations and executors

Anthony Williams本人が自ら、現在提案中の並列処理ライブラリを実装したところ、文面に様々な不備が見つかったとのことで、その不備を列挙して、然るべき修正案などを書いている論文。

executorがabstractクラスなのは不便だ。executorコンセプトを用意して、実行時ポリモーフィックな振る舞いには、別途generic_executor_refのようなtype erasureを使ったラッパーを容易すべきだ。

Scheduled Executorがタイムアウト指定にstd::chrono::system_clockを使うのはいかにもおかしい。std::chrono::steady_cloxkを使うべきだ。そもそも、タイムアウト指定にタイムスタンプを使うというのは、利用例の一部しかカバーできないので、やはり標準のabstract基本クラスと仮想関数を用意して様々な利用例に対応できるようにすべきである。

executorの種類が少なすぎる。executor *で返されるより、generic_executor_refで返すべきだ。その記述も不十分だ。

そのほか、futureに対する拡張案へのコメントが並んでいる。設計の不備、文面の不備を指摘するものが多い。

N4033: synchronized_value<T> for associating a mutex with a value

これもあの有名なAnthony Williamsの論文。T型を自動的にmutexでロックするライブラリ、synchronized_value<T>の提案

値に排他的なロックをかけるにはmutexを使うが、mutexをそのまま使うのは、甚だ面倒である。

int value1 ;
std::mutex m1 ; // value1用のmutex
int value2 ;
std::mutex m2 ; // value2用のmutex

void f()
{
    std::lock_guard<std::mutex> guard(m2) ; // おっと
    value1 = 123 ; // おおっと
} 

上記のコードは、ロックすべきmutexオブジェクトを間違えているのだが、残念ながら、コンパイラーはこれを警告してくれない。

この問題は、ある型のオブジェクトとそのオブジェクトを守るmutexを管理するライブラリがあればいいのだ。そこで、そのようなライブラリを論文著者はすでにDr. Dobb'sの記事として執筆していたが、その設計を手直しして正式に提案している。


std::synchronized_value value ;

void f()
{
    *valule = 123 ;
    int x = *value ;
}

仕組みとしては、synchronized_valueは、T型とmutexをメンバーとして持っている。operator *は、未規定の型を返す。この型は生成時に元のsynchronized_valueのオブジェクトのmutexをロックし、破棄されるときにmutexをアンロックする。また、T型へのリファレンスを返す。その結果、安全に自動的にロック、アンロックした上に、値の読み書きもできる。

これは、値の単発の読み書きをするにはいいが、複数の操作を一括して行うには効率が悪い。そのため、オブジェクトの生存期間の間だけロックし続ける、公に公開されているインターフェースもある。

std::syncronized_value<std::string> value ;

void f()
{
    std::update_guard< std::string > guard(value) ;

    *guard += "hello" ;
    *guard += "hello" ;

// guardのデストラクターでアンロックされる
}

N4034: Destructive Move

より制約が厳しい、保証された破壊的ムーブをするためのライブラリの提案。

現在のムーブというのは、ムーブ後のオブジェクトを、未規定だが有効な状態にするように義務付けている。つまり、ムーブ後のオブジェクトの状態は定められていないが、通常使えるように使えなければならないということだ。

しかし、クラスの実装によっては、これを無例外保証のまま行うのは難しい。そこで、破壊的ムーブの提案となる。

破壊的ムーブ(destructive move)とは、ムーブ後のオブジェクトは破棄された後であり、利用も、もう一度破棄することも出気ない状態になる。こ無例外ムーブが提供できないクラスでも、無例外破壊的ムーブは提供できるクラスは多い。また、ムーブされた後のオブジェクトはどうせ破棄する場合もある(vectorがその内部バッファーを増やす場合など)

また、破壊的ムーブというのは、単にバイト列のコピーで済ませられる場合もある。そのような条件を満たす型を、trivially destructive movable typeと名付ける。

具体的な提案としては、

template <class T>
void destructive_move(T* to, T* from) noexcept( /* see below */ );

という破壊的ムーブのための関数テンプレートの追加と、is_trivially_destructive_movable<T>という、trivially destructive movableな型かどうかを調べられるtraitsを追加する。destructive_moveは、is_trivially_destructive_movableがtrueの場合、バイト列コピーを行う。また、destructive_moveが特定の型に対してオーバーロードされていた場合は、ADLによって発見され、そちらが呼ばれるようになっている。

また、この配列版、destructive_move_arrayもある。

template <class T>
void destructive_move_array(T* to, T* from, size_t sz)
noexcept(is_nothrow_destructive_movable<T>::value);

リファレンス実装はhttp://halpernwightsoftware.com/WG21/destructive_move.tgzにある。これはBloomberg LPのBDEライブラリで5年以上使われていて、vector操作を劇的に高速化させたとのことである。

[論文フォーマットは暗黙にPDF以外のまともなフォーマットが使われるべき] N4035: Implicit Evaluation of "auto" Variables and Arguments

N3748の改訂版。

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

matrix A, B ;

auto C = A * B ;

上記のコードで、変数Cの型がなんであるかはわからない。matrix型かもしれないし、違うかもしれない。たとえば、matrixクラスの作者は、最適化のために、計算が実際に必要になるまで遅延させるため、内部的なラッパー型や、Expression Templateの技法を使った複雑な型を返しているかもしれない。

しかし、ここでmatrix型が欲しい場合、いったいどうすればいいのだろうか。

そのため、この論文では、autoを初期化するときに暗黙に型変換される型を、ライブラリ側で指定できる機能と、ユーザー側でその指定された暗黙の型変換を無効にする機能を追加しようと提案している。そのためには、できるだけわかりやすい文法を考えなければならない。

たとえば、matrixクラスは、operator *(Matrix const &, Matrix const &)の結果として、product_exprというラッパークラスを返しているとする。

論文では、いくつかの文法案が提案されている。

using宣言

class product_expr
{
public :
    using auto = matrix ;
} ;

このように記述すれば、auto specifierでは、暗黙にproduct_exprからmatrixに型変換される。型変換は通常通り、product_exprを引数として受け取るmatrixのコンストラクターとか、product_exprからmatrixへの変換関数として実装すればよい。

operator記法


class product_expr
{
public :
    matrix operator auto() { ... }
} ;

この文法の利点は、通常のコンストラクターや変換関数とは独立して、auto specifierのためだけの型変換処理が書けることである。ただし、そういう需要はあまりないのではないかとも思うし、戻り値の型を推定させた場合、何やらわかりにくい。

auto operator auto() { ... }

他にも、decayを特殊化してそれを特別に扱おうという提案もあるが、これは筆者は気に入らないので、わざわざここで紹介しない。

ユーザー側でこの余計なお世話を無効化する方法としては、explicitキーワードを使う文法が提案されている。

explicit auto C = A * B ;

David Vandevordeは、ライブラリで無効化することも可能であるという例を提示した。

auto C = noeval(A * B) ;

noevalの実装は以下の通り


template <typename T>
struct noeval type
{
    const T& ref;
    noeval type(const T& ref) : ref(ref) {}
    operator T const&() { return ref; }
    using auto= T const&;
} ;

template <typename T>
auto noeval(const T& ref)
{
    return noeval type(ref);
}

ライブラリの無効化のために、まさに提案されているこの機能をもう一度使うとはにくいコードだ。さすがはDavid Vandevorde。

[論文からPDFフォーマットの廃止に向けて] N4036: Towards Implementation and Use of memory_order_consume

2014年2月に、Linus Torvaldsが、GCCのML上で暴れていた。この論文著者とも殺伐とやりあっていた。

gcc archive, author index for February, 2014

Linusの主張はこうだ。

C11/C++11に入ったアトミック操作は使い物にならない。規格がクソすぎるためである。よってLinuxカーネルでは使わない。

規格化されたアトミック操作は保証が弱すぎる。したがって、Linuxカーネルのアトミック操作のすべてを置き換えることはできない。また、Speculative storeのような問題ある最適化を抑制することもできない。そもそもspeculative storeなんて許されるべきではないだろ。コンパイラー屋は、規格を指さして、「でもほら、規格上許されちゃってるんだよねーん」とかほざいているが、規格がぶっ壊れてる。ぶっ壊れたコード生成を正当化するためにクソぶっ壊れた規格を使っている。現実のハードウェアに基づかない規格などクソだ。

結局、問題に対処するために、Linuxカーネルではvolatileを使っている。アトミック操作ではすべての問題ある最適化を抑制できない。すると、volatileかつアトミック型を使わければならない。じゃあ、最初っからvolatileだけでいいじゃねーか。なんでわざわざアトミック型まで使わなきゃなんねーんだよ。

アトミック操作はまだひとつかふたつのプラットフォーム向けにしか実装されていないし、まだ実装経験が浅すぎてどうせバグだらけだ。結局、Linuxカーネルの今のやり方(volatile+インラインアセンブリ)は維持しなければならない。その上で、ひとつかふたつのプラットフォーム向けに標準規格のアトミック操作もつかってみよーかなーなんてなるわけねーだろドアホ。なんでそんなに複雑にしなきゃならねーんだよボケ。

そもそもmemory_order_consumeってクソすぎるだろ。なんだよこのcarries dependencyって概念はよ。コンパイラーが依存順序を完璧に解析できるわけねーだろ。で、明示的にkill_dependency()で依存を断ち切れって? ふざけんじゃねぇぞ。

そもそも、グローバルな最適化なんて糞食らえだ。ソースコードの解析だけですべてが分かると思うな。ソースコードに記述されていない、ハードウェアやモジュールなどの外部の別言語で書かれたライブラリがメモリを操作することだってあるだろうが。ローカルな最適化だけにしておけ。

標準規格はクソ使えないので我々Linuxカーネルではゼッテー使わねー。馬鹿げた机上の空論を捨てて、規格をさっさと直しやがれ。

論文著者は、規格化されたアトミック操作が現実の需要と乖離している理由を、コンパイラーの最適化手法が、7年前の規格化していた時と比べて、格段にアグレッシブになっていること、当時より長い依存チェインが現実に使われていることなどを上げている。

論文では、Linus Torvaldsでも満足する機能を提案しようと、型ベースの制限付き依存チェインとして、value_dep_preserving型指定子を提案している。

この論文を読むのはつかれた。アカデミックバリバリの2カラムのクソレイアウトを利用したクソみたいなtexで書かれたものをPDFで出力した論文で、しかも元のtexのソースコードは公開しないときているので、最高に読みづらかった。仕方がないので紙に印刷して読んだ。そして、背景事情を調べるためにLinusのGCC MLでの発言を追う必要があったので、これまた時間がかかった。

[非PDFドキュメントが欲しい] N4037: Non-Transactional Implementation of Atomic Tree Move

トランザクショナルメモリーを使わずに、lock contentioもできるだけおこさず、要素をあるツリーから別のツリーにムーブするアルゴリズムの考察。2014年のIssaquah会議でそういう課題が出たので考察したらしい。

論文では、バランスも何もないバイナリツリーのみを考察している。

論文自体は、あまりC++らしくない。ただ、C++会議でそのような条件を満たすアルゴリズムが現在存在しないので今後の課題として設定されたので、その道の専門家であるPaul McKenneyが考察したようだ。

N4038: Proposal for Unbounded-Precision Integer Types

C++の標準ライブラリに無限精度整数型の提案。

プログラミングにおいて、C++の基本型の整数型が表現しきれないほど大きな数値を扱う需要はよくある。特に、暗号用途には必須だ。C++用のそのようなライブラリは、多数ある。Javaはそのようなライブラリを標準で持っている。C++にも無限精度整数型が必要だ。

無限精度整数型(Unbounded-Precision Integer Type)とは言うものの、現実的には、有限のコンピューターリソース上に実装される以上、当然、有限の精度を持つ。ここでいう無限精度とは、ハードコードされた上限がなく、システムのリソース以外に桁数に制限を加えるものがないことを言う。

提案では、無限精度整数型であるstd::integer型と、無限精度ビット列型であるstd::bitsが提案されている。

std::integer型は、既存の組み込みの整数型と組み合わせて使うことができ、また組み込みの整数型がサポートしている演算はすべて同じ文法でサポートする。また、その他にも無限精度整数型を必要とする分野で需要ある演算をサポートする。需要あるすべての演算を網羅することはできないので、必要な演算を実装できる基本的な演算を提供する。abs, sqr, sqrt, pow, mod, mulmod powmod, gcd, lcmあたりだ。is_zero, is_oddがあるのも興味深い

また、integer型のメンバー関数のsizeとcapacityは、内部ストレージを再確保せずに表現できる10進桁数の上限を返す。reserveも桁数を取る。

現在表現している値を表現できる規模に内部ストレージを縮小するshrink_to_fitもある。

get_data_proxyというメンバー関数もあり、これは内部ストレージを直接見たり操作したりできる。

ちなみに、ドワンゴ社内では、競技プログラミングに使えるとの意見が多かった。

N4039: Default executor

現在提案中の並列実行ライブラリには、デフォルトのexecutorがない。デフォルトのexecutorはあるべきである。しかし、何をデフォルトにすればいいのか議論は付きない。タスクごとにスレッドを作るexecutorはわかりやすいが、一般的に、スレッドプールよりコストがかかる、しかし、スレッドプールでは、タスクがブロックされてしまうことがある。

論文は結論を出さず、それぞれのexecutorの利点、欠点を挙げるだけにとどまっている。

ドワンゴ広告

最近、ドワンゴのオフィスが入っている歌舞伎座タワーの7FのコンビニがICE BOXを置くようになったので、食べ過ぎないように鋼鉄の意思を要求されてつらい。

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

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

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

妖怪ハウスのイベント予定

今月の妖怪ハウスで筆者が予定しているイベント一覧

8月7日夜、ドライフルーツヨーグルト試食会、焼き肉

8月8日夜、Monty Python鑑賞会、多分焼き肉

8月23日、ボードゲーム会、スイーツ(笑)会

2014-08-05

何でWindowsは俺のワイヤレスキーボードをトースターだと認識するんだ?

device manager - Why does Windows think that my wireless keyboard is a toaster? - Super User

俺は彼女の父親から古いPCを相続したんだが、プリンターを設定していると、ちょっとおどろきのものが現れた。

画像

さて、二つの疑問がある。

  1. なんでWindowsは俺のワイヤレスキーボードをトースターだと認識するんだ?
  2. なんでWindowsはデバイスメニューにトースターのアイコンを持っているんだ?

なぜか、質問者のワイヤレスキーボードに、トースターのアイコンが表示されている。Windowsはなぜそんなアイコンを用意しているのだろうか。

答えは。

理由1

Microsoftはトースター用ドライバーというサンプルを作っている。このサンプルでは、<DeviceIconFile>Toaster.ico</DeviceIconFile>という行があり、おそらく、お前のキーボードの製造業者は、このサンプルをそのまま使ったのだろう。

理由2

キーボードの裏に、食パンを差し込む口は見当たらないか?

どうやら、Microsoftがサンプル用に作ったトースター用のドライバーのデバイス情報のXMLファイルをそのまま使ったずぼらなキーボードメーカーがいるらしい。サンプルのアイコンがトースターなのは、Microsoftのユーモア精神なのか、そのままコピペして使うずぼらなハードウェア製造業者を防ぐためなのか。それでもコピペはされる。

それにしても、USBトースターはあまり実用になりそうにない。一般的なトースターの消費電力は1000Wはある。一方、USBは3.0でもせいぜい100W程度の電力しか供給できない。USB3.0ポートを制御用に1ポート、電力供給用に9ポート必要とするトースターだろうか。電源ユニットも相当によい物を使う必要がありそうだ。

2014-08-04

ループカウンタを64bitにしたり、 バッファのサイズを定数にしたらパフォーマンス激落ちなんだけど何で?

c++ - Replacing 32bit loop count variable with 64bit introduces crazy performance deviations - Stack Overflow

stackoverflowで、興味深い質問が行われている。

簡単にまとめるとこうだ。std::uint64_t型の配列の各要素にx86-64のpopcnt(1になっているビット数を数える命令)を適用したい。

コードの肝心の部分を書くと、以下のようになる。

for (unsigned i=0;i<size/8;i+=4) {
    count+=_mm_popcnt_u64(buffer[i]);
    count+=_mm_popcnt_u64(buffer[i+1]);
    count+=_mm_popcnt_u64(buffer[i+2]);
    count+=_mm_popcnt_u64(buffer[i+3]);
}

ループの中で、バッファーの要素を4個づつ、Compiler Intrinsicに渡して、popcntを実行している。

さて、ここで奇妙な事実がある。Sandy/Ivy BridgeやHaswellで、ループカウンター用の変数であるiの型をunsigned int型とstd::uint64_t型とでベンチマークを取ってみると、質問者の環境で、unsigned int型の処理速度は26GB/sだが、std::uint64_t型では、13GB/sと、速度が激減するのだ。

これはGCCのバグなのだろうか。しかし、Clangで実験してみても、やはりunsigned intが26GB/sに対し、uint64_t型は15GB/sと、明らかに処理速度が違う。

更に良くわからないことがある。バッファーのサイズを保持するsize変数であるが、バッファーサイズは実行時の入力により決定されるため、定数ではない。しかし、バッファーサイズを決め打ちにしてsizeを定数にしてみると、なんと性能はunsignt intもuint64_tも共に20GB/sになる。Clangでは、どちらも15GB/sになる。

size変数をstaticにしてみると、uint64_t型でも、20GB/sにまで速度が改善する。

いったい何なのだ。

最も投票数の多い回答が興味深い。

この不思議なパフォーマンスの変化の理由は、IntelのSnady/Ivy Bridge, Haswellのpopcnt命令の、不必要なデータ依存によるものだという。

popcnt src, dest

とあったとき、なぜかIntelのCPUは、結果を出力する先のレジスターにデータ依存が発生するのだという。そのため、destに指定されたレジスターの値が定まるまで、後続のpopcntを並列実行できず、スループットが落ちるのだという。しかし、destはどうせ上書きしてしまい、直前の値は何の意味も持たないので、データ依存もクソもないはずなのだが。

現時点で、GCCはこのIntelのCPUの奇妙な挙動をまだ把握していない。コンパイラーはCPUごとのこのような特性を把握して、事前にxor reg, regなどしてデータ依存をそぎ落としておくべきなのだという。

AMDのCPUにはこのような不必要なデータ依存はないそうだ。

なぜループカウンター用の変数の型を64bitにしたり、バッファーサイズを保持する変数を定数にしたりするだけで結果が劇的に変わるかというと、コンパイラーのレジスタ割り当てが変わるためだという。

一体、何故こんな不必要なデータ依存が発生するのかという疑問であるが、回答者は、IntelのCPUはオペランドを二つ取る似通った命令を共通の方法で処理していて、popcntと何らかの命令を同じ枠組みで処理しているためではないかとしている。このようにすることで、プロセッサーの設計を簡単にできるのだとか。

ドワンゴ広告

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

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

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

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

2014-08-03

momonga.vim #6 in ドワンゴ

2014年8月2日の土曜日、momonga.vim #6が、ドワンゴのセミナールームで開催された。

momonga.vim #6 in ドワンゴ(あきらかに) - connpass

momonga.vimとは、もくもく会だ。もくもく会とは、一人では集中して作業できない人間が、皆で集まるという強力な理由付けの元に、何らかの作業をするという会だ。もくもくはいったいどこから始まったのかという疑問であるが、おそらくphaさんが始めたのではないかと思う。

今回、ドワンゴのセミナールームでVimのもくもく会が開催された理由というのは、momonga.vimの主催、@supermomongaさんが、勉強会に使える会場を探していたからだ。何でも、最初は自宅でやっていたらしいが、指数関数的に参加に人数が増えていき、このままでは年末には武道館が必要になる増加率であるという。ドワンゴのセミナールームは煩悩の数と同じ108人入り、マイク、プロジェクター、WiFi、椅子と机、ホワイトボードなどの、勉強会に必要な設備はひと通り揃っているので、勉強会に最適である。

さて、人数自体は108人入るのだが、あまりに大勢を入れ過ぎると、もくもく会の趣旨に反するとのことで、募集人数は40人に設定された。

さて、当日の筆者は、場所を提供するスタッフとして12時半頃に会場に行った。

懸念事項としては、当日にももんがさんが来るかどうかという究極の問題がある。ももんがさんというのは、極めて億劫な人であり、家から出るのが億劫で来ないということは十分に考えられるからだ。とはいっても、さすがに今回は自分で主催した勉強会であるので、十分に家から出てくる理由がある。

幸い、そういうことはなく、本人は早めに会場に到着した。しかし、8月1日の21時に起床したとのことで、若干の違和感は残った。

さて、今回の勉強会には、あの暗黒美夢王(Dark Vim Master)が来た。暗黒美夢王には、開始と締めくくりに、かの有名なエディ歌を披露していただいた。エディ歌とは、編集王(エディットキング) バトルエディターズに使われている歌である。

さて、もくもく会は、もくもく会であるから、特に特筆すべきこともなく各人が黙々と作業していた。

さて、最後に進捗のある人の発表が行われた。なにしろ、Vimに関連する作業である。Vimとはテキストエディターである。テキストエディターである以上、人目を引く見た目にわかりやすい何か動きのある発表などあろうはずもない。

ところが、なんとVimうさぎさん(C++力ないよー) (rbtnn)が、Vim上でマリオのようなものを実装しかけていた。

さて、もくもく会のあとは、妖怪ハウスで懇親会を行った。懇親会では、もう何度目かわからないピザを焼いた。ピザはだいぶ慣れてしまった。今後は別の具に挑戦したいところだ。

さて、日曜日はのんびりするとしよう。

ドワンゴ広告

この記事は日曜日ののんびりした気分で書かれた。

ところで、ドワンゴで筆者が企画して開く勉強会は、私は出勤扱いになる。そのためこの勉強会で筆者、なんと社畜らしく休日出勤してしまった。休日出勤した以上、代休を取らねばならない。

ところで、ドワンゴ社内のテキストディター利用率の統計が取られたことはないが、おそらくEmacsよりVim勢のほうが多数派ではないかと思われる。ドワンゴでは社員が各自好きなテキストエディターを使っている。なぜならば、テキストエディターはソースコードの読み書きをする極めて重要なツールであるので、プログラマーの好みが激しい分野である。テキストエディターの選択はプログラマーに委ねられていなければならないのだ。

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

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

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

2014-08-02

任天堂の使っている自由ソフトウェア

任天堂ホームページ:任天堂製品に関連するオープンソースソフトウェアのソースコード配布ページ

任天堂がひっそりと、自社製品で使っている自由ソフトウェアを公開している。

残念ながら、彼らはオープンソースという誤った用語を使っている。オープンソースというのは誤った運動であり、誤解を招きやすい用語だ。自由ソフトウェアという用語を使うべきである。

WiiUや3DSのファイルは相当に大きいので落として確かめていないが、Hacker Newsによれば、改変版Webkitとか、MSVCでコンパイルできるようにしたBluetoothライブラリとかだ。バーチャルコンソールにはMozillaのnanojitが使われているらしい。

WiiUのバーチャルコンソールがサイズも小さいので実際に落として確認してみたところ、nanojitが入っていた。

気になるのは、マリオカート8に使われている、BluetoothのSBCコーデックのライブラリだ。COPYINGをみるとGPLとなっている。ただし、COPYING.LIBをみると、LPGLとなっている。これはどういうことか。

ソースコードの著作権表記によると、どうやらSBCライブラリはLPGLで、そのライブラリを使ったsbcdecとかsbcencとかがGPLらしい。なるほど、sbcdecやsbcencはそのままマリオカート8に使うわけでもないし、任天堂が使っているのはLGPLのSBCライブラリだけなのだろう。

まあ、残念ながら、あまり面白いことはない。少なくとも、任天堂はCとC++を使っているということが明らかになっただけだ。

任天堂の秘密主義は本当に危険だ。私は任天堂のハードウェア用のプログラミング環境について、ほとんど知らない。どんな言語を使っているかすら、推測するしかない。これは、任天堂が馬鹿げたNDAを結んで、情報を公にしていないからである。

しかし、ガラケーの黒歴史で学んだように、我々は公開されていない情報から学ぶことはできない。公開されていない情報は見つからないからだ。公開できない情報は、他人と相談することができない。他人と知識の共有ができない場合、何とかひねり出したクソみたいな方法を、ノウハウだと勘違いしてしまう。

ガラケー時代は、ライブラリのインターフェースすら公開することができなかった。しかし、公にならない情報から人は学べないのだ。

そんな状況で、どうやって優秀なエンジニアを雇うというのか。情報が公開されていない場合、外部の人間は学ぶことができないので、新たにエンジニアを雇おうとしても、任天堂のハードウェア上のプログラミング経験は一切ないド素人を雇わなけばならないわけだ。いかに優れたエンジニアであっても、任天堂のハードウェアに関しては、学ぶ方法がないのだからド素人だ。全く経験したことのないハードウェア、ツール、ライブラリを学ぶのには、時間がかかる。何と無駄なことをしているのだ。

任天堂が落ち目なのも、当然だ。

それに、もはやゲーム専用機の時代は終わった。なぜゲームしかできない機械をわざわざ持たなければならないのか。今や筆者を除く誰もが持っているスマートフォンは、十分な性能を備えていて、クッキークリッカーやハイパーオリンピックぐらいなら余裕で実装できる。ハイパーオリンピックは、当時おおはやりしたゲームである。クッキークリッカーは最高のゲームである。そして、今はソシャゲというクッキークリッカーやハイパーオリンピックが流行っている。そう、ゲームの本質はクッキークリッカーやハイパーオリンピックだったのだ。あれでよかったのだ。自分の行動がゲームに良い成果となって現れるだけでよかったのだ。そこの深い技量など正確で高速な入力だのは、必要なかったのだ。自分の行動には、課金も含まれる。もはやパッケージを売って利益を上げる必要もないのだ。必要なのは継続的な課金するほどの価値の提供であって、高価なパッケージの抱き合わせ商法や品薄商法や限定版商法ではないのだ。

ところで、筆者は最近、ゲームをした。

Xibalba

未来からやってきたオールドスクールなシューターだ。久しぶりにいいゲームをした。

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