December 28, 2012
テンプレート・メタプログラム中で文字列を使う

source: http://cpp-next.com/archive/2012/10/using-strings-in-c-template-metaprograms/

メタプログラミング1が大好きになる理由のなかでも、プログラムに”小さな言語”を埋めこむことができる、というのは抗い難い魅力がある。たとえば、複素数のパーサーを再帰下降パーサーを手書きするよりは、こんなふうに書きたいと思うだろう。

auto parse_complex =
    '(' >> double_ >> -(',' >> double_) >> ')'
  | double_;

ほかにも、CDプレーヤーのステートマシンを手書きするかわりに、状態遷移表をこのように書きたいと思うはずだ。

auto player =
Stopped + play [some_guard] / (some_action , start_playback)  == Playing ,
Stopped + open_close/ open_drawer                             == Open    ,
Stopped + stop                                                == Stopped ,
Open    + open_close / close_drawer                           == Empty   ,
Empty   + open_close / open_drawer                            == Open    ,
Empty   + cd_detected [good_disk_format] / store_cd_info      == Stopped

これらBoost.SpiritとBoost.MetaStateMachineを使った二つの例は、どのようにパーサーやステートマシンを記述できるかを示している。 これらのライブラリに組み込まれているメタプログラムは、ユーザーが記述した高度に抽象化された宣言を解釈し、超効率的な実行コードを生成する。 これらの宣言を表現するための”小さな言語”はライブラリ・ドメインに特化しており(パーサーやステートマシン)、ホスト言語であるC++に埋め込まれている。このような言語は、 EDSL(Embedded Domain Specific Language) と呼びならわされる。

式テンプレート

上記二つのEDSLには、”式テンプレート(expression templates)”と言うテクニックが用いられている。 式テンプレートにおいては、人が普通考える即時計算のような流れではなく、演算子と関数をオーバーロードすることで、メタプログラムが最終的に要求されたコードを生成するために利用する、コンパイル時パースツリーの生成を達成している。 この “遅延評価” によって、ライブラリは式全体を考慮することができるので、式を即時評価する場合には不可能な最適化を行うことができる。

BoostにはProtoという式テンプレートライブラリを組み立てるためのライブラリがある、という点をみても、式テンプレートは汎用的で非常に効率的であると言えるだろう。 だが、厳しい制限もある。式がC++で有効な表現でなければならないという点だ。このような小さな言語の強力な利点としてよく上げられるのは、既に存在していて、広く知られており、その問題領域に属する人が受け入れやすいように進化した構文を提供できる点だろう。 このような進化はコンピュータで言語が処理されるよりもはるか前に起こっている場合もある(記譜法について考えてみよ)。よって、有効なC++の式として正確に解釈できるDSLというのはごく少ない。我々には近い構文を思いつくのが精々だ。非常に”コンピューターに近い”言語、たとえば正規表現でさえも、直接C++に与えることはできない。

auto all_caps = [A-Z]+; // SYNTAX ERROR

古き良き文字列ならどうか?

もちろんC++式の代わりに、DSLコードスニペットを表現するのに文字列を使う手もある。 実際、この方法は”従来の”ライブラリにもよく使われている。

auto all_caps = std::regex("[A-Z]+"); // OK

ただ、一つ問題がある。文字列の内容はランタイムにしか分からないので、この埋めこんだ言語はランタイムにしか解釈できず、翻訳した結果をコンパイル済みのコードに落とすことが不可能になることだ。”遅延評価”は実行時まで遅延されてしまう。

しかし、逆に言えば、コンパイル時に文字列をパースできれば、最適なパフォーマンスを得ることができるわけだ。これをやってのけるには、テンプレートメタプログラムに文字列を渡すことが必要不可欠だ。テンプレートメタプログラムはもちろん、この文字列を処理して、同時に、解釈可能なC++コードを生成しなければならない。こんな風に書けるのが理想だろう。

tmp_string<"Hello World!">

しかし、文字列リテラル”Hello World!”は内部リンケージを持つ文字配列なので、テンプレート実引数として渡すことはできない。

メタプログラムでの文字列

さて、テンプレートメタプログラムが解釈できる文字シーケンスをどう表現したらいいだろう?

MPLは、コンパイル時に個々の要素に対する操作や列挙を行う機能をもつ、テンプレートメタプログラム用のシーケンスを備えている。例えば、

boost::mpl::vector_c<
    char, 'H','e','l','l','o',' ','W','o','r','l','d','!'
> s;
 
char const fifth_char                   // 'o'
  = boost::mpl::at_c<s,4>::type::value; // like s[4]

いちどこのように表現できてしまえば、これを処理するのは簡単だ2。問題はどうやってここまで持ってくるかだ。もちろん、直接書き下してもいいが、とても書きにくいし読みにくい。MPLには複数の文字定数を受けつける特殊なラッパーテンプレートがある。

boost::mpl::string<'Hell','o Wo','rld!'>

ほんのちょっと短くなったが、さらに読みづらくなってしまった。また、文字の挿入や削除は非常にやっかいだ。というのは、4文字境界を破壊しないように、毎回手作業で調整しなければならないからだ。

ましな方法

必要なのは”Hello World!”のようなC++文字列リテラルをMPLシーケンスに自動変換する方法だ。そのために、MPLのpush_backメタ関数を使って一文字ずつ組み上げてみよう。こんな風に。

boost::mpl::push_back<
  boost::mpl::string<'Hell'>,
  boost::mpl::char_<'o'>
>::type // approximately boost::mpl::string<'Hell','o'>

MPLシーケンスの要素はどんな型もとり得るので、mpl::char_ラッパーを使って’o’文字を型に落し込んでいる。上の式は”Hello”という文字列をmpl::char_sのコンパイル時シーケンスとして生成する。

この方法を使えば、文字列全体を一文字ずつ生成できる:

boost::mpl::push_back<
  boost::mpl::push_back<
    // ...
    boost::mpl::push_back<
      boost::mpl::string<>,   // The empty string
      boost::mpl::char_<'H'>
    >::type,
    // ...
    boost::mpl::char_<'d'>
  >::type,
  boost::mpl::char_<'!'>
>::type

この型式は空文字列で始まり、一文字ずつ”Hello World!”という文字を加えていくのだが、もちろんこんなのを手作業で書きたくはないだろう。コンパイラが自動的にこれを生成するワザが欲しいと思うのが普通だ。そのために、突破口となる新しい言語機能が必要になる。

一般化された定数式

C++11では、”一般化された定数式”3 が標準化された。これは基本的に二つのことをやってのける。

  1. 文字列リテラルのようなものも、コンパイル時定数として使える
enum { o = "Hello"[4] }; // OK in C++11
  1. 実引数が全てコンパイル時定数なら、出力される結果もコンパイル時定数になるconstexpr関数の追加
constexpr unsigned factorial(unsigned x)
{
    return x == 0 ? 1 : x * factorial(x-1);
}
int a[factorial(6)];    // OK in C++11

ぱっと見、この二つの機能があれば、もうコンパイル時に文字列リテラルを直接操作できるような気がする。 だがしかし、constexpr関数には、普通の関数としてもコンパイルされなければならない、という縛りがある。仮に実引数のうち一つでもコンパイル時定数でないなら、この関数は普通にランタイム動作することになり、コンパイル時定数ではなくなってしまう。その結果、関数内部、そしてその戻り値の型は、ふつうの実行時に利用される値として扱わねばならなくなってしまう。 これはC++メタプログラミングの要であるが、実行時の値は型に対してまったく影響をおよぼさない。

