Boost.Preprocessor
でプログラミングしましょう
DigitalGhost http://d.hatena.ne.jp/DigitalGhost/ http://twitter.com/DecimalBloat
私のこと
hatena のプロフィールとか
見てください
とりあえず FizzBuzz 書いてみた
#define FIZZBUZZ_OP(z, n, d) \ FIZZBUZZ_OP_I( \ BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), \ FIZZBUZZ_OP_II(n, 5, BUZZ)), \ n) #define FIZZBUZZ_OP_I(t, n) \ BOOST_PP_IIF(BOOST_PP_IS_EMPTY(t), n, t) #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t) BOOST_PP_ENUM_SHIFTED(101, FIZZBUZZ_OP, _)※ include は省略してます
gcc -P で展開
1 , 2 , FIZZ , 4 , BUZZ , FIZZ , 7 , 8 ,
FIZZ , BUZZ , 11 , FIZZ , 13 , 14 , FIZZBUZZ
, 16 , 17 , FIZZ , 19 , BUZZ …
コンパイルすらせず
解けた!
Boost.Preprocessor について
「コピペ→ちょっとだけ変更」を人間が繰り返す
代わりに、プリプロセッサで自動化するためのマ
クロいろいろ
template<typename T1, typename T2, …,
typename T50> とかいうテンプレート(実際
にBoostにはあります)を作るときとかにとても
便利
使われている例
Boost.ScopeExit
Boost.Typeof
Boost.ConceptCheck
Boost.Parameters
etc...
実行環境
VC : cl.exe /EP ソースファイル
gcc : cpp -P ソースファイル
※ cl.exe /EP だと、プリプロセスディレクティブの
行も空行として残ってしまうので、適当に削除してく
ださい
“Hello World!” in Preprocessor
ソース hello.cpp
Hello, World!
$ gcc -P hello.cpp
“Hello World!” in Preprocessor
ソース hello2.cpp
#define HELLO(x) Hello, x!
HELLO(x)
$ gcc -P hello2.cpp
FizzBuzz はどうなってるの?
#define FIZZBUZZ_OP(z, n, d) \ FIZZBUZZ_OP_I( \ BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), \ FIZZBUZZ_OP_II(n, 5, BUZZ)), \ n) #define FIZZBUZZ_OP_I(t, n) \ BOOST_PP_IIF(BOOST_PP_IS_EMPTY(t), n, t) #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t) BOOST_PP_ENUM_SHIFTED(101, FIZZBUZZ_OP, _)BOOST_PP_ENUM_SHIFTED(n, op, data)
展開する数とマクロで、 1 〜 (n 1)
–
まで数字
を変えながらコピペするマクロ
#define DECLARE_OP(z, n, data) data ## n template<
BOOST_PP_ENUM_SHIFTED(51, DECLARE_OP, typename T) > struct vector50;
BOOST_PP_ENUM_SHIFTED(n, op, data)
展開する数とマクロで、 1 〜 (n 1)
–
まで数字
を変えながらコピペするマクロ
#define DECLARE_OP(z, n, data) data ## n template< DECRARE_OP(z, 1, typename T) , DECRARE_OP(z, 2, typename T) , … DECRARE_OP(z, 50, typename T) > struct vector50;
BOOST_PP_ENUM_SHIFTED(n, op, data)
展開する数とマクロで、 1 〜 (n 1)
–
まで数字
を変えながらコピペするマクロ
#define DECLARE_OP(z, n, data) data ## n template< typename T ## 1 , typename T ## 2 , … typename T ## 50 > struct vector50;
BOOST_PP_ENUM_SHIFTED(n, op, data)
展開する数とマクロで、 1 〜 (n 1)
–
まで数字
を変えながらコピペするマクロ
#define DECLARE_OP(z, n, data) data ## n template< typename T1 , typename T2 , … typename T50 > struct vector50;
関連するマクロ
BOOST_PP_ENUM_SHIFTED_PARAMS(n, d)
d ## 1, d ## 2, … d ## (n 1)
–
と展開する
template< BOOST_PP_ENUM_SHIFTED_PARAMS(51, typename T) > struct vector50;関連するマクロ
BOOST_PP_ENUM_SHIFTED_PARAMS(n, d)
d ## 1, d ## 2, … d ## (n 1)
–
と展開する
template<
typename T ## 1 , typename T ## 2 , … typename T ## 50 >
関連するマクロ
BOOST_PP_ENUM_SHIFTED_PARAMS(n, d)
d ## 1, d ## 2, … d ## (n 1)
–
と展開する
template<
typename T1 , typename T2 , … typename T50 >
関連するマクロ
関数テンプレートの場合:
BOOST_PP_SHIFTED_BINARY_PARAMS
template< typename T1 , typename T2 , … typename T50 > tuple50< T1 , T2 , … T50 >
make_tuple50(
BOOST_PP_ENUM_BINARY_PARAMS(50, const T, & arg) );
関連するマクロ
関数テンプレートの場合:
BOOST_PP_SHIFTED_BINARY_PARAMS
template< typename T1 , typename T2 , … typename T50 > tuple50< T1 , T2 , … T50 >
make_tuple50(
const T ## 1 & arg ## 1 , const T ## 2 & arg ## 2 , …
const T ## 50 & arg ## 50 , );
関連するマクロ
関数テンプレートの場合:
BOOST_PP_SHIFTED_BINARY_PARAMS
template< typename T1 , typename T2 , … typename T50 > tuple50< T1 , T2 , … T50 >
make_tuple50(
const T1 & arg1 , const T2 & arg2 , …
const T50 & arg50 , );
FizzBuzz はどうなってるの?
#define FIZZBUZZ_OP(z, n, d) \ FIZZBUZZ_OP_I( \ BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), \ FIZZBUZZ_OP_II(n, 5, BUZZ)), \ n) #define FIZZBUZZ_OP_I(t, n) \ BOOST_PP_IIF(BOOST_PP_IS_EMPTY(t), n, t) #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t) BOOST_PP_ENUM_SHIFTED(101, FIZZBUZZ_OP, _)BOOST_PP_CAT(a, b)
a と b をトークン連結します
a ## b とだいたい同じです
BOOST_PP_CAT(a, b)
それは ## で十分じゃないの?
#define BAD(a, b) a ## b
#define GOOD(a, b) BOOST_PP_CAT(a, b) #define DOUBLE(a) a a BAD(HOGE, DOUBLE(FUGA)) GOOD(HOGE, DOUBLE(FUGA))
HOGEDOUBLE(FUGA)
と展開されてしまう
(トークン連結のほうが先に
実行される)
HOGEFUGA FUGA
になる
BOOST_PP_CAT(a, b)
ちなみに実装
#define BOOST_PP_CAT(a, b) BOOST_PP_I(a, b) #define BOOST_PP_CAT_I(a, b) a ## b
こうすれば a, b がマクロだった場合、全て展開
が終わった後に連結します
FizzBuzz はどうなってるの?
#define FIZZBUZZ_OP(z, n, d) \ FIZZBUZZ_OP_I( \ BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), \ FIZZBUZZ_OP_II(n, 5, BUZZ)), \ n) #define FIZZBUZZ_OP_I(t, n) \ BOOST_PP_IIF(BOOST_PP_IS_EMPTY(t), n, t) #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t) BOOST_PP_ENUM_SHIFTED(101, FIZZBUZZ_OP, _)BOOST_PP_IIF(cond, t, f)
cond が
「1」というトークンであれば t
「0」というトークンであれば f
BOOST_PP_IIF(1, HOGE, PIYO) → HOGE
BOOST_PP_IIF(0, HOGE, PIYO) → PIYO
※一つめの引数(cond)が受け付けられるのは、「0」
か「1」か、もしくはそう展開されるマクロのみ
プリプロセッサでの数字の扱い
プリプロセッサは「1」が示す
値
を認識してい
ない
「1LL」とか「1.0」は
×
プリプロセッサの数字の扱い
「123」や「abc」を「
文字の並び(トーク
ン)
」として扱っている
丸カッコとカンマ以外の記号や空白が混ざってい
てもトークン
「123-abc; .exe」も
一つの
トークン
関連するマクロ
BOOST_PP_IF(cond, t, f)
cond が 1〜255 なら t 、0 なら f になる
BOOST_PP_EXPR_IIF(cond, t)
cond が 1 なら t に、 0 なら空トークンになる
BOOST_PP_EXPR_IIF(1, HOGE) → HOGE BOOST_PP_EXPR_IIF(0, HOGE) →FizzBuzz はどうなってるの?
#define FIZZBUZZ_OP(z, n, d) \ FIZZBUZZ_OP_I( \ BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), \ FIZZBUZZ_OP_II(n, 5, BUZZ)), \ n) #define FIZZBUZZ_OP_I(t, n) \ BOOST_PP_IIF(BOOST_PP_IS_EMPTY(t), n, t) #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t) BOOST_PP_ENUM_SHIFTED(101, FIZZBUZZ_OP, _)BOOST_PP_BOOL(n)
BOOST_PP_NOT(n)
BOOST_PP_BOOL(n)
n が 1〜255 なら 1 、0 なら 0
( n != 0 と同じ)BOOST_PP_NOT(n)
n が 1〜255 なら 0 、0 なら 1
( !n と同じ) BOOST_PP_BOOL(42) , BOOST_PP_NOT(42) → 1 , 0FizzBuzz はどうなってるの?
#define FIZZBUZZ_OP(z, n, d) \ FIZZBUZZ_OP_I( \ BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), \ FIZZBUZZ_OP_II(n, 5, BUZZ)), \ n) #define FIZZBUZZ_OP_I(t, n) \ BOOST_PP_IIF(BOOST_PP_IS_EMPTY(t), n, t) #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t) BOOST_PP_ENUM_SHIFTED(101, FIZZBUZZ_OP, _)BOOST_PP_IS_EMPTY(a)
A が空トークンか、空トークンに展開されるな
ら 1 、識別子なら 0
BOOST_IS_EMPTY(HOGE) → 0 BOOST_IS_EMPTY( ) → 1 #define DUMMY BOOST_IS_EMPTY(DUMMY) → 1 (2 番目の例は VC では警告がでます)FizzBuzz はどうなってるの?
#define FIZZBUZZ_OP(z, n, d) \ FIZZBUZZ_OP_I( \ BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), \ FIZZBUZZ_OP_II(n, 5, BUZZ)), \ n) #define FIZZBUZZ_OP_I(t, n) \ BOOST_PP_IIF(BOOST_PP_IS_EMPTY(t), n, t) #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t) BOOST_PP_ENUM_SHIFTED(101, FIZZBUZZ_OP, _)BOOST_PP_MOD(m, n)
a を b で割った余り(つまり m % n)
BOOST_PP_MOD(5, 3) → 2 BOOST_PP_MOD(BOOST_PP_MOD(139, 25), 8) → 6BOOST_PP_ADD BOOST_PP_SUB
BOOST_PP_MUL BOOST_PP_DIV
もあります
a を b で割った余り(つまり m % n)
もう一度、数字に関する注意
BOOST_PP_IIF と同じく、C++ の値を認識し
ているわけではないので、
BOOST_PP_ADD(1 + 1, 2 + 2)
とかはできない
BOOST_PP_ADD(
BOOST_PP_ADD(1, 1),
BOOST_PP_ADD(2, 2))
これはOK
FizzBuzz はどうなってるの?
#define FIZZBUZZ_OP(z, n, d) \ FIZZBUZZ_OP_I( \ BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), \ FIZZBUZZ_OP_II(n, 5, BUZZ)), \ n) #define FIZZBUZZ_OP_I(t, n) \ BOOST_PP_IIF(BOOST_PP_IS_EMPTY(t), n, t) #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t) BOOST_PP_ENUM_SHIFTED(101, FIZZBUZZ_OP, _)FizzBuzz を手動で展開
BOOST_PP_ENUM_SHIFTED(101, FIZZBUZZ_OP, _) を展開 FIZZBUZZ_OP(z, 1, _) , FIZZBUZZ_OP(z, 2, _) , … FIZZBUZZ_OP(z, 100, _)FIZZBUZZ_OPは、
1→1
2→2
3→FIZZ
…
というようなことをする
FizzBuzz を手動で展開
string fizzbuzz_op(int n) { string const & fizzbuzz =
string(!(n % 3) ? "FIZZ" : "") + string(!(n % 5) ? "BUZZ" : ""); return fizzbuzz.empty() ? lexical_cast<string>(n) : fizzbuzz; }
FIZZBUZZ_OP を C++ で書いてみる
(C++的にはすごく効率が悪いですが、後の解説のためな
ので見逃してください)
FizzBuzz を手動で展開
この関数をこんな風に置き換え
str1 + str2
→
BOOST_PP_CAT(str1, str2)
m % n
→
BOOST_PP_MOD(m, n)
!n
→
BOOST_PP_NOT(n)
str.empty()
→
BOOST_PP_IS_EMPTY(str)
c ? "str" : ""
→
BOOST_PP_EXPR_IIF(c, str)
c ? a : b
→
BOOST_PP_IIF(c, a, b)
FizzBuzz を手動で展開
string fizzbuzz_op(int n) { string const & fizzbuzz =
string(!(n % 3) ? "FIZZ" : "") + string(!(n % 5) ? "BUZZ" : ""); return fizzbuzz.empty() ? lexical_cast<string>(n) : fizzbuzz; }
ここからプリプロセッサで置き換え
FizzBuzz を手動で展開
string fizzbuzz_op(int n) { string const & fizzbuzz =
BOOST_PP_CAT(!(n % 3) ? "FIZZ" : "", !(n % 5) ? "BUZZ" : ""); return fizzbuzz.empty() ? lexical_cast<string>(n) : fizzbuzz; }
str1 + str2 → BOOST_PP_CAT(str1, str2)
FizzBuzz を手動で展開
string fizzbuzz_op(int n) { string const & fizzbuzz = BOOST_PP_CAT( BOOST_PP_EXPR_IIF(!(n % 3), FIZZ), BOOST_PP_EXPR_IIF(!(n % 5), BUZZ)); return fizzbuzz.empty() ? lexical_cast<string>(n) : fizzbuzz; }
c ? "str" : "" → BOOST_PP_EXPR_IIF(c, str)
FizzBuzz を手動で展開
string fizzbuzz_op(int n) { string const & fizzbuzz = BOOST_PP_CAT( BOOST_PP_EXPR_IIF(BOOST_PP_NOT(n % 3), FIZZ), BOOST_PP_EXPR_IIF(BOOST_PP_NOT(n % 5), BUZZ)); return fizzbuzz.empty() ? lexical_cast<string>(n) : fizzbuzz; }
!n → BOOST_PP_NOT(n)
FizzBuzz を手動で展開
string fizzbuzz_op(int n) { string const & fizzbuzz = BOOST_PP_CAT( BOOST_PP_EXPR_IIF(BOOST_PP_NOT( BOOST_PP_MOD(n, 3)), FIZZ), BOOST_PP_EXPR_IIF(BOOST_PP_NOT( BOOST_PP_MOD(n, 5)), BUZZ)); return fizzbuzz.empty() ? lexical_cast<string>(n) : fizzbuzz; }
m % n → BOOST_PP_MOD(m, n)
FizzBuzz を手動で展開
string fizzbuzz_op(int n) { string const & fizzbuzz =
BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), FIZZBUZZ_OP_II(n, 5, BUZZ)); return fizzbuzz.empty() ? lexical_cast<string>(n) : fizzbuzz; } #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t)
FIZZBUZZ_OP_II を定義
FizzBuzz を手動で展開
string fizzbuzz_op(int n) { string const & fizzbuzz =
BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), FIZZBUZZ_OP_II(n, 5, BUZZ)); return BOOST_PP_IIF(fizzbuzz.empty(), n, fizzbuzz); } #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t)
c ? a : b → BOOST_PP_IIF(c, a, b)
FizzBuzz を手動で展開
string fizzbuzz_op(int n) { string const & fizzbuzz =
BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), FIZZBUZZ_OP_II(n, 5, BUZZ)); return BOOST_PP_IIF(BOOST_PP_IS_EMPTY(fizzbuzz), n, fizzbuzz); } #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t)
str.empty() → BOOST_PP_IS_EMPTY(str)
FizzBuzz を手動で展開
#define FIZZBUZZ_OP(z, n, d) \ FIZZBUZZ_OP_I( \ BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), \ FIZZBUZZ_OP_II(n, 5, BUZZ)), \ n) #define FIZZBUZZ_OP_I(t, n) \ BOOST_PP_IIF(BOOST_PP_IS_EMPTY(t), n, t) #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t)fizzbuzz_op をマクロ化
FizzBuzz を手動で展開
#define FIZZBUZZ_OP(z, n, d) \ FIZZBUZZ_OP_I( \ BOOST_PP_CAT(FIZZBUZZ_OP_II(n, 3, FIZZ), \ FIZZBUZZ_OP_II(n, 5, BUZZ)), \ n) #define FIZZBUZZ_OP_I(t, n) \ BOOST_PP_IIF(BOOST_PP_IS_EMPTY(t), n, t) #define FIZZBUZZ_OP_II(m, n, t) \ BOOST_PP_EXPR_IIF(BOOST_PP_NOT(BOOST_PP_MOD(m, n)), t) BOOST_PP_ENUM_SHIFTED(101, FIZZBUZZ_OP, _)完成!
もっと複雑な例
make_smart
newでT型の値を作ってすぐにスマートポインタで管
理する場合、だいたい
smart_ptr<T>(new T(param1, param2 …));
という感じに書く
これは面倒だし、生のポインタが一瞬登場してしま
う。完全にポインタを消したい
make_smart_ptr(1引数版)
make_smart(1引数版)
template<typename T, typename T0>
my_smart_ptr<T> make_smart(T0 param0) { return my_smart_ptr<T>(new T(param0)); }
これでは値で引数を受けているので効率が悪い
参照を使おう
make_smart_ptr(1引数版)
make_smart(1引数版)
template<typename T, typename T0>
my_smart_ptr<T> make_smart(T0 & param0) { return my_smart_ptr<T>(new T(param0)); }
make_smart_ptr(1引数版)
make_smart(1引数版)
template<typename T, typename T0>
my_smart_ptr<T> make_smart(T0 & param0) { return my_smart_ptr<T>(new T(param0)); }
template<typename T, typename T0>
my_smart_ptr<T> make_smart(T0 const & param0) { return my_smart_ptr<T>(new T(param0));
}
これで、引数が const 参照かそうでないかに
よって呼び分けられる
make_smart_ptr(2引数版)
1つ目の引数が const / 非 const 、2つ目の引
数が const / 非 const で、2 * 2 = 4 パター
ン必要
template<typename T, typename T0, typename T1>
my_smart_ptr<T> make_smart(T0 & param0, T1 & param1) { return my_smart_ptr<T>(new T(param0, param1));
}
template<typename T, typename T0, typename T1>
my_smart_ptr<T> make_smart(T0 const & param0, T1 & param1) { return my_smart_ptr<T>(new T(param0, param1));
}
template<typename T, typename T0, typename T1>
my_smart_ptr<T> make_smart(T0 & param0, T1 const & param1) { return my_smart_ptr<T>(new T(param0, param1));
}
template<typename T, typename T0, typename T1>
my_smart_ptr<T> make_smart(T0 const & param0, T1 const & param1) { return my_smart_ptr<T>(new T(param0, param1));
make_smart_ptr(n引数版)
3要素タプル版
1つ目の引数が(略)で、2 * 2 * 2 = 8 パターン
4要素(略)
1つ目(略)で、2 *(略)= 16 パターン
結局ここまでだけでも、2 + 4 + 8 + 16 = 30
パターン書かないといけない
面倒!
Boost.PP で自動生成
#define DEF_MAKE_SMART_OVERLOADS_OP(z, n, data) \BOOST_PP_SEQ_FOR_EACH_PRODUCT(DEF_MAKE_TUPLE, ((n)) BOOST_PP_REPEAT(n, MAKE_CONST_SEQ, _)) #define DEF_MAKE_SMART(r, seq) \
DEF_MAKE_SMART_I(BOOST_PP_SEQ_HEAD(seq), BOOST_PP_SEQ_TAIL(seq)) #define DEF_MAKE_SMART_I(n, seq) \
template< typename T , BOOST_PP_ENUM_PARAMS(n, typename T) > \ my_smart_ptr<T> \
make_smart(BOOST_PP_FOR((n, 0, seq), PARAMS_P, PARAMS_OP, DECL_PARAMS)) { \ return my_smart_ptr<T>(new T(BOOST_PP_ENUM_PARAMS(n, param))); \
}
#define PARAMS_P(r, state) PARAMS_P_I state
#define PARAMS_P_I(n, i, seq) BOOST_PP_GREATER(n, i) #define PARAMS_OP(r, state) PARAMS_OP_I state
#define PARAMS_OP_I(n, i, seq) \
(n, BOOST_PP_INC(i), BOOST_PP_SEQ_TAIL(seq)) #define DECL_PARAMS(r, state) DECL_PARAMS_I state #define DECL_PARAMS_I(n, i, seq) \
BOOST_PP_COMMA_IF(i) T ## i BOOST_PP_SEQ_HEAD(seq) & param ## i #define MAKE_CONST_SEQ(z, n, _) (()(const))
代入
Y は X() と展開される
4 と展開したい!
#define X() 4 #define Y() X() #undef X Yそこで
BOOST_PP_SLOT(1) は 10 に展開される
素敵!
#define X() 4 #define BOOST_PP_VALUE 1 + 2 + 3 + X() #include BOOST_PP_ASSIGN_SLOT(1) #undef X BOOST_PP_SLOT(1)誰得?
Boost.Typeof は、型を整数の列にエンコード
する必要があるので、型に一意な整数 ID を振る
ために使われています
他に
BOOST_PP_ITERATE
自分自身を繰り返し #include します
BOOST_PP_ENUM で書くにはマクロが大きすぎる
場合に便利
Boost.PP を読むときの注意
BOOST_PP_AUTO_REC というマクロが登場
しますが、これが曲者です
http://d.hatena.ne.jp/DigitalGhost/20090903/1252002035