2012-01-21

2011-01 pre-Kona 2012 mailingの簡易レビュー

2011-01 pre-Kona 2012 mailingが公開された。

最新のドラフト規格はN3337になる。今回は、単なる文面の細かい修正に留まる。したがって、これを最新のC++11の規格を学ぶために参照しても、恐らく差し支えないであろう。しかし、以前にもC++標準化委員会のMLで、規格書の値段の高さを冗談めかしたコメントにて、「今後公開される無料のドラフト規格は修正を含んでいるので、現行の400ドルもする正式な規格書よりベターといえるのではないか?」という疑問も沸く。閑話休題(あだしごとはさておき)

N3322: A Preliminary Proposal for a Static if
N3329: Proposal: static if declaration

N3322とN3329の根本的な違いがわからない。なぜほとんど同じ提案のペーパーが二つ公開されたのだろうか。

コンパイル時条件分岐の提案。暫定的なキーワードとして、static_ifを使うとすると、以下のようにかける。

static_if( 条件 )
{

}
else
{

}

ifと何が違うのか。それは、条件分岐がコンパイル時に行われることである。たとえば、

template < typename T >
void f( T && t )
{
    static_if( std::is_same<T, std::vector<int>>::value )
    {
        t.push_back( 0 ) ;
    }
}

このようなコードが可能になる。従来のifでは、もし、テンプレートパラメーターTの型が、push_back(int)というメンバー関数を持っていなければ、コンパイルエラーになる。しかし、static_ifはコンパイル時に行われるので、コンパイルエラーにはならない。

これは、現在でも、テンプレートやオーバーロード解決を駆使することによって実現できる。実際、大昔のC++98においても、STLを正しく実装するためにはコンパイル時条件分岐のテクニックは必須である。であれば、最初から言語によるサポートをしたほうがよいのは当然だ。ちなみに、D言語ではネイティブでサポートされているとの由。どこからかD言語信者の勝ち誇ったかような雄叫びが聞こえてくるが、空耳であろう。

N3323: A Proposal to Tweak Certain C++ Contextual Conversions, v3

以下のコードを考える。

template< class T >
class Holder
{
public:
    Holder( ) = default ;
    Holder( T val ) : val( val ) { }
    operator T & ( ) { return val; }
    operator T ( ) const { return val; }
private:
    T val ;
} ;

まあ、よくある簡単なクラスだ。テンプレート引数の型を保持して、ユーザー定義変換で、あたかもその型のように振る舞うクラスだ。変換関数が非const版とconst版に分かれているが、これによって、オブジェクトがconstでも使えるようになる。さっそく使ってみよう。

    Holder<int *> p ;
    p = new int(0) ; // OK
    *p += 1 ; // OK
    int * pointer = p ; // OK

実に楽だ。では、使い終わったので保持しているポインターをdeleteしよう。

    delete p ; // エラー

残念ながら、これはエラーになる。なぜならば、ここではポインターへの暗黙の型変換が行われる。その際、「ポインターに暗黙に変換できる唯一の方法」を要求しているからだ。ところが、ポインターに変換する方法は二つある。よって、エラーとなる。

ではどうするか。明示的なキャストを使う方法もある。しかし、プログラマーというのは怠惰な人間なので、常に一文字でもタイプ数を減らす方法を模索するものだ。

    delete (p+0) ; // OK
    delete +p ; // OK

delete pが通らないのがバカバカしくなるコードだ。これを解決するために、TとTのリファレンス型を返す変換関数がある場合を認めようという提案。

N3324: Terminology: "indirection" versus "dereference"

規格の文面で、「ポインターを経由した間接アクセス」という意味の用語が統一されていない。そのため、すべてのdereferenceを、"indirecting through a pointer"という表現に置き換える提案。単純な置換えだが、かなり広範に渡る変更となる。

N3325: HTML for C++ Standards Documents

C++標準化委員会の公開するペーパーは、色盲やポータビリティを考慮して、HTMLにしようという提案。

ただし、主要ブラウザーがHTML5(というより、HTML5という名前自体に意味がないが)に進みつつあるなか、厳格なHTML 4.01に固執するのはどうかと思う。また、文字コードにはASCIIを使えというのも今時ありえない。第一、委員の多くが、ASCIIでは表現できない名前を持っている。したがって、作者は文字コードがASCIIだと信じていても、実はISO-8859-Xを使っていたりする。