constexpr std::array<int,n> // ERROR: n is not a constant expression
f(unsigned n); 
 
constexpr void g(unsigned m)
{
    std::array<int, m> a;  // ERROR: m is not a constant expression
}

制限をごまかすには

まだ全てがオジャンになったわけではない。constexpr関数でそう多くのことを達成できないが、前に示した通り、文字列リテラルも定数式だし、定数式である配列の添字演算子もまた定数式である。よって、”Hello World!”[ 0 ]は定数式なので、先の型シーケンスは以下のようにも構築できる。

boost::mpl::push_back<
  boost::mpl::push_back<
    // ...
    boost::mpl::push_back<
      boost::mpl::string<>,
      boost::mpl::char_<"Hello World!"[0]>
    >::type,
    // ...
    boost::mpl::char_<"Hello World!"[10]>
  >::type,
  boost::mpl::char_<"Hello World!"[11]>
>::type

上の例は、前のヴァージョンそっくりだが、個々の文字を記述するのではなく”Hello World!”という文字列から添字演算子で抽出している点が異なる。

明らかに、複雑さの上にややこしさを積んだようなもので、より悪くなっているようにしか見えない! だがちょっと待ってほしい。上の例は、マクロ実引数として文字列リテラルを取るプリプロセッサマクロに書き直せる。

#define _S(s)                                   \
  boost::mpl::push_back<                        \
    boost::mpl::push_back<                      \
      // ...                                    \
      boost::mpl::push_back<                    \
        boost::mpl::string<>,                   \
        boost::mpl::char_<s[0]>                 \
      >::type,                                  \
      // ...                                    \
      boost::mpl::char_<s[10]>                  \
    >::type,                                    \
    boost::mpl::char_<s[11]>                    \
  >::type

こうすれば、以下のように”Hello World!”はテンプレート実引数として渡せるようになる。

f<_S("Hello World!")>

マクロを整える

マクロを短縮していないので、上記の例は4倍ほども長くなっているし、典型的なDRY違反を犯している。もうすこしこのコードをまともにするために、Boostのプリプロセッサメタプログラミングライブラリを使って、この繰り返しをなんとかしてみよう。

#define PRE(z, n, u) boost::mpl::push_back<
#define POST(z, n, u) , boost::mpl::char_<s[n]>>::type
 
#define _S(s)                                   \
  BOOST_PP_REPEAT(12, PRE, ~)                   \
    boost::mpl::string<>,                       \
  BOOST_PP_REPEAT(12, POST, ~)

BOOST_PP_REPEATがPRE、POSTを展開するとき、nマクロ実引数として、添字、すなわち展開するときのリスト中の位置を渡す。POSTはこの添字からs中の文字を示す添字を生成する。4

さて、constexprの使い時だ

12という数字はこの例の文字列の長さからもってきたが、これでは12文字より短い文字列に対応できない。例えば、_S(“foo”)は以下のように展開されるだろう。

boost::mpl::push_back<
  // ...
    boost::mpl::push_back<
      boost::mpl::push_back<
        boost::mpl::push_back<
          boost::mpl::push_back<
            boost::mpl::string<>,
            boost::mpl::char_<"foo"[0]>
          >::type,
          boost::mpl::char_<"foo"[1]>
        >::type,
        boost::mpl::char_<"foo"[2]>
      >::type,
      boost::mpl::char_<"foo"[3]>
    >::type,
  // ...
  boost::mpl::char_<"foo"[11]>
>::type

{‘f’, ‘o’, ‘o’, ‘\0’} はたった4要素しかないので、与えられる添字は配列の終端を9回も越していることになる。幸いにも、これらのことはコンパイル時に解決されるので、コンパイラはエラーを吐いてくれるだろう。安全なだけではなく、もっと汎用性を持たせられないものか。この問題をどうにかするには、配列の要素を取得する関数テンプレートを記述すればよい。そしてここでconstexprがやってくれる。

template <int N>
constexpr char at(char const(&s)[N], int i)
{
  return i >= N ? '\0' : s[i];
}

char const(&s)[N]は N個の要素からなるconst charの配列への参照を意味し、これにより配列のサイズが推論できるので、終端よりむこうの要素を指すような添字が与えられたなら0を返すことができる。_Sマクロで使っている添字演算子をこれで置きかえてみよう:

#define PRE(z, n, u) boost::mpl::push_back<
#define POST(z, n, u) , boost::mpl::char_<at(s, n)>>::type
 
#define _S(s)                                   \
  BOOST_PP_REPEAT(12, PRE, ~)                   \
    boost::mpl::string<>,                       \
  BOOST_PP_REPEAT(12, POST, ~)

やっかいな0

上述の実装で_S(“foo”)をコンパイルできるようになったが、これはイマイチよろしくない結果を吐きだす。”foo”に対応するシーケンスを吐くのではなく、”foo\0\0\0\0\0\0\0\0\0”に対応するシーケンスを吐いてしまうのだ。_Sが文字列の長さについて感知しないので、ただ言われた数だけ文字列のうしろにヌル文字を追加してしまうからだ。この運命を変えるには、boost::mpl::push_backを、規定の長さに達したらシーケンスの拡張を止めるラッパーに置きかえればよいだろう。

template <class S, char C, bool EOS>
struct push_back_if_c :
  boost::mpl::push_back<S, boost::mpl::char_<C>>
{};
 
template <class S, char C>
struct push_back_if_c<S, C, true> : S {};

このラッパーは元文字列と、文字数、さらに追加のboolean実引数を取る。このbooleanが真のとき、文字列の終端を越えたことを示す。このとき、単に前の結果を返すだけだ。でなければ、push_backに実引数を転送する。

この関数を使うことで、このマクロは12文字以下の文字列を正しく扱うことができるようになる。

#define PRE(z, n, u) push_back_if_c<
#define POST(z, n, u) , at(s, n), (n >= sizeof(s))>::type
 
#define _S(s)                                   \
  BOOST_PP_REPEAT(12, PRE, ~)                   \
    boost::mpl::string<>,                       \
  BOOST_PP_REPEAT(12, POST, ~)

制限を設定可能にする

12文字より文字列が長い場合はどうだろう?うむ、このマクロは破綻してしまう。しかし、ハードコーディングしている12を別のシンボルに置きかえれば、ユーザーが上限を設定できるようになる。

#ifndef STRING_MAX_LENGTH
  #define STRING_MAX_LENGTH 32
#endif
 
#define PRE(z, n, u) push_back_if_c<
#define POST(z, n, u) , at(s, n), (n >= sizeof(s))>::type
 
#define _S(s)                                   \
  BOOST_PP_REPEAT(STRING_MAX_LENGTH, PRE, ~)    \
    boost::mpl::string<>,                       \
  BOOST_PP_REPEAT(STRING_MAX_LENGTH, POST, ~)

このコードは_Sが扱える最大文字数を示すSTRING_MAX_LENGTHマクロを導入している。ユーザーが_Sの定義をインクルードする前に#define STRING_MAX_LENGTHがなければ、デフォルトとして12が与えられる。

もしpush_back_ifが_Sを使うたびにSTRING_MAX_LENGTH回利用されるとしたら、必要よりも高い制限を使わないのは理に叶っている。ただ注意しなければならないのは、未使用の文字それぞれについて、同じ組の実引数が繰りかえしpush_back_if_cに渡されるということだ。

push_back_if_c<
  // The final string,
  '\0',
  true
>

