ファイルイテレーション
ファイルイテレーションは複雑だがパワフルな垂直反復を構成するものである。
それはファイルをユーザが指定した範囲の各数値について繰り返しincludeする。
チュートリアル
このメカニズムは動作させるのに二つの情報を必要とする。
どの範囲をイテレートするかという情報と、includeするべきファイル名である。
オプションとして、同一のファイルに複数のイテレーションを行う際に区別するための第三の情報を取ることができる。
これらの情報は一つまたは二つの名前付き外部変数の仕組みによって取得される。
これらの引数はBOOST_PP_INTERATION_PARAMS_xという名前のユーザ定義マクロか、あるいはBOOST_PP_FILENAME_xとBOOST_PP_ITERATION_LIMITSの二つの組み合わせによって指定される。
BOOST_PP_ITERATION_LIMITSはイテレートする値の範囲を指定する。
それは下限と上限をあらわす二つの要素を含む組へと展開されるものでなければならない。
下限と上限はともに0からBOOST_PP_LIMIT_ITERATIONの範囲内の値でなければならない。
例えば、もしユーザが0から10までの範囲でファイルをincludeしたいならば、
BOOST_PP_ITERATION_LIMITSは次のようになる:
#define BOOST_PP_ITERATION_LIMITS (0, 10)
マクロの名前の直後に空白があることに注意せよ。
このマクロは二つの引数を取らない。
上の例では、もし空白がなければ、0と10は不正な識別子であるとしてプリプロセッサがエラーを返す。
BOOST_PP_ITERATION_LIMITSマクロで指定される下限と上限はともに評価されるパラメータである。
したがってそれらは単純な算術的あるいは論理的な式でもよい。
たとえば、上の定義は次のようにも書ける:
#define N() 5
#define BOOST_PP_ITERATION_LIMITS (0, N() + 5)
このために、もしマクロの後ろの空白が無ければ、この定義は文法的に不正なものになってしまうことがありうる:
#define A 0
#define B 10
#define BOOST_PP_ITERATION_LIMITS(A, B)
// 注意: 空白が無い ^
もしこれが起これば、このマクロを利用するメカニズムの内側でエラーが起こるだろう。
(その場合の)エラーメッセージは意味不明のものとなるかもしれないので、常に空白をいれることを忘れてはならない。
上の例の正しいバージョンは次のようになる:
#define A 0
#define B 10
#define BOOST_PP_ITERATION_LIMITS (A, B)
// 注意: 空白がある ^
BOOST_PP_FILENAME_xはイテレーションの対象となるファイル名を指定する。
xはイテレーションの次元である。
(以下我々はこれを1(すなわち第一次元)とみなす。よって実際には我々はBOOST_PP_FILENAME_1について考える。)
このマクロは有効なファイル名へと展開されなければならない。引用記号などはどのようにファイルをアクセスするかによってかわってくる:
#define BOOST_PP_FILENAME_1 "file.h"
// -or-
#define BOOST_PP_FILENAME_1 <file.h>
あとはこのメカニズムを呼び出すだけでファイルイテレーションが実行される:
??=include BOOST_PP_ITERATE()
(トークン??=
は、#
へと置換されるトライグラフである。
私はマクロの定義や展開するのではなくincludeするのであることをはっきりさせるためにトライグラフを使ったが、別にそれは必須ではない。
ダイグラフ%:
も使うことができる。
いくつかのコンパイラはトライグラフやダイグラフを受け付けないことを憶えていてほしい。
その場合以外では、好きなものを使ってよい。)
したがって、もし"file.h"を1から10までイテレートしたいなら、次のようなものを組み合わせればよい:
#define BOOST_PP_ITERATION_LIMITS (1, 10)
#define BOOST_PP_FILENAME_1 "file.h"
??=include BOOST_PP_ITERATE()
上のコードは"file.h"のincludeを10回繰り返す効果がある。
別の方法として、イテレートの範囲とファイルの両方を一つのマクロBOOST_PP_ITERATION_PARAMS_xで表現することもできる。
やはりxは次元で、ここでは我々はそれを1とする。
このマクロは順に、下限、上限、ファイル名、そしてオプションからなる配列へと展開されるものでなければならない。
#define BOOST_PP_ITERATION_PARAMS_1 (3, (1, 10, "file.h"))
??=include BOOST_PP_ITERATE()
これは前のバージョンと全く同じ動作をする。
一度に二つのパラメータ指定方法のいずれか一方だけを使うことができる。
(その理由は、後で述べるように、二つの異なった次元抽象化の方法があるからである。)
現実的には同じファイルを10回includeしたところであまり役に立たない。
違いは、マクロの状態が各々の回で変化することである。
例えば、イテレートの値はBOOST_PP_ITERATION()マクロで取得できる。
もし"file.h"が次のように定義されているなら...
// file.h
template<> struct sample<BOOST_PP_ITERATION()> { };
...それは次のようにイテレートされ...
template<int> struct sample;
#define BOOST_PP_ITERATION_PARAMS_1 (3, (1, 5, "file.h"))
??=include BOOST_PP_ITERATE()
...その結果は各々の回で異なる:
template<> struct sample<1> { };
template<> struct sample<2> { };
template<> struct sample<3> { };
template<> struct sample<4> { };
template<> struct sample<5> { };
ファイルがそれ自身の上でイテレートできない理由はない。
これはコードをまとめる利点がある。
問題は、ファイルの通常の部分とイテレートされる部分を区別しなければならないことだ。
このライブラリはそのためにBOOST_PP_IS_ITERATINGマクロを提供している。
このマクロはイテレーション中ならば1に定義される。
例えば、"file.h"の中身を、それをイテレートするファイルの中に混ぜ込むには:
// sample.h
#if !BOOST_PP_IS_ITERATING
#ifndef SAMPLE_H
#define SAMPLE_H
#include <boost/preprocessor/iteration/iterate.hpp>
template<int> struct sample;
#define BOOST_PP_ITERATION_PARAMS_1 (3, (1, 5, "sample.h"))
??=include BOOST_PP_ITERATE()
#endif // SAMPLE_H
#else
template<> struct sample<BOOST_PP_ITERATION()> { };
#endif
このように同じファイルを使うことで、別の問題が起きる。
もしファイルが自分自身の上で二つの別々のファイルイテレーションをおこなったら何が起きるだろうか?
これのためにオプションフラグパラメータが存在する。
それは別々のイテレーションを区別するものである。
// sample.h
#if !BOOST_PP_IS_ITERATING
#ifndef SAMPLE_H
#define SAMPLE_H
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_shifted_params.hpp>
template<int> struct sample;
#define BOOST_PP_ITERATION_PARAMS_1 (4, (1, 5, "sample.h", 1))
??=include BOOST_PP_ITERATE()
template<class T, class U> struct typelist_t {
typedef T head;
typedef U tail;
};
template<int> struct typelist;
struct null_t;
template<> struct typelist<1> {
template<class T0> struct args {
typedef typelist_t<T0, null_t> type;
};
};
#ifndef TYPELIST_MAX
#define TYPELIST_MAX 50
#endif
#define BOOST_PP_ITERATION_PARAMS_1 (4, (2, TYPELIST_MAX, "sample.h", 2))
??=include BOOST_PP_ITERATE()
#endif // SAMPLE_H
#elif BOOST_PP_ITERATION_FLAGS() == 1
template<> struct sample<BOOST_PP_ITERATION()> { };
#elif BOOST_PP_ITERATION_FLAGS() == 2
#define N BOOST_PP_ITERATION()
template<> struct typelist<N> {
template<BOOST_PP_ENUM_PARAMS(N, class T)> struct args {
typedef typelist_t<
T0,
typename typelist<N - 1>::args<BOOST_PP_ENUM_SHIFTED_PARAMS(N, T)>::type
> type;
};
};
#undef N
#endif
"flags"パラメータを使っているところに注意せよ(それはBOOST_PP_ITERATION_FLAGS()を使ってアクセスされる)。
それはsample
イテレーションと、typelistを線形化するイテレーションを区別している。
二つ目のイテレーションはファイルイテレーションの仕組みの威力をあらわしている。
それはtypelist<3>::args<int, double, char>::type
のような形をしたtypelistの線形化を生成している。
ほかのイテレーションを使ってこのtypelistの例を完全に線形化することができる...
// extract.h
#if !BOOST_PP_IS_ITERATING
#ifndef EXTRACT_H
#define EXTRACT_H
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
// "void"のようないくつかの特定の型は関数引数の型には使えない
template<class T> struct incomplete {
typedef T type;
};
template<class T> struct strip_incomplete {
typedef T type;
};
template<class T> struct strip_incomplete<incomplete<T> > {
typedef T type;
};
template<template<int> class output, class func_t> struct extract;
#ifndef EXTRACT_MAX
#define EXTRACT_MAX 50
#endif
#define BOOST_PP_ITERATION_PARAMS_1 (3, (1, EXTRACT_MAX, "extract.h"))
??=include BOOST_PP_ITERATE()
#endif // EXTRACT_H
#else
#define N BOOST_PP_ITERATION()
#define STRIP(z, n, _) \
typename strip_incomplete<T ## n>::type \
/**/
template<template<int> class output, class R BOOST_PP_ENUM_TRAILING_PARAMS(N, class T)>
struct extract<R (BOOST_PP_ENUM_PARAMS(N, T))> {
typedef typename output<N>::template args<BOOST_PP_ENUM(N, STRIP, nil)>::type type;
};
#undef STRIP
#undef N
#endif
この仕事を完了させるためにヘルパーマクロを定義する:
#define TYPELIST(args) extract<typelist, void args>::type
typedef TYPELIST((int, double, incomplete<void>)) xyz;
これについて二つの小さな警告がある。
まず、特定の型、たとえばvoid
のように、関数引数の型に使えないものは、incomplete<T>
で包んでやる必要がある。
もう一つは、二重の括弧はうんざりさせられる。
もしC++でC99規格の可変長引数マクロを使えるなら、TYPELIST
は次のように定義できる:
#define TYPELIST(...) extract<typelist, void (__VA_ARGS__)>::type
typedef TYPELIST(int, double, short) xyz;
下限と上限の値はイテレーションの内側でもBOOST_PP_ITERATION_START()とBOOST_PP_ITERATION_FINISH()を使ってアクセスできることに注意せよ。
これらの例と説明がファイルイテレーションの威力を明らかにしたことを望む。
しかしこれは単なるはじまりにすぎない。
このファイルイテレーションの仕組みは、多次元のイテレーションをサポートする。
多次元
このファイルイテレーションの仕組みは、BOOST_PP_LIMIT_ITERATION_DIMまでの次元をサポートする。
1次元(一番外側)はすでに上の例で使った。
2番目の次元を使うためには、単にプレースホルダーxの値を1から2に置き換えればよい。
#define BOOST_PP_ITERATION_PARAMS_2 /* ... */
^
...あるいは...
#define BOOST_PP_FILENAME_2 /* ... */
^
各次元は1からはじまり順番通りに使わなければならない。
したがって、上の例は最初の次元の内側でのみ有効である。
この時点では、BOOST_PP_ITERATION、BOOST_PP_ITERATION_START、BOOST_PP_ITERATION_FINISHに関してこれ以上の説明は必要無い。
BOOST_PP_ITERATION()は全体の次元にかかわらず現在の次元の値へ展開される。
同様に、BOOST_PP_ITERATION_START()とBOOST_PP_ITERATION_FINISH()は現在のイテレーションの下限と上限へと展開される。
次の疑似コードを参照せよ:
for (int i = start(1); i <= finish(1); ++i) {
// A
for (int j = start(2); j <= finish(2); ++j) {
// B
}
// C
}
Aの箇所においてはBOOST_PP_ITERATION()はi
を参照する。
BOOST_PP_ITERATION_START()とBOOST_PP_ITERATION_FINISH()はstart(1)
とfinish(1)
をそれぞれ参照する。
しかしBの箇所では、BOOST_PP_ITERATION()はj
--すなわちその箇所での現在の値--を参照する。
同様にBOOST_PP_ITERATION_START()はstart(2)
を参照し、以下同様である。
もし各次元で別々のファイルを使用する場合は、特に問題は起きないし、また多次元の使用は簡単なことである。
しかしながら、2次元以上を同じファイルで使うのは、他の次元と区別されなければならない。
ファイルイテレーションの仕組みは、BOOST_PP_ITERATION_DEPTHをその目的で提供している:
// file.h
#if !BOOST_PP_IS_ITERATING
#ifndef FILE_H
#define FILE_H
#include <boost/preprocessor/iteration/iterate.hpp>
#define BOOST_PP_ITERATION_PARAMS_1 (3, (1, 2, "file.h"))
??=include BOOST_PP_ITERATE()
#endif // FILE_H
#elif BOOST_PP_ITERATION_DEPTH() == 1
// A
+ BOOST_PP_ITERATION()
#define BOOST_PP_ITERATION_PARAMS_2 (3, (1, 2, "file.h"))
??=include BOOST_PP_ITERATE()
// C
#elif BOOST_PP_ITERATION_DEPTH() == 2
// B
- BOOST_PP_ITERATION()
#endif
これは次のような結果を生じる:
多次元はまた別の疑問を生む。
どのようにして現在の次元以外の次元の状態を得ることができるだろうか?
いいかえると、どのようにしてBの箇所でi
をアクセスすることができるだろうか?
プリプロセッサの遅延評価のために、これは動作しない...
// ...
#elif BOOST_PP_ITERATION_DEPTH() == 1
#define I BOOST_PP_ITERATION()
#define BOOST_PP_ITERATION_PARAMS_2 (3, (1, 2, "file.h"))
??=include BOOST_PP_ITERATE()
#undef I
#elif BOOST_PP_ITERATION_DEPTH() == 2
#define J BOOST_PP_ITERATION()
// use I and J
#undef I
#endif
ここでの問題は、I
はBOOST_PP_ITERATION()を参照しているのであって、I
の定義の箇所でのBOOST_PP_ITERATION()の値を参照しているわけではないことだ。
このライブラリはこれらの値を参照するのに二つの手段--絶対的と相対的--を用意している。
前者は特定のイテレーションフレーム(次元)をアクセスする。
どこの次元からでも最初の次元にアクセスするには、BOOST_PP_FRAME_ITERATION(1)を使う。
どこの次元からでも二番目の次元にアクセスするには、BOOST_PP_FRAME_ITERATION(2)を使う。以下同様である。
フレーム版の下限、上限、フラグをアクセスする方法も存在する。
それぞれBOOST_PP_FRAME_START、BOOST_PP_FRAME_FINISH、BOOST_PP_FRAME_FLAGSである。
そういうわけで、前の例を修正するために、I
の定義を変更する....
// ...
#elif BOOST_PP_ITERATION_DEPTH() == 1
#define I BOOST_PP_FRAME_ITERATION(1)
// ...
このライブラリは現在の次元から相対的に別の次元(すなわち前の次元)をアクセスするマクロも提供している。
それらのマクロへの引数は現在のフレームからのオフセットとして解釈される。
例えば、BOOST_PP_RELATIVE_ITERATION(1)は常に現在の直前の外側の次元を参照する。
引数0はオフセットとして解釈されるため、BOOST_PP_RELATIVE_ITERATION(0)はBOOST_PP_ITERATION()に常に等しい。
BOOST_PP_RELATIVE_ITERATION(2)は現在の次元の直前のさらに一つ前の次元を参照する。
下限と上限は同様にBOOST_PP_RELATIVE_STARTとBOOST_PP_RELATIVE_FINISHでそれぞれアクセスできる。
相対的次元のフラグはBOOST_PP_RELATIVE_FLAGSでアクセスできる。
相対性
私は前に、わけあってパラメータ化のメカニズムに二つの方法があることを述べた。
その理由は次元の抽象化にある。
いくつかの状況においてコードからはイテレートされている次元がわからない場合がある。それはおそらく複数の異なる次元で同じコードが再利用されるために起こる。
もしコードがもう一度イテレートされるなら、その次元に基づいた正しいパラメータを与える必要がある。
このメカニズムによって保持されるマクロの状態は、次元の値に対して相対的に参照することができる。
これがBOOST_PP_RELATIVE_アクセッサ群が目的とするものである。
同様に、ユーザ定義の名前付き外部引数は同じように定義することができる--ただしイテレートするファイルの名前を除いては。
下限と上限はこのメカニズムによって評価されるので、実装にはもはやBOOST_PP_ITERATION_LIMITS必要無く、識別子はイテレーションの各次元で再利用することができる。
残念ながら、ファイル名だけはそうはいかない。
ライブラリは引用符(あるいはアングルブラケット)の付いた文字列を評価する方法を持たない。
したがって、各次元には別々のマクロを使う必要がある。
これがBOOST_PP_FILENAME_xマクロの存在理由である。
これらはメカニズムによって抽象化できない断片だけを孤立化させるために存在する。
ファイル名を抽象化した流儀で定義するには、次のようなことをする必要がある:
#define UNIQUE_TO_FILE "some_file.h"
#if BOOST_PP_ITERATION_DEPTH() == 0
#define BOOST_PP_FILENAME_1 UNIQUE_TO_FILE
#elif BOOST_PP_ITERATION_DEPTH() == 1
#define BOOST_PP_FILENAME_2 UNIQUE_TO_FILE
#elif BOOST_PP_ITERATION_DEPTH() == 2
#define BOOST_PP_FILENAME_3 UNIQUE_TO_FILE
// ... BOOST_PP_LIMIT_ITERATION_DIMまでくりかえし
#endif
意図するところは、これをファイル名以外では行わないですむようにすることである。
もしこれが一つのファイルの中で複数回行う必要がある(BOOST_PP_FILENAME_xは使用されたら自動的にundefされる)なら、
適切な定義をするために別のファイルを使うことを考えよ:
# // detail/define_file_h.h
# ifndef FILE_H
# error FILE_H is not defined
# endif
#
# if BOOST_PP_ITERATION_DEPTH() == 0
# define BOOST_PP_FILENAME_1 FILE_H
# elif BOOST_PP_ITERATION_DEPTH() == 1
# define BOOST_PP_FILENAME_2 FILE_H
# elif BOOST_PP_ITERATION_DEPTH() == 2
# define BOOST_PP_FILENAME_3 FILE_H
# elif BOOST_PP_ITERATION_DEPTH() == 3
# define BOOST_PP_FILENAME_4 FILE_H
# elif BOOST_PP_ITERATION_DEPTH() == 4
# define BOOST_PP_FILENAME_5 FILE_H
# else
# error unsupported iteration dimension
# endif
そしてこのように使う....
// file.h
#if !BOOST_PP_IS_ITERATING
#ifndef FILE_H
#define FILE_H "file.h"
#define BOOST_PP_ITERATION_LIMITS (1, 10)
#include "detail/define_file_h.h"
??=include BOOST_PP_ITERATE()
#endif // FILE_H
#else
// iterated portion
#endif
このような少しの骨折りによって、コード肥大を起こさずに抽象化を維持することが可能になる。
残念ながら、これは各ユニークなファイル名について行う必要のある完全に一般的な解決方法ではないが、何も無いよりはましである。
結論
これでこのメカニズムが提供するものについてほぼすべて言及した。
このメカニズムの完全な利用法を説明するために、これらを利用してfunction_traits
テンプレートを実装してみよう。
Function Traits - An Involved Example
function_traits
テンプレートメタ関数の実装には、ファイルイテレーションの主要な各部分を使う必要がある。
(この例はコンパイラの問題を回避したりすることをしていない。メカニズムを説明する目的のためだけに存在する。)
結果は次のような機能を持つべきである:
- 返り値型
- 引数の個数と型
- 型が関数へのポインタであるか、関数への参照であるか、メンバ関数へのポインタであるか、あるいは単なる関数型であるか
- 型(関数)が省略記号を持っているか
- もしメンバ関数へのポインタでないなら、等価な関数へのポインタ、関数への参照、関数型
- もしメンバ関数へのポインタなら、どのクラスのメンバ関数であるか、constやvolatile装飾されているか
これを実装する方法は無数にある。
下の実装の中で何が起こっているかの簡単な概要を述べる。
実装は本質的には、関数のアリティ(引数の個数)を扱わなければならない。
したがって、最低限、我々は関数引数の個数についてのイテレートを使ってテンプレートfunction_traits
の部分的特殊化を定義しなければならない。
この状況は可変長引数関数(省略記号を持った関数)によって一層込み入ったものになる。
したがって、各アリティについて、可変長バージョンも必要となる。
またメンバ関数へのポインタについても扱わなければならない。
これによって、引数の個数や可変長引数だけでなく、constやvolatile装飾についても取扱わなければならなくなる。
わかりやすくするため、下の実装では関数型とメンバ関数へのポインタ型を別々に取扱っている。
これは混ぜることも出来るが、そうすればその結果はかなり複雑になるだろう。
関数型を取扱うために、この実装は関数の引数の個数についてイテレートしている。
各個数について、各々をアクセスするために、各パラメータについてイテレートしている。
さらにそれは再び自身をincludeして、同じ個数の引数を持つ可変長引数の特殊化を定義している。
それは大雑把に言って次の疑似コードと同等のことをおこなう:
void make_spec(int i, bool variadic) {
:open function_traits<i, variadic>
for (int j = 0; j < i; ++j) {
:parameter<j>
}
:close
if (!variadic) {
make_spec(i, true);
}
return;
}
void function_types(int max_arity) {
for (int i = 0; i <= max_arity; ++i) {
make_spec(i, false);
}
return;
}
メンバ関数へのポインタを少し違った方法でおこなわれる。
まず、constやvolatile装飾に関してイテレートする。
constやvolatileの各装飾において、関数の引数の個数に関してイテレートする。
各引数個数において、各パラメータに関してイテレートする。
さらにそれは自身を再びincludeして、可変長引数の特殊化を定義する...
void make_spec(int j, const char* cv, bool variadic) {
:open function_traits<j, cv, variadic>
for (int k = 0; k < j; ++k) {
parameter<k>
}
:close
if (!variadic) {
make_spec(j, cv, true);
}
return;
}
void gen_arities(const char* cv, int max_arity) {
for (int j = 0; j <= max_arity; ++j) {
make_spec(j, cv, false);
}
return;
}
void pointers_to_members(int max_arity) {
static const char* cv_qualifiers[] = { "", "const", "volatile", "const volatile" };
for (int i = 0; i < 4; ++i) {
gen_arities(cv_qualifiers[i], max_arity);
}
return;
}
以下が完全な実装である。
この例はファイルイテレーションのメカニズムだけでなくライブラリ自身の威力を示しているため、もしメカニズムを完全に理解したいならば慎重にたどること...
// function_traits.hpp
#if !BOOST_PP_IS_ITERATING
#ifndef FUNCTION_TRAITS_HPP
#define FUNCTION_TRAITS_HPP
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/apply.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/iteration/self.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
// ユーザによって伸長されることを許す
#ifndef FUNCTION_TRAITS_MAX_ARITY
#define FUNCTION_TRAITS_MAX_ARITY 15
#endif
namespace detail {
// デフォルト値を繰り返さないようにする
struct function_traits_base {
static const bool is_plain = false;
static const bool is_pointer = false;
static const bool is_reference = false;
static const bool is_member = false;
};
} // detail
// (ここでは)定義しない
template<class> struct function_traits;
// 省略記号の状態を展開する
#define ELLIPSIS(n) \
BOOST_PP_APPLY( \
BOOST_PP_TUPLE_ELEM(2, n, ELLIPSIS_I) \
) \
/**/
// 関数の引数個数についてイテレートする
#define BOOST_PP_ITERATION_PARAMS_1 \
(4, (0, FUNCTION_TRAITS_MAX_ARITY, "function_traits.hpp", 0)) \
/**/
??=include BOOST_PP_ITERATE()
// const/volatile装飾をインデックス値より取得する
#define QUALIFIER(n) \
BOOST_PP_APPLY( \
BOOST_PP_TUPLE_ELEM( \
4, n, \
(BOOST_PP_NIL, (const), (volatile), (const volatile)) \
) \
) \
/**/
// メンバ関数へのポインタのconst/volatile装飾についてイテレートする
#define BOOST_PP_ITERATION_PARAMS_1 \
(4, (0, 3, "function_traits.hpp", 1)) \
/**/
??=include BOOST_PP_ITERATE()
// 一時的マクロを消す
#undef QUALIFIER
#undef ELLIPSIS
// 関数へのポインタの部分をオーバライドする
template<class T> struct function_traits<T*> : function_traits<T> {
static const bool is_plain = false;
static const bool is_pointer = true;
};
// 関数への参照の部分をオーバライドする
template<class T> struct function_traits<T&> : function_traits<T> {
static const bool is_plain = false;
static const bool is_reference = true;
};
// eof
#endif // FUNCTION_TRAITS_HPP
// 関数型についての特殊化
#elif BOOST_PP_ITERATION_DEPTH() == 1 \
&& BOOST_PP_ITERATION_FLAGS() == 0 \
/**/
// 省略記号の状態を定義する
#if BOOST_PP_IS_SELFISH
#define ELLIPSIS_I ((true), (...))
#else
#define ELLIPSIS_I ((false), BOOST_PP_NIL)
#endif
#define N BOOST_PP_ITERATION()
template<class R BOOST_PP_ENUM_TRAILING_PARAMS(N, class T)>
struct function_traits<R (BOOST_PP_ENUM_PARAMS(N, T) ELLIPSIS(1))>
: detail::function_traits_base {
static const bool is_plain = true;
typedef R function_type(BOOST_PP_ENUM_PARAMS(N, T) ELLIPSIS(1));
typedef function_type* pointer_type;
typedef function_type& reference_type;
static const bool has_ellipsis = ELLIPSIS(0);
typedef R return_type;
static const int parameter_count = N;
template<int, class D = int> struct parameter;
#if N
// iterate over parameters
#define BOOST_PP_ITERATION_PARAMS_2 \
(3, (0, N - 1, "function_traits.hpp")) \
/**/
??=include BOOST_PP_ITERATE()
#endif
};
#undef N
#undef ELLIPSIS_I
// 省略記号付きの変種のためにこの部分を再びincludeする
#if !BOOST_PP_IS_SELFISH
#define BOOST_PP_INDIRECT_SELF "function_traits.hpp"
??=include BOOST_PP_INCLUDE_SELF()
#endif
// const/volatile装飾についてのイテレーション
#elif BOOST_PP_ITERATION_DEPTH() == 1 \
&& BOOST_PP_ITERATION_FLAGS() == 1 \
/**/
#define BOOST_PP_ITERATION_PARAMS_2 \
(3, (0, FUNCTION_TRAITS_MAX_ARITY, "function_traits.hpp")) \
/**/
??=include BOOST_PP_ITERATE()
// メンバ関数へのポインタの特殊化を生成する
#elif BOOST_PP_ITERATION_DEPTH() == 2 \
&& BOOST_PP_FRAME_FLAGS(1) == 1 \
// 省略記号の状態を定義する
#if BOOST_PP_IS_SELFISH
#define ELLIPSIS_I ((true), (...))
#else
#define ELLIPSIS_I ((false), BOOST_PP_NIL)
#endif
#define N BOOST_PP_ITERATION()
#define Q QUALIFIER(BOOST_PP_FRAME_ITERATION(1))
template<class R, class O BOOST_PP_ENUM_TRAILING_PARAMS(N, class T)>
struct function_traits<R (O::*)(BOOST_PP_ENUM_PARAMS(N, T) ELLIPSIS(1)) Q>
: detail::function_traits_base {
static const bool is_member = true;
typedef R (O::* pointer_to_member_type)(BOOST_PP_ENUM_PARAMS(N, T) ELLIPSIS(1)) Q;
typedef O class_type;
typedef Q O qualified_class_type;
static const bool has_ellipsis = ELLIPSIS(0);
static const bool is_const =
BOOST_PP_FRAME_ITERATION(1) == 1 || BOOST_PP_FRAME_ITERATION(1) == 3;
static const bool is_volatile =
BOOST_PP_FRAME_ITERATION(1) == 2 || BOOST_PP_FRAME_ITERATION(1) == 3;
typedef R return_type;
static const int parameter_count = N;
template<int, class D = int> struct parameter;
#if N
// iterate over parameters
#define BOOST_PP_ITERATION_PARAMS_3 \
(3, (0, N - 1, "function_traits.hpp")) \
/**/
??=include BOOST_PP_ITERATE()
#endif
};
#undef Q
#undef N
#undef ELLIPSIS_I
// 省略記号の付いた変種のためにこの部分を再びincludeする
#if !BOOST_PP_IS_SELFISH
#define BOOST_PP_INDIRECT_SELF "function_traits.hpp"
??=include BOOST_PP_INCLUDE_SELF()
#endif
// パラメータ特殊化
#else
#define X BOOST_PP_ITERATION()
template<class D> struct parameter<X, D> {
typedef BOOST_PP_CAT(T, X) type;
};
#undef X
#endif
throw
仕様のサポートが欠けているという問題も残っている。
例外仕様については部分特殊化が不可能なので、それを完全に取扱うことはできない。
しかしながら、例外仕様を含んだ実際の関数の型を報告することはできる(上の例は型を再構築しているため、そのようにはなっていない)。
もし気が向いたら、演習としてあなたは自分自身でそれをやってみるといい。
参照
- Paul Mensonides