そもそも、HTMLファイル内に、<meta http-equiv="Content-Type" content="text/html;charset=US-ASCII">などと書くのは、どのブラウザーからも機能しないと見放された太古の笑うべき仕様である。誰も正しい文字コードを指定しなかったから、信頼できないのだ。自己言及的なことに、同じく発表された、N3336が、HTMLファイル内ではWindows-1252であると主張しておきながら、"您好世界"という文字列を使っている。HTMLファイルの文字コードは、実際にはUTF-8である。こんなジョークでは何にでも笑う大阪人すら笑わせられない。

N3326: Sequential access to data members and base sub-objects

全世界の変態の皆様、特にBoost.Fusionユーザーの皆様、大変長らくおまたせ致しました。長年待ち望んでいたであろう機能の提案でございます。

unionではない任意のクラスのすべてのデータメンバーの型の一覧を、std::tupleでアクセスできる機能の提案。これさえあれば、メタプログラムがますます面白くなるだろう。

N3327: A Standard Programmatic Interface for Asynchronous Operations

非同期処理のためのライブラリの提案。

N3328: Resumable Functions

名前通り、レジュームできる関数の提案。これにより、非同期処理が実にシンプルに書ける。非同期処理のために単純なlambdaを複数書くのがめんどくさい人向け。

N3333: Hashing User-Defined Types in C++1y

C++11では、ハッシュテーブルを用いたコンテナーを追加した。unordered_setとunordered_mapである。そして、ユーザー定義型からハッシュを得るためのインターフェースも提供した。しかし、ユーザーがハッシュを実装するための補助は何もない。自分の用意したクラスをコンテナに入れたいユーザーは、自分でハッシュの算出を実装しなければならない。しかし、十分に強いハッシュ関数を実装するのは、一般人には困難である。現状を放置すれば、弱いハッシュ実装を使ったプログラムが世にあふれるであろう。そのため、ハッシュ関数の実装を助けるインターフェースを提供する提案。

この提案で用意されるのは、ひとつのプロセスがその回の実行に限り、ローカルにハッシュテーブルを構築するのに必要な強度のハッシュである。このハッシュ値は、プログラムの実行ごとに異なるものが出力される。つまり、ハッシュ値をディスクに格納して、次回の実行時に読み込んで使うということはできないし、複数のプログラムでハッシュ値を共有することもできない。また、文字列のハッシュ値を比較して、内容が一致しているかどうかを確実に確かめられるほどの強度もない。ましてや、セキュリティ上の目的でこのハッシュ値を使うことはできない。ハッシュ関数は目的毎に実装が異なり、すべての利用例をカバーしたハッシュ関数の実装が不可能なことはもちろん、ハッシュ関数のインターフェースの統一もいいアイディアではない。この提案されているハッシュ関数はあくまで、STLのコンテナに使うのに十分な強度のハッシュである。

N3334: Proposing array_ref<T> and string_ref

配列や文字列へのリファレンスというのは、現実によく使われる型である。しかし、どの型を使うかというと、定まった型がない。たとえば、std::vectorを使うことができる。しかし、何もstd::vectorにこだわる必要はない。たとえば、ポインターとその長さでもいい。文字列にしたって、std::stringのこともあれば、ポインターと長さのこともある。

もちろん、テンプレートを使って汎用的に実装することはできる。しかし、実装が複雑になり重複も生まれる。そのため、array_refというクラスを作り、どんな型でも、ある一定の操作方法さえ提供していれば、それを配列とみなして処理し、ユーザー側には統一されたインターフェースを提供しようというのが、このペーパーの提案だ。そのため、ユーザーコードは、わざわざこのような古典的な内容のために、クラスや関数自体をテンプレート化しなくてもよくなる。

N3335: Filesystem TR2 Proposal

TR2で採用する予定のFilesystemライブラリーの叩き台。Boost.Filesystem V3がベースになっている。

N3336: Adapting Standard Library Strings and I/O to a Unicode World

名前の通り、既存の文字列や入出力のライブラリーを、C++11で採用したUnicodeに対応させようという提案。TR2に採用予定。我々Unicodeに生きる日本人としては当然の変更である。