すなわち、未使用の文字のぶんだけ、コンパイル時間は伸びてしまうが、テンプレート実体化の数は変わらない。

まとめ

プリプロセッサ、constexpr、テンプレートメタプログラミングを組みあわせることで、文字列リテラルをテンプレートメタプログラムで操作できる型に落としこむ方法を示した。このアーティクルで紹介した技法は、Metaparseライブラリという、コンパイル時文字列パーサー生成キットに組み込まれている。


Footnotes:

1 メタプログラミングが大嫌いになる理由に関する議論については、また別の機会に ;-)

2 テンプレートメタプログラミングできればまぁ…

3 N2235 Generalized Constant Expressions — Revision 5

4 この例は、C++プリプロセッサがそれ自体では文法的に正しくないコード断片を生成可能であるという事実を利用している。入れ子になったマクロ呼び出しで入れ子になった構造を生成するかわりに、単に二つの平易な繰りかえしを生成している。一方は区切りを開始し、もう一方で終了している。この技法はプリプロセッサを利用する上で有効な方法である。

June 20, 2012
Constant Initialization

source: http://akrzemi1.wordpress.com/2012/05/27/constant-initialization/

ひとつ前の、コンパイル時計算についての記事では、いかにこのconstexprという新しいキーワードを使い倒して、面白い効果を引きだすかについて述べた。 さて今回は、このキーワードの用途についてもうすこし見ていこう。もうすこしコンパイル時定数の話も書けると思うが、例えばこの提案を読んでいる読者には既知のことだと思うので。

この記事はconstant initializationについてである。端的に言うと、これは初期化順やデータレースに起因する問題を避けて、global、static、またはスレッドロゥカルなオブジェクト(定数でなくともよい)を初期化するための新しい方法である。

globalに関する諸問題

この記事では、’global’というのは、以下のようなオブジェクトを意味する:

  • 静的記憶期間をもつnamespace-scope オブジェクト(namespaceは global でも無名でも可)
  • スレッド記憶期を持つnamespace-scope オブジェクト
  • クラスのstaticデータメンバ
  • 'global' への参照

globalの利用はいろいろな意味で問題になる。コードの局所性を破壊するので、しばしばプログラムの理解の妨げになる。関数呼び出しがglobalを変更しているかどうか分からない。globalへのアクセスは、ロック機構でglobalを保護するか、atomicを使わない限りデータレースを引きおこしやすい。C++は’動的ライブラリ’の概念がないので、プログラムがあるライブラリに動的にリンクしたとき、globalがどう振る舞うべきか規定されていない。いずれかがglobalのコピーを持つべきか?globalはライブラリがリンクされる前に初期化されるべきか?しかしライブラリがリンクするまえに動的ライブラリのglobalを初期化できるわけがない… あとglobalは作成される前にも、破棄した後にもアクセスしたり使ったりできてしまう。

ここで示したいのは、globalの初期化の順の問題についてである。次のような二つのファイルにまたがる例を考えてみよう:

file 1:

1   extern std::mutex m; // declaration
2   std::thread t1{job1};
3   std::thread t2{job2};
4   //...

file 2:

1   std::mutex m; // definition
2     
3     
4   // ...

mutex mのようなシチュエーションは実際のプログラムでもよく見られる。すなわち、ヘッダファイルに宣言があり、実際のglobalオブジェクトは別のファイルに定義されている、というケースだ。さて、mainが実行される前にふたつのスレッドがふたつのジョブにとりかかる。mainが走る前にジョブの一部が実行される。このふたつのスレッドはmutex mで同期を取らなければならない。mutexで同期を取るにはロックされているかどうか確認して(値を読み取り)、ロックする(そして値を書き込む)。しかし、最初のスレッドが初めてmutexでロックしようとしたとき、はたしてmutexはデフォルトコンストラクタで初期化されているだろうか?

お気づきのことかもしれないが、この問題は「静的オブジェクトの初期化順の問題(static initialization order fiasco)」と呼ばれる。すなわち、mがglobalとしてある翻訳単位(ファイル)にあり、t1とt2がglobalとして別の翻訳単位にあるとき、異なる翻訳単位にあるglobal間の初期化順は一般に決定不能である…。

さてここで’一般に’というワードを強調しておく。というのはC++11はC++03よりはるかに、これが問題になるケースが少ないからで、実際のところ、上記の例は静的初期化と呼ばれる機能により初期化順の問題が解決される。

Zero-initialization

概してその機構は次のように説明される。型のコンストラクタがある制約を満たしており、コンストラクタに渡される実引数がある制約を満たしているとき、globalはプログラムが実際に実行されるまえ、すなわちコンパイル時に初期化されることが保証される。この機構はまったく新しいものではなく、制限つきながらC++03にもzero-initializationとして存在している。

例えばintやfloat、pointerやその配列などのように、トリビアルなデフォルトコンストラクタを持ち、明示的な初期化子を持たない型のglobalは、コンパイル時に0で初期化される。コンパイル時にグローバルアドレスに配置されているglobalについては簡単に達成され(スレッドロゥカルなオブジェクトに関してはこの限りではないが)、また0埋めするのはコンパイラに負荷をかけない。これは実行時にアロケートされ、0埋めするために実行時オーバーヘッドがかかるる自動オブジェクトと違う点である。

globalのZero-initializationは種々の有用なデザインに適用できる。例えば、なにも初期化しなくてもglobal intを宣言するだけでカウンタを作ることができる。

1   int counter; // no initializer

これは0クリアが保証されている。同様に、次のように生ポインタの遅延ロードを実装することもできる。

1   Utility * ptr; // no initializer
2    
3   Utility & getUtility()
4   {
5     if (!ptr) {
6       ptr = new Utility;
7     }
8     return *ptr;
9   }

これも、null-pointerで初期化されていることが保証されている。しかしながら、(並行処理の問題はいったん置くとしても)、この例には問題がある。ロードしたオブジェクトを破棄する必要性が生じたとき、まずsmart pointerを使うことを考えるだろうが、smart pointerは’プリミティヴ’ではないし、自前のデフォルトコンストラクタを持っている(か、デフォルトコンストラクタ自体ない)場合がほとんどだろう。zero-initializationの保証は失われてしまう。

ゆえに、読者はGo言語で薦められている初期化アプローチを適用してみようと考えるかもしれない。 すなわち、型のデータを工夫して、zero-initializationが自前の型について有効になるようにするのである。ポインタについては簡単だ。

1   template <class T>
2   class SmartPointer
3   {
4     T * rawPtr_;
5     // no constructor
6   };

zero-initializationはこのオブジェクトについて有効になり、nullの生ポインタを保持するnull smart pointerができる。mutexの場合は、このアプローチは実装に制限ができる。mutexの実装は次のようになるだろう:

1   class mutex
2   {
3     bool youCanLockMe_;
4     vector<thread::id> threadsLocked_;
5     // invariant: if (youCanLockMe_) threadsLocked_.empty();
6   };

ここで、youCanLockMe_ はどのスレッドもmutexをロックしていないことを示し、threadsLocked_は現在ロックされているthreadのリストであり、現在のオーナーがロックが解除した時通知を行う。この実装はzero-initializationでは二つの理由で動作しない。まず、youCanLockMe_フラグはzero-initializationの結果falseに設定されるので、mutexはロック状態で初期化されることになり、どうやってもアンロックできないことになってしまう。次に、vectorはzero-initializationのタイミングでは利用できない。すなわち、デフォルトコンストラクタ(実行時にのみ呼ばれる)は、vectorにアクセスする前に呼び出されなければならない。さもなければ未定義動作になってしまう。

