評価済みスロット
評価済みスロットは、通常プリプロセッサにより行われる遅延評価を避けつつ汎整数定数式を完全に評価するためのツールである。
チュートリアル
このメカニズムの使い方を理解するために、簡単なファイル繰り返しの例から始める。
以下のような場合を考えよう:
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < i; ++j) {
// ... i と j を使う
}
}
これは、以下のような多次元ファイル繰り返しの実行時モデルの例である:
// file.hpp
#if !BOOST_PP_IS_ITERATING
#ifndef FILE_HPP_
#define FILE_HPP_
#include <boost/preprocessor/iteration/iterate.hpp>
#define BOOST_PP_ITERATION_PARAMS_1 (3, (0, 9, "file.hpp"))
#include BOOST_PP_ITERATE()
#endif // FILE_HPP_
#elif BOOST_PP_ITERATION_DEPTH() == 1
#define I BOOST_PP_ITERATION()
#define BOOST_PP_ITERATION_PARAMS_2 (3, (0, I, "file.hpp"))
#include BOOST_PP_ITERATE()
#undef I
#elif BOOST_PP_ITERATION_DEPTH() == 2
#define J BOOST_PP_ITERATION()
// I と J を使う
#undef J
#endif
このコードには問題がある。
記述者は、I がひとつ前の次元のイテレーションを指していることを期待したのであろうが、そうはならない。
ユーザーが I を参照したとき、それは実際には BOOST_PP_ITERATION() を参照しているのであって、
定義した時点での BOOST_PP_ITERATION() の値を参照しているのではない。
しかも、それは J が指しているのとまったく同じ値を指しているのである。
問題となっているのは、プリプロセッサが常にすべてを遅延評価していることである。
その解決には、そこで評価されたI が必要なのである:
// ...
#elif BOOST_PP_ITERATION_DEPTH() == 1
#define I BOOST_PP_ITERATION()
// ...
幸運にも、ライブラリはそれを行うためのメカニズム、評価済みスロットを提供する。
以下のコードでは先の例の修正として本メカニズムを使用した:
// ...
#elif BOOST_PP_ITERATION_DEPTH() == 1
#define BOOST_PP_VALUE BOOST_PP_ITERATION()
#include BOOST_PP_ASSIGN_SLOT(1)
#define I BOOST_PP_SLOT(1)
// ...
評価済みスロットへの代入には二つのステップがある。
まず、ユーザーは名前付けされた外部引数、BOOST_PP_VALUE を定義しなければならない。
この値は汎整数定数式でなければならない。
次に、ユーザーは BOOST_PP_ASSIGN_SLOT(x) をインクルードしなければならない。
ここで x は、代入を行うスロット番号である(1 から BOOST_PP_LIMIT_SLOT_COUNT まで)。
これにより BOOST_PP_VALUE を評価し、番号 x のスロットにその結果を代入するだろう。
スロットの値を取り出すには、ユーザーは BOOST_PP_SLOT(x) を使わなければならない。
上記のケースでは、I は依然として遅延評価されている。
しかし、I は今では BOOST_PP_SLOT(1) を評価する。
この値は BOOST_PP_ASSIGN_SLOT(1) が後で呼ばれない限り、変化しないだろう。
上級テクニック
スロットメカニズムは計算を行うこともできる。
#include <iostream>
#include <boost/preprocessor/slot/slot.hpp>
#include <boost/preprocessor/stringize.hpp>
#define X() 4
#define BOOST_PP_VALUE 1 + 2 + 3 + X()
#include BOOST_PP_ASSIGN_SLOT(1)
#undef X
int main(void) {
std::cout
<< BOOST_PP_STRINGIZE(BOOST_PP_SLOT(1))
<< &std::endl;
return 0;
}
突き詰めると、プリプロセッサの #if(または #elif)ディレクティブで評価可能なものは、defined オペレータを除いてすべて利用可能である。
スロットへの代入時に、そのスロット自身を使用することもできる:
#define BOOST_PP_VALUE 20
#include BOOST_PP_ASSIGN_SLOT(1)
#define BOOST_PP_VALUE 2 * BOOST_PP_SLOT(1)
#include BOOST_PP_ASSIGN_SLOT(1)
BOOST_PP_SLOT(1) // 40
参照
- Paul Mensonides