N3339: A Preliminary Proposal for a Deep-Copying Smart Pointer

unique_ptrとsmart_ptrをディープコピーできるようにする提案。ディープコピーとは、ポインターが指し示す派生関係にあるクラスの実行時のオブジェクトのコピーである。俗にクローンとも呼ばれている。

struct A { } ;
struct B : A { } ;
struct C : B {  } ;

A * get() ; // 実行時に型をnewして返す。

std::shared_ptr< A > p1( get() ) ;
std::shared_ptr< A > p2( /*ここで、p1の実行時の型のオブジェクトをnewコピー構築して渡したい*/ ) ;

p1は、実行時にA, B, Cのいずれかの型を取る。実行時に、この型のオブジェクトを何とかしてディープコピーしたい。

これらのスマートポインターは、いわゆるpimplのような実装を隠す手法に使われる。その際、ディープコピーができると嬉しい。そこで、ディープコピーを補助するためのインターフェースの叩き台を提案している。

N3340: Rich Pointers

コンパイラーの支援により強力なRTTIを提供するrich_ptrの提案。大規模な動的サーバーや分散システム、動的プログラミング言語の実装などに使われることを想定している。

N3341: Transactional Language Constructs for C++

2008年から始まった、C++にトランザクショナルメモリーを導入するワーキンググループの経過発表。

N3342: Digit Separators coming back

ユーザー定義リテラル「貴様のようなニューグラマーのなり損ないは、Rejectされる運命なのだ。わかるか!」
数値区切り「まだだ! まだ終わらんよ!」

C++11には入らなかった、区切り付き数字の提案。大きな数字、たとえば123456789が、アンダースコア区切りで、123_45_6_789と書けるようになる。何桁で区切っても構わない。正直、クソなユーザー定義リテラルよりこっちがC++11に入って欲しかった。提案された文法では、既存のクソなユーザー定義リテラルの文法とは曖昧にならない。

int x = 123_4_5_6_780 ; // 123456789

N3344: Toward a Standard C++ 'Date' Class

日付を表現する標準Dateクラスの提案。標準ライブラリーとして広く使えるライブラリーを作るのは難しい。

N3346: Defect Report: Terminology for Container Element Requirements - Rev 1

規格の文面の、コンテナー要素における、用語を統一する修正。

N3347: Modules in C++ (Revision 6)

近代的なプログラミング言語なら普通は備えているモジュールの提案。いつか#includeはもとより、プリプロセッサーのサポートを終了できる日を夢見て。もちろんC++だから、プリプロセッサーと共用できるように設計されている。

N3348: Scoping of operator new

現在、グローバル名前空間スコープとクラススコープでしか認められていないoperator newのオーバーロードを、名前空間スコープで宣言できるようにする提案。

N3349: Ease of using namespaces

本気で提案しているのかちょっと理解しかねる提案。namespace内の名前を無視させるディレクティブ"ignore namespace"の提案。以下のように使う。

namespace foo { class X { } ; }
namespace bar { class X { } ; }

using namespace foo ;
using namespace bar ;

void f()
{
    ignore namespace foo ;
    X x ; // bar::X 
}

このように、ignore namespaceディレクティブがある場所では、指定された名前空間内の名前をすべて無視する。こんなのが何の役に立つのか。おそらく、有名なライブラリのヘッダーをincludeする際に使うのだろう。

// C++11のインライン名前空間
inline namespace Win32 {
#include <Windows.h>
}


int GetLastError() ; // たまたまWin32 APIと名前が一致する関数

void f()
{

{// このスコープではWin32 APIは使わない。
    ignore namespace Win32 ;
    int result = GetLastError() ; // Win32 APIは無視される。
}

}

ある名前空間に属する名前をすべて無視するディレクティブなんて泥臭い機能である。それこそ、using namespaceよりひどい機能である。しかし、現実のプログラミングでは、どうしても変更できない巨大なライブラリ(しかも名前空間を使っていない)を複数組み合わせることを余儀なくされる。ライブラリが巨大であれば、どうしても名前の重複は避けられない。その際、わざわざスコープ解決演算子を使うのはめんどくさい。結局、これなしではもっと泥臭いコードになるのを回避する泥臭い機能なのだろう。それにしても・・・やはり泥臭い。