一方、以下のような実装ならば動作する。

01  class treadIdLink
02  {
03    thread::id id_;
04    SmartPointer<treadIdLink> next_;
05  };
06   
07  class mutex
08  {
09    bool locked_;
10    SmartPointer<treadIdLink> threadsLocked_;
11    // invariant: if (!locked_) threadsLocked_ == nullptr;
12  };

まずここでは、フラグの意味を逆にしたので、zero-initializationの結果、フラグは今mutexをロック可能であることを示している。また、ロックしたスレッドのリストをlinked listに保持している。こうすれば、smart pointerをzero-initializationした結果であるnull-pointerで空のリストを表現できる(うまくいけば)。

この方法はmutexについては適用できる(というのは、他のコンストラクタを必要としないからだ)が、ここで作成したSmartPointerは実際うまく動かないだろう。なぜなら、smart pointerは、例えば、生ポインタを取るような他のコンストラクタが必要だからだ。そして、コンストラクタを提供してしまうと、コンパイラはデフォルトコンストラクタを作ることを求めてくる。これに従ってしまうと、このポインタを(実行時の)デフォルトコンストラクタが呼ばれる前に利用するのは未定義動作になってしまう。しかし、いかなるコンストラクタも持たないsmart pointer(mutexも同様)であっても、別の致命的な問題がある。それは、zero-initializationはglobalに対してのみ働く、ということだ。つまり、自動オブジェクトや動的に構築したオブジェクトについては初期値にゴミが入っていることになる(intやfloatやpointerやそれらの配列についてもそうであるのと同じく)。

Constant initialization

C++11はzero-initializationのアイディアを拡張して、いかなるglobalの初期化についても十分シンプルに、コンパイル時に行われることが保証されるようにした。もう見当がついたかもしれないが、’十分シンプル’とはconstexpr関数ならびにconstexprコンストラクタと同じルールが適用できる、ということだ。constexpr関数をコンパイル時に評価し、複合型の構築に使うというのは、おそらく読者には目新しい情報ではないはずだ。しかし、ここでは、constではないオブジェクトの初期化について述べている。mutexは必要に応じて状態を変えなければならないから。

C++11のルールではどのようになっているのだろう? 全てのglobalがzero-initializeされ、そのあとconstant initializationが行なわれる。 すなわち、コンパイル時定数、または初期値があるconstant expressionの評価によってのみ、globalオブジェクトは、globalの実行時初期化が行われる前、コンパイル時に初期化される。 それゆえ、初期化されたオブジェクトはそれ自体がコンパイル時定数でなくともよい(もちろんその場合、他のglobalのconstant initializationには使えないが)。

どう使うのだろう? ほとんどなにもしなくてよい:

1   template <class T>
2   class SmartPointer
3   {
4     T * rawPtr_;
5     constexpr SmartPointer() noexcept : rawPtr_{nullptr} {};
6     // other constructors
7   };

この宣言は最初の例よりもなにやらぎこちなく見えるが、細かく見ていこう。constexprはこのconstructorがconstexpr-constructorであることを示している。すなわち、このクラスがリテラル型(しかし、このスマートポインタはそうではない)か、内包する式がすべて定数式で、静的初期化段階に実行することができるならばコンパイル時定数として作成できる。この特徴はユーザー型がリテラル型であることを要求しない。 noexceptはconstant initializationにおいて重要ではないが、(定数式としてコンパイル時に初期化できる程度に)簡単な関数を定義したとき、これは、失敗しない関数であり、例外の基本的な保証と強い保証を見たす構成要素として利用可能であることを示すという意味がある。 上記のスマートポインタクラスのコンストラクタはtrivialではない。trivial コンストラクタはrawPtr_をnull pointerに初期化しないので、ここでは明示的に初期化している。生ポインタをnull pointerに設定する処理は定数式である。よって、このコンストラクタは十分に単純であり、静的初期化が保証される。

さて、これが何をもたらすか? SmartPointer<T> 型のglobalオブジェクトがデフォルトコンストラクタで初期化されるならば、コンパイル時に初期化処理が行われることが言語レヴェルで保証されているので、初期化順の問題にもう悩まされることはない。同時に、デフォルトコンストラクタ以外のコンストラクタは実行時に初期化される。globalはC++03と同じように生成する。

1   // at global namespace
2   SmartPointer<int> intPtr;

もう他にconstexprをタイプする必要はない(し、実際すべきでない)。デフォルトコンストラクタがconstexpr constructorとして定義されれば十分だ。intPtrはコンパイル時定数ではない。そしてまた ‘実行時定数’ でもないので、実行時に自由に値を変えることができる。これは単に初期化処理がコンパイル時に行なわれることが保証されているだけなのである。同様に、mutexについてもconstant initializationを保証できる:

1   class mutex
2   {
3     bool locked_;
4     SmartPointer<treadIdLink> threadsLocked_;
5     // invariant: if (!locked_) threadsLocked_ == nullptr;
6    
7     constexpr mutex() noexcept : locked_{false}, threadsLocked_{nullptr} {};
8   };

メンバ変数を0で初期化しなくてもよいので、以下のような実装も可能だ:

1   class mutex
2   {
3     bool youCanLockMe_;
4     SmartPointer<treadIdLink> threadsLocked_;
5     // invariant: if (youCanLockMe_) threadsLocked_ == nullptr;
6    
7     constexpr mutex() noexcept : youCanLockMe_{true}, threadsLocked_{nullptr} {};
8   };

しかしながら、ロックしたスレッドIDの保持にstd::vectorを使う実装に戻すことはできない。というのは、vectorのデフォルトコンストラクタはconstexpr constructorではない(し、そう仕様に要求されているわけでもない)からだ。

Constant initialization の潜在的な諸問題

さて、我々はglobalクラスのコンパイル時初期化を可能にする道具を手にしたわけだが、constant initializationに依存している場合に注意すべき重要な問題がある。

globalの初期化が本当にコンパイル時に行なわれているかどうかを確認するのは困難である。もちろんバイナリを精査する手は残っているが、マルチプラットフォーム対応や、(デバッグやリテールのような)コンパイルモードを変える場合を考えてみれば、検査したバイナリに対しての保証でしかなく、プログラムに対する保証ではない。コンパイラはこの件に関して何の助けにもならない。コンパイラに、あるglobalがconstant initializeされているかどうか検査させる術はない。もしリテラル型の定数を初期化しているならば、以下のようにすればコンパイラに確認させることができる:

1   constexpr double x{1.0};
2   double y{2.0};              // ups! y is not a compile-time constant
3   const Complex c1{x, y};     // constant initialized at run-time
4   constexpr Complex c2{x, y}; // error: y is not a compile-time constant.

二番目のconstexprでコンパイラにc2を初期化する式が全て定数式かどうか(この場合ば’y’が定数式でない)を検証させている。しかしこれでは、constant initializationのみならず、オブジェクトの不変性も要求してしまっている。 もしconstant initializationだけが必要なら、constexpr指定子を使うことができない。しかしそれでは、不用意に定数式ではない(さらに型変換のような式は見えない)サブ式をたったひとつでも使ってしまっていたら、初期化は断りなく実行時に行なわれることになるだろう。 それゆえ、このような潜在的な問題を回避する最良の方法は、constant initializationは、不用意に’非定数式’を引数に取りようがないデフォルトコンストラクタだけに限ることである。

