※boost.diそのものに関する話は出てきません。ご了承ください
この記事は 2019C++アドベントカレンダー (https://qiita.com/advent-calendar/2019/cpp) 5日目の記事になります
boost.diのannotations
boost.diには(今気にすべきことだけを言うと)「オブジェクトの構築を補助する機能」が含まれている
その中にはannotationsというものがあり、以下のようにコンストラクタ引数に名前を付ける機能がある
// 名前用型
auto int1 = [] {};
// 型定義
struct Hoge {
BOOST_DI_INJECT(Hoge , (named = int1) int a, double b) // aに"int1"という名前を付ける
: a(a), b(b) {}
int a = 0;
double b = 0;
};
auto injector = di::make_injector(
di::bind<int>().named(int1).to(42) //intでint1という名前のパラメタを42で初期化
, di::bind<double>().to(3.14) // doubleのパラメタを3.14で初期化
);
auto x = injector.create<Hoge>(); // 便利に構築。何が便利なのかはboost.diのドキュメントかDIに関する記事を読んでくれ
// 想定通りにオブジェクトが構築された
std::cout<<x.a<<std::endl; // 42
std::cout<<x.b<<std::endl; // 3.14
しかし、BOOST_DI_INJECT(Hoge , (named = int1) int a, double b)
の (named = int1) int a
という文法はC++的に考えて奇妙だ。しかもこのannotations、付けてもつけなくてもよい
これはどういったトリックだろうか?
大雑把な実装原理
#define B(_) A
#define TEST(X) B X
上のようなマクロを書いたとき、TEST(X)
のX
が
-
(hoge) 1
の場合:B(_)
で置換されてtoken列A 1
になる -
1
の場合: それ以上置換できるルールが存在しないのでtoken列B 1
になる
ことを利用している
もう少し具体的な挙動
#define PP_CAT(L,R) PP_CAT2(L,R)
#define PP_CAT2(L,R) L##R
#define AA 1,
#define AB 0,
#define B(_) A
#define TEST(X) PP_CAT(A,B X)
-
TEST((1)1)
は-
TEST((1)1)
->PP_CAT(A,B(1)1)
->PP_CAT2(A,A 1)
->AA 1
->1,1
のように置換される
-
-
TEST(1)
は-
TEST(1)
->PP_CAT(A,B 1)
->PP_CAT2(A,B 1)
->AB 1
->0,1
のように置換される
-
このように置換すれば最終的にいい感じのtoken列に変換できるので、あとはマクロなりで好きな個所を取り出しいい感じに使える using type = なんとか
でもなんでも生やしてほしい
boost.diにおける実装
https://github.com/boost-experimental/di/blob/v1.1.0/include/boost/di/aux_/preprocessor.hpp#L102
この行のあたりの挙動を追ってほしい