N3350: A minimal std::range<Iter>

そのまんま、Boost.Rangeの同等ライブラリのTR2への提案。

N3351: A Concept Design for the STL

C++11では却下されたSTLのためのコンセプト。一時C++11のドラフトに入っていたものより洗練されているとのこと。特に、たったの41のコンセプトしか定義せず、コンセプトマップを必要としない実装で、しかもテンプレートメタプログラミング臭いトリックが使われていないそうだ。

N3352: C++ Binary Fixed-Point Arithmetic

固定精度の整数と実数のライブラリの提案。これ上限が示されていないのだけれど、例えばcardinal<1024>にすれば、0 <= n <= 2^1024の範囲の整数クラスが使えるんだろうか。

N3353: C++ Concurrent Queues

同期キューライブラリの提案。

N3353: C++ Concurrent Queues

ストリーム(iostreamのストリーム)のためのロック機構の提案。しかし、ストリームなんてまともに使われていないから今更無駄だと思うのだが。

N3355: C++ Distributed Counters

マルチスレッドな環境で使えるカウンターの提案。頻繁にインクリメントされるが、読み出しは稀であるカウンターを想定している。複数のスレッドからのインクリメントは高速に行える。そのかわり、読み出しの遅さは許容する。

N3356: C++ Mutable Threads

スレッドプールより低レベルなライブラリーの提案。通常のスレッドは、あるひとつの関数を実行して、その関数から戻った際に、実行を停止する。しかし、ひとつの関数を実行するためだけにスレッドを作るのは非効率的な場合がある。例えば、小さな関数を大量に非同期に実行させたい場合などだ。そのような場合に使える、再利用可能なスレッドライブラリーである。このライブラリーは、スレッドプールの実装に使える。スレッドプールというには、やや低級なライブラリである。

以上。さて、今年こそC++本を完成させないといけないのだが、流石に疲れてきた。思うに、紙書籍という媒体には、もう未来がないと思う。それに、どうやら私はひとつのまとまった本を書く才能がないらしい。ブログなら簡単にかけるが、本は難しい。今の私を古人に擬すならば、橘成季だろう。

古今著聞集の作者である橘成季は理想主義者だった。彼は当時の公家らしく、様々な芸事に手を出した。競馬、詩文、作文、音楽、舞踊、はては絵までやっている。しかし、今日の我々は、橘成季を知らない。彼の作品はそれほど評価されず、残っていないからだ。彼は当時はやりだった説話集も作った。理想主義者らしく、説話を分類し、話の典拠を明記し、当時の公家日記に見つからないものはその旨も記した。しかし、古今著聞集は、一流にはなれなかった。読んでいても文章はつまらないし、量でいえば今昔物語に負けている。理想は高かったが、惜しいかな、本人の力量がどうしようもなく足りていなかったのだ。

思うに、もう少し軽いノリの本を作るべきだったと思う。ブログのような、あるひとつの主題に対して短い文章を書いたものをまとめた、それこそ説話集のような形式を取るべきだったのだと思う。代わりに今書いているのは、規格書の劣化コピーだ。それなら最初から規格書を読めばいいのだ。もし英語が読めないとしたら、その者は今日においてプログラマーとなる資格はない。

ともかく、始めたことは終わらせなければならない。初心者向けではない本でしかも自分ですら未熟だと思う内容なのだから、どうも売れる気がしない。しかも悲惨なことに、この数年、本の執筆だけを、如何にしてプログラミング言語の文法と機能を教えるかということだけを考えてきたので、まともにプログラミングできなくなっている。プログラマーとしては役立たずだ。これが終わったら、この業界からは身を引くべきなのかもしれない。C++を極めた挙句プログラマーにはなれませんでした、とは笑い草だ。いや、実際には、極めてすらいないのだ。

2 comments:

Akira Kanasugi said...

N3348は、現在、クラスのメンバー関数とグローバル関数でしか認められていない operator new を、任意の namespace で定義できるようにする提案でしょうか?

江添亮 said...

そういえばそうでした。
operator newは名前空間スコープではオーバーロードできなかったんでしたね。