もうひとつ、constexpr関数/コンストラクタ テンプレートにまつわる問題がある。ある関数実体がconstexpr関数であるための条件をひとつでも満たしていない場合、コンパイラは断りなくconstexpr指定子を落してふつうの関数にすることが認められている(し、実際要求されている)。このため、mutexのように、’非テンプレート’型を用いるか、テンプレートパラメータTが与えられた場合、Tを操作する関数(テンプレート実装者からはどんな実装になっているか分からない)を呼ばないようにしたほうがより安全である。– 先のSmartPtrのように。

そしてこれはC++標準ライブラリでも多かれ少かれ採用されているガイドラインでもある。コンパイル時定数はさておき、静的初期化はstd::unique_ptrや、std::shared_ptr、std::weak_ptr、std::mutex、std::once_flagのような、型をnullptrで初期化するdefault constructorにのみ適用されている。 STLのコンテナにもおなじような保証がなされている、と思うかもしれない。その考えはたしかに筋が通っているが、標準ライブラリのプロバイダーからしてみると、実行時の効率など別の目的にもかなう柔軟性をライブラリに持たせるためには、このガイドラインが非常に大きな足枷になってしまう。

このガイドラインの興味深い例外として、std::atomicクラステンプレートが挙げられる。このクラステンプレートのデフォルトコンストラクタはconstexprではない(自動オブジェクトについてC言語と同じ実行時パフォーマンスを保証するために値は未初期化のままにしている)が、データコンストラクタはconstexprである。

まとめ

ここではコンパイル時の初期化について多少誇張している。C++標準が本当に保証しているのはzero-initialization(0で初期化する)とconstant initialization(定数で初期化する)(これら二つの初期化はまとめて静的初期化(static initialization)と呼ばれる)が、プログラム上他の(動的)初期化よりも前に行われるということである。 それゆえ、静的初期化について、プログラム開始時のあるタイミングで行なわれても規格上は問題ない。だが、初期化順の問題を回避する、という目的を果たすために、またコンパイラ作者はただISO C++ 標準に従うだけではなく、市場のニーズ(例えばパフォーマンスなど)についても応えるよう努力するであろうから、このような初期化は、コンパイラによって、実行時コストなしで行なわれるのが自然であろう。

ここであらためて、もういちどconstexpr 関数/コンストラクタが持つ二つの役割について見ておこう:

  1. 複合型のコンパイル時定数の作成および評価
  2. 可変複合型globalオブジェクトのコンパイル時初期化

参考

  1. "Generalized constant expressions” — a sequence of proposals for generalizing constant expressions (N1521, N1972, N1980, N2116, N2235) by Gabriel Dos Reis, Bjarne Stroustrup and Jens Maurer.
  2. The C++ International Standard. It used to be accessible from the Committee’s website. If you need to refer to an online resource, the latest standard draft — N3376 — is pretty similar in content. The following are the relevant places therein: § 3.6.2 (Initialization of non-local variables), § 3.8 (Object lifetime), § 5.19 (Constant expressions), § 7.1.5 (The constexpr specifier), § 8.5 (Initializers), § 12.1 (Constructors), § 12.9 (Inheriting constructors).
  3. N2994: “constexpr in the libray: take 2” by Alisdair Meredith. It contains loads of useful information about constant initialization.

June 13, 2012
C++Now! - BoostCon新装開店 (C++Now! – Boost-Konferenz im neuen Gewand)

source: http://www.heise.de/developer/artikel/developer_artikel_1586999.html

新しいBoost(Neuer Boost)

五月中旬、BoostConに代わって、C++Now!が開催された。新しいC++11標準についても、簡便性やモジュール性についての議論が交わされ、焦点が当てられた。

(Mitte Mai fand die aus der BoostCon hervorgegangene Konferenz C++Now! statt. Neben dem Schwerpunkt auf dem neuen C++11-Standard sorgten Überlegungen hinsichtlich eines modularen und einfacheren C++ für Aufsehen.)

Boost-CommunityはBoost Libraryのさらなる発展のためにBoostConをここ数年開催している。昨年、新しいC++が標準化されたことを受けて、非常に多くのアイデアがBoost-Commnunityに流れ込み、会合の主題はBoost LibraryというよりC++11に移ったようだ。会合の名前はC++Now!にかわったものの、例年通りコロラド州アスペンで開催された。

(Die Boost-Community organisierte die BoostCon in den vergangenen Jahren, um die Entwicklung der Boost Bibliotheken voranzutreiben. Mit der Verabschiedung des neuen C++-Standards im vergangenen Jahr, in den viele Ideen der Boost-Community geflossen sind, hat sich nun der Schwerpunkt der Konferenz von den Boost Bibliotheken zu C++11 verschoben. Während sich der Name der Konferenz änderte, blieb der Veranstaltungsort derselbe: Die C++Now! fand wiederum in Aspen, Colorado, statt.)

会合の参加者はC++標準委員会の面々によるプレゼンを期待していた。例えばC++標準委員会メンバーであり、libstdc++のリードディヴェロッパであるHoward Hinnant氏によるRvalue-referenceのプレゼンである。氏はC++03からのreferenceは、新しい標準においてlvalue-referenceと改名され、C++11においてreferenceと言ったばあい、lvalueとrvalue両方を指すと述べた。氏は種々のexpressionとそれらの関係について示した。単なるreferenceにとどまらず、glvalueやxvalue、prvalueといった概念についても述べた。最後に、Hinnant氏は、関数の戻り値をrvalue referenceで定義する必要はない、というのも、より高速かつよりよい方法としてRVOがあるからだ、という実用的な助言で締めくくった。

(Die Konferenzteilnehmer konnten sich auf Präsentationen einiger Mitglieder des C++-Standardkomitees freuen. So stellte Howard Hinnant, Mitglied des C++-Standardkomitees und verantwortlicher Entwickler der Bibliothek libstdc++, Rvalue-Referenzen vor. Er erklärte zum Beispiel, dass Referenzen aus C++03 im neuen Standard in Lvalue-Referenzen umbenannt wurden und Referenzen in C++11 sowohl Lvalue- als auch Rvalue-Referenzen bedeuten. Er präsentierte verschiedene Arten von Ausdrücken und zeigte deren Zusammenhänge auf. So gibt es nicht nur Referenzen, sondern zum Beispiel auch glvalues, xvalues und prvalues. Schließlich gab Hinnant praktische Tipps, etwa Rückgabewerte von Funktionen nicht zwangsläufig als Rvalue-Referenz zu definieren, da es mit der RVO (Return Value Optimization) eine schnellere und bessere Möglichkeit gebe, Werte von Funktionen zurückzugeben.)

コンパイルを拒否するためのC++のmodule(Veto für modulares C++)

同じくC++標準委員会メンバーであるDavid Vandevoorde氏は、moduleに関する氏の意見について述べた。moduleはC++11に定義されなかったが、氏が将来のC++に搭載して欲しいと望んでいる機能である。moduleはdynamicでもstatic libraryでもなく、特に多くのヘッダファイルからなるプロジェクトのコンパイル速度向上に寄与する。

(David Vandevoorde, ebenfalls Mitglied des Standardkomitees, stellte seine Idee der Module vor. Module sind nicht in C++11 definiert, sollen aber nach Vandevoordes Willen Bestandteil einer zukünftigen Version von C++ sein. Bei Modulen handelt es sich nicht um dynamische oder statische Bibliotheken, vielmehr geht es um die Möglichkeit, die Kompilierung zu beschleunigen – vor allem, wenn ein Projekt aus vielen Header-Dateien besteht.)

Vandevoorde氏が提案したmoduleには、あるヘッダファイルに変更があっても毎回コンパイルされるわけではないというより高度なコンパイル済みヘッダファイルが含まれている。興味深いことに、clangはコンパイラ依存のmoduleをサポートしており、これは期待した通りの効果を発揮し、C++のコードを変更することなくコンパイル速度の向上に寄与している。

(Bei den von Vandevoorde vorgeschlagenen Modulen geht es eher um eine intelligente vorkompilierte Header-Datei, die nicht jedes Mal neu zu kompilieren ist, wenn sich eine Header-Datei ändert. Interessanterweise unterstützt Clang Compiler-abhängige Module, die den gewünschten Effekt haben und die Kompilierung beschleunigen können, ohne dass C++-Code zu ändern ist.)

MicrosoftでVisual Studioのプログラムマネジャーを務めるSumit Kumar氏は、プレリリース版のVisual Studio 11について述べ、開発環境の新機能について紹介した。例えば、プロジェクトは非同期的にロードできるようになるので、開発者は他のプロジェクトがバックグラウンドでIDEにロードされている間にコードを閲覧したり編集するといったことができるようになる。Visual Studio 11は内部の依存関係を示すグラフを自動的に生成することができる。例えば、実行ファイルやnamespace、classや関数の依存関係をグラフにして表示できる。

(Sumit Kumar, Programm-Manager bei Microsoft für Visual Studio, präsentierte eine Vorabversion von Visual Studio 11 und stellte einige der Neuheiten der Entwicklungsumgebung vor. Beispielsweise lassen sich mit der IDE Projekte asynchron laden, sodass ein Entwickler sofort mit dem Startprojekt arbeiten sowie Code ansehen und verändern kann, während andere Projekte noch im Hintergrund geladen werden. Visual Studio 11 kann automatisch Grafiken erstellen, die die internen Zusammenhänge einer Software erkennen lassen. Zum Beispiel kann man in einer Grafik in eine ausführbare Datei hineinzoomen, um Namensräume, Klassen oder Funktionen sichtbar zu machen.)

またKumar氏によると、Visual StudioのデバッガはMicrosoftが最適化に力を注いだ重要ツールである。開発者コミュニティに寄せられた批判を受け入れて、Microsoftは最終的にこのカンファレンスで紹介したVisual Studio11のプレリリース版よりもカラフルにするとのことである。

(Laut Kumar wird der Debugger in Visual Studio als eines der wichtigsten Werkzeuge erachtet, weswegen ihn Microsoft erkennbar optimiert hat. Zu guter Letzt hat sich Microsoft die Kritik seiner Entwickler-Community zu Herzen genommen, die Symbole in der IDE farbiger zu gestalten, auch wenn die auf der Konferenz vorgestellte Vorabversion von Visual Studio 11 noch eintönige Symbole besaß.)

より簡便なC++に向けて (Credo für einfacheres C++)

参加者に最も感銘を与えたキーノートは、一流の科学者でありAdobeのソフトウェアアーキテクトでもあるSean parent氏によるものではないろうか。氏はC++が今後進むべき道を示そうとした。氏は、たったふたつの値を保持するだけのデータ構造であるstd::pairの実装を例に、ヘッダファイルを上から下までスクロールして、C++はもっとシンプルになるべきだと述べた。また、C++を使う開発者が実際にコンピュータのパフォーマンスの恩恵を受けられるように、C++はGPUのような部品はもとより、コンピュータ全ての装置に標準でアクセスできる方法を提供すべきだとも述べた。threadについては、C++11で標準搭載されたので、C++開発者は、現在プロセッサの複数のコアにアクセスしてマルチスレッドアプリケーションを組むことができるようになった一方、GPUのようなパワフルな装置にアクセスするためのインターフェースがC++標準にない。

(Am meisten beeindruckt hat die Teilnehmer wahrscheinlich die Keynote von Sean Parent, Principal Scientist und Softwarearchitekt bei Adobe. Er versuchte eine Richtung vorzugeben, in der sich C++ weiterzuentwickeln habe. Seiner Meinung nach ist C++ drastisch zu vereinfachen. Beispielhaft zeigte er eine Implementierung von std::pair und scrollte die Header-Datei seitenlang nach unten, obwohl diese Datenstruktur letztlich lediglich zwei Werte speichert. Außerdem müsse C++ einen standardisierten Zugriff auf Komponenten wie die GPU bieten, damit Entwickler mit C++ tatsächlich von der Leistungsfähigkeit eines Computers – und zwar aller Komponenten eines Rechners – profitieren können. Während sich mit Threads, die mit C++11 Einzug in den Standard gehalten haben, nun Multithreaded-Anwendungen entwickeln lassen und sich somit auf mehrere Kerne eines Prozessors zugreifen lässt, haben C++-Entwickler derzeit keine Möglichkeit, über eine im C++-Standard definierte Schnittstelle auf leistungsfähige Komponenten wie die GPU zuzugreifen.)

C++Now!での他のプレゼンとして、Alisdair Meredith氏は、新しいC++標準の策定を通じて学んだことを話した。また、MicrosoftでC++標準ライブラリ開発に従事しているStephan t. Lavavej氏は、C++11の正規表現ライブラリについて発表した。Googleでclangチームを率いているChandler Carruth氏は、いかにclangがC++コードのリファクタリングの助けになるかについて語った。

(Weitere Vorträge der C++Now! hielten etwa Alisdair Meredith, der erklärte, welche Lehren das Komitee aus der Entwicklung des neuen C++-Standards gezogen hat, und Stephan T. Lavavej, der bei Microsoft für die C++-Standardbibliothek verantwortlich ist. Dieser gab eine Präsentation zu regulären Ausdrücken in C++11. Chandler Carruth, der das Clang-Team bei Google leitet, erklärte schließlich, wie Clang beim Refactoring von C++-Code helfen kann.)

およそ130名いた参加者でなくとも、全プレゼンの概要は閲覧可能である。ほとんどのプレゼン資料はpdfファイルでダウンロード可能だ。また、プレゼンのヴィデオはカンファレンスのWeb siteにて公開される。

(Wer nicht zu den rund 130 Konferenzteilnehmern zählte, kann auch unter eine Übersicht zu allen Vorträgen aufrufen. Für zahlreiche Präsentationen stehen PDF-Dateien zum Download zur Verfügung. Videos der Präsentationen werden in Kürze auf der Konferenz-Website veröffentlicht.)

次回のC++Now!は2013年5月12日から17日、Aspenにて開催される予定である。

(Die nächste C++Now! findet vom 12. bis 17. Mai 2013 wieder in Aspen statt.)

April 17, 2012
NASA shows off new algae farming technique for making biofuel

April 16, 2012
New 3D printing process could lead to DIY drugstores

March 23, 2012
標準のむかう道: The C++ Standard Library著者Nicolai Josuttisへのインタヴュー

source: http://www.informit.com/articles/printerfriendly.aspx?p=1846582


Danny Kalevが"The C++ Standard Library: A Tutorial and Reference, 2nd edition"の著者であるNicolai Josuttis氏に、氏の新著について、またC++11の好ましい点、好ましくない点について、そしてなぜC++が氏の最良の言語でないのかについてインタビューを行った。


Danny Kalev: The C++ Standard Library: A Tutorial and Reference の第一版は1999年、C++98として知られるC++の最初の標準規格が承認された次の年に出版されました。この第一版と、もうすぐ出版される第二版の主な違いについて教えてください。

Nicolai Josuttis: えっと、僕はC++11の標準化プロセスを追いかけていなかったんだ。2008年末、vectorやpairのようなクラスがC++98/03とどう変わったのか調べるべく僕は初めて新しい標準を見た。とても衝撃を受けたね。”いったいぜんたい、このクラス宣言にある && はどういう意味なんだ?” …理解するまでとても戸惑ったのを覚えているよ。さて、C++98との違いについて聞くなら、僕はまるっきり違うと答えるね。単純なプログラムの書きかたも、複雑なクラス定義も劇的に変わっている。例えば、C++11のpair<>は行数にして倍ほど増えている。

でも、変化の方向は正しいと思う。C++11はC++の力-パフォーマンス-に重点を置いている。だが、これはプログラマがよいクラスを設計する難易度が上がるという障害を伴う。…ふむ、そう困難でもなくなるかもしれない。プログラマが知るべきことをきちんと知っていれば。…だが、今や知るべきことをきちんと知るのは非常に難しい。ある意味で、C++11はまったく新しい言語で、僕の本は新しいC++プログラミングスタイルと新しいクラスを含めて、この変化を単に反映したものだと言えるかもしれない。

Danny: どうして第二版を書こうと思ったのですか?読者の要求があったからですか?それともC++11標準が出たからですか?

Nicolai: どっちもだよ。C++11をカヴァーするよう更新してほしい、というリクエストは常だ。

Danny: C++を主に使っていて、C++11の変化についていきたいと考えているプログラマにとって、C++11標準ライブラリをマスターする最良の方法はなんでしょうか?一般的なC++erにとって重要なC++標準ライブラリの特徴とはなんでしょうか?

Nicolai: 僕の本を買ってください;-) どの特徴が重要になるかは、プログラマと問題領域に依るね。もちろん、concurrencyやmultithreadingサポートという点にそそられるプログラマはきっと多いだろう。だけど他のプログラマは乱数生成(これはC++11標準の26.5節に記述されている)に興味があるかもしれない。一般的なプログラマにとって、最も重要な変更は多分言語コアにあるはずだ - 標準ライブラリの使い方を劇的に変えるようなものもあるからね。

Danny: templateは標準ライブラリの根幹です。containerやalgorithm、iteratorはtemplateの上に組み上げられてますし。では、多態や仮想関数、継承といった古典的なOOPの原理は、今日的C++ではもはや過去のものなのでしょうか?

Nicolai: ふむ、いくつか回答を思いついたよ。まずは、templateは(実行時ではなくコンパイル時の)多態に使えるよ、ということ。次に、C++は純粋オブジェクト指向言語でもなければ、純粋ジェネリック言語でもない、ということ。C++の真価はこの二つを組み合わせたときにこそ発揮されるのだ。…最後に、端的な回答を。”はい。そのとおり”。

Danny: では、C++の言語コアについてお話を聞かせてください。lambda expression の利点は、もしあるなら、いったい何でしょうか。この機能が一般的なプログラマのコードで広く使われるようになると思いますか?それとも主にC++ライブラリの中で使われるに止まると思いますか?lambda expressionが潜在的に持っている短所はありますか?

Nicolai: 僕がC++でプログラムを書くとき、もう二度とalgorithmと関数オブジェクトを組みあわせて使わないことを宣言するよ。でも公平を期していうなら、最近あんまりプログラミングしてないんだけど。それでもまぁ、僕はそんなに特殊な部類には入らないと思う。僕の意見では、lambdaが導入されてようやく、algorithmを使ったコードを高速かつ読みやすく書けるようになったと思う。これはすばらしいことだ。まぁ、lambdaには問題もあるね。(関数オブジェクトのように)lambdaは局所的な振舞いを規定するけれど、複数の呼び出しの間で状態を保持しない。この点では、関数オブジェクトの出番はまだあると言えるだろう。

Danny: rvalue referenceはC++11言語コアで一番大きな変更だと思います(この機能の詳細についてはこちらを参照のこと)。実際のアプリケーションでこの機能を使う機会はありましたか?使える機能だと思いましたか?この機能の利点はなんでしょう?

Nicolai: 申しわけない。このところ僕のプロジェクトではC++をあまり使っていないんだ。繰りかえしになるけど、僕のC++11の知識は第二版を書く中で身につけたものなんだ。でも、実際に試行して、機能についてはコミュニティのエキスパート達と議論を重ねてきた。だから、この本は初心者が書いたものじゃない。言わば、”熟練した初心者”が書いたものなんだ ;-)

さて、そういう訳で、新機能のなかでrvalue referenceが一番重要な変更かどうかは分からないよ。まぁC++の利点 - 例えばパフォーマンス -をさらに強化する鍵であるとは思う。しかしながら、この本のなかで、ムーヴセマンティクスとrvalue referenceが導入された動機と、機能の説明をするのがどれほど大変なことか分かっている。僕にとっての、C++の一番目を惹く変化は auto、range-based for、initializer listかな。これら三つの機能のおかげで、もうこの機能なしではやっていけないくらいに、小さな例示コードをとても明快に書けるようになった。

Danny: よくC++11は妥協の産物だと言われます。特定の機能(例えば、自動ガーベッジコレクタ、スレッドプール、ソケットライブラリ)は標準に載りませんでした - 標準策定期間が伸びてしまうので。あなたはこのC++11標準はよくできていると思いますか?2003年の時点で持っていた期待に沿うものですか?

Nicolai: この本を書き終えて、僕はC++11の大ファンだと胸をはって言えるよ。あわせて、これらの小さな単純化と改良は相当なものだとも。でもまた、C++を構成する機能とコンポーネントを使い熟すのは時として困難だ。そして、templateのインスタンシエーションにまつわるエラーメッセージはいまだに大問題だ。(Danny註:templateまわりのエラーメッセージは解読するのが骨である) 思うに、標準委員会は単純化にそれほど気を払っていないようだ。ええと、これは標準化作業に関わっている人への苦情ではないよ。彼らは信じられないほど大変な仕事を、よくやってのけていると思う。C++では、メンテナンス中のすべてのソフトウェアで彼らの成果を感じることができるはずだ。多くの問題は、後方互換性を維持することに由来する不均質性に起因しているからね。

Danny: C++11にはなにが欠けていると思いますか? どんな機能やライブラリが次のC++標準で取り入れられて欲しいですか

Nicolai: ファイルシステムがないのは痛いね。あとUTF8やUTF16、Unicodeのようなキャラクタセットのサポートもまだまだかな(本のなかで、UTF8からUTF16に変換する例があるけれど、この手のものがほとんどない)。コンセプトがないのはべつに問題ないね。実際、コンセプトがC++11規格から外れる前から、第二版ではコンセプトを出さないようにしようと思っていたし。(註:コンセプトについて、議論の流れや標準から外すに至った経緯についてより詳細を知りたければこちらをご覧ください)

Danny: ではあなた自身についてもうすこしお話しください。あなたは長年C++標準委員会のメンバーを務めていらっしゃいます。C++11の標準化に際して、どの方面で主に活動されましたか?一緒に設計したライブラリはありますか?

Nicolai: 僕は一流のエキスパートじゃないんだ。多分、委員会のほとんどのメンバーのほうが僕よりC++に詳しいんじゃないかな。僕は主により適切なものになるように質問したり対処したりだよ。C++98のときも、C++11のときも、後半になって飛び込んで、それから、重点的に、矛盾点がないかないかどうか見たり、欠陥を修正したり、動作を明確にしたりしていたね。僕が出した唯一のものはarray<> クラステンプレートの初期実装かな。でもあれもBjarne Stroustrup氏の”The C++ Programming Language"にあるc_arrayクラスの翻案だけどね。

Danny: 近年、C++に対する関心がまた高まってきているように思います(“C++ルネッサンス”とか言われている)。これは、プログラミング界がC++以外の言語の見果てぬ夢から醒めたのでしょうか。それとも単にC++自体が良くなったのでしょうか? あと、これは少し個人的なことになりますけれど、好きな言語はありますか? 最近どんなプロジェクトに関与しましたか?

Nicolai: プログラミングは僕の今の仕事にはあまり重要ではない。SOA(Service Oriented Architecture)とシステムインテグレーションのエキスパートとして、ここのところ僕の主な関心事は、多数の企業間、および、多数のシステム内で分散した動作(“ビジネスプロセス”)を実現する技術や方法を確立することでプロジェクトや企業をサポートすることだ(註:SOAについてのより詳細はこちら)。ゆえに、僕のトピックはシステム開発というよりシステムランドスケープのメンテナンスだね。

C++は実は僕の好きな言語ではない。まぁ実際のところ、好きな言語なんてないんだけど。僕とプログラミング言語の最後の格闘は15年前になるね。さて、質問はどの問題をどんなコンテキストで解決しているのか?ということでいいのかな。コンテキストに応じて、主にawkとsedがあるUnix shell script、Java、C++、Excelなんかで動作を”プログラミング”しているね(註:Excelはプログラミング言語ではないと思われるかもしれないが、僕の考えでは、特定の問題については、プログラミング言語としての力量を備えていると思う)。

もしパフォーマンスが本質的な問題なら、C++以外の手はないと思うね。でも、この言語をつかうためには、高いコストを払う必要がある。C++が複雑であるがゆえに、他の言語に比べて普通のプログラマに対するサポートが信じられないほど悪い。これこそがC++の欠点だ。僕の本が売れるのはこれのお陰と言ってもいいんだけど。C++は仕事を生むと言える。これは今のご時勢ありがたいことだね。

March 21, 2012
出張報告書:2012/2 C++ Standard Meeting

source: http://herbsutter.com/2012/03/08/trip-report-february-2012-c-standards-meeting/

2012年春のISO/IEC JTC1/SC22/WG21 (C++)会議が、アメリカ合衆国ハワイ州コナにて2/6-10の日程で開催された。

主要な成果としては、業会からのC++への注目がより増してきているので、今年はたいへんな年になるだろう、ということだ。これはC++にとっていいニュースだけど。

それでは、会議のハイライトを紹介していこう。

参加者

これはC++11標準が完成して二度目の会合になる。C++98を完成させた後のように、業界の動向を注視すべくしばらく”静かに”しているなら尚更のことだが、標準が発布されてからしばらくは参加者が少くなるのが常である。

だが、以下をご覧になれば分かるとおもうが、標準を発布してから、C++標準委員会は減速するどころか加速したのは明らかだ。これは特筆すべき参加人数に顕著にあらわれている。なんと73人ものエキスパートがこの会合に参加したのである。その中にはGoogleやIntel、Microsoft、NVidiaといった企業からの参加者も多数含まれる。これは、次期標準の策定計画や、それに先駆けて提出された論文に対する関心の高さのあらわれであろう。

全速前進! part 1: C++1y

最も大きな議題は、C++11の”次を何にする”か決めることだった。 月曜の朝っぱらから、WG21はすぐにISO C++ Standardの新しい版 -これには言語とlibraryの拡張が含まれる- に向けて作業を開始し、この新しい”C++1y”標準の完成をおおよそ5年後と見積ることで一致した。よって、yは7になる見込みだが、これは現時点では大まかな見積りに過ぎない。

できるだけyを7にするには、C++1yでは、提案されている数だけでももうキャパオーバーしている、メジャーな言語機能の追加を最大一件に絞ることだ。 evolution working group (EWG)の議長はいつもどおりBjarne Stroustrupが務めたが、このため今回取りあげる主要な提案の概観を調査するのに何週間もかかってしまい、その結果、今回はconcurrencyとparalleismからmoduleとstatic ifまでのプレゼンテーションが行われた。

今回の会合ではyes/no二択での採択は取らないようにし、ほとんどの提案については次の10月に行なわれる総会で、より洗練した提案を再度取りあげることにした。よって、次の会合から、C++1yに許された時間のなかで検討すべき提案について、採択するかしないかを検討することになるだろう。 次のWG21総会までに、提案のいくつか(特にconcurrency/parallelismアプローチとmoduleシステムの提案)について、この議題をより深めるために、小規模の会合を追加で夏に開催することが決まった。詳細は以下に述べる。

全速前進! part 2: ライブラリの数々

先のレポートで述べたように、library working group (LWG)はもうすでに提案の募集と標準ライブラリの追加作業を継続することが決まっている。 Kona会合ではまだライブラリ拡張のチャンスがあることを確認し、LWGの議長であるAlisdair Meredithはthis call for library proposals を議題にあげた。

しかし、これらのライブラリはなにもC++1yのためだけではない。というのは、LWGは迅速に成果を提供すべく小さな単位/小さなサイズで新しいライブラリを導入するつもりだからだ。FileSystemやNetworkingのようなライブラリは、非常に大きいので、C++1yとは分けて(註: C++1yより早い段階で)Technical Specification(TS)の形式で発布できる程度に独立した部品として発布する。(語註: TSとはISO用語で、最初のライブラリ拡張であるTRのように、我々がType 2 Technical Report(TR)と呼んでいたものである)

これら分割した部分については別々に作業をすすめ、完全にWG21 LWGへ移行できる段階に達したとき、LWGがその進捗を管理することになるだろう。おそらくはC++1yの国際標準(IS)の一部として、もしくは単体のTS、ISとして。

研究部会

作業全体を加速するべく、また並行して作業をすすめられるように、今回初めて4つのWG21公式研究部会(SG: Study Group)を設立した。これはWG21総会の間に独立して会合を開催し、作業をすすめる権限をもつ。 これらの作業部会は提案をより早く進捗/洗練することが目的だが、TSやISとして発行される前に、さらに洗練の度合いを高めて承認を受けるために、通例どおりWG21にかけられることになるだろう。

4つの研究部会と議長は下記のとおり。

  • SG1: 並列、並行 (Hans Boehm)
  • SG2: モジュール (Doug Gregor)
  • SG3: FileSystem (Beman Dawes)
  • SG4: Networking (Kyle Kloepper)
おそらく一回以上はSGの会合が開催されることになるだろう。いまのところ、僕が把握している範囲では以下の通りである:
  • 2012年5月の第二週:アメリカ合衆国ワシントン州ベルビュー(SG1 と SG4)
  • (必要ならば) 2012年夏:カナダ トロント(SG1)

今後の予定

研究部会の会合を除くと、ISO C++ standards committee総会の開催予定日ならびに開催予定場所は以下の通りである:

  • 2012/10/15-19: アメリカ合衆国オレゴン州ポートランド
  • 2013/4/15-20: イギリス ブリストル

March 19, 2012
Teaching fat cells to burn calories: New target against obesity involves brown fat

February 15, 2012
thunderbirdのメール折り返し文字数指定 - ゆどうふろぐ

February 6, 2012
Amazon fungi found that eat polyurethane, even without oxygen

Liked posts on Tumblr: More liked posts »