Submit Search
Define and expansion of cpp macro
•
11 likes
•
4,282 views
D
digitalghost
Follow
1 of 48
Download now
Downloaded 10 times
More Related Content
Define and expansion of cpp macro
1.
Define and Expansion
of CPP Macro Cプリプロセッサマクロの定義と展開 2014/06/28 ドワンゴC++勉強会 #1 でちまる
2.
自己紹介 ● でちまる ● @decimalbloat ●
仕事 – 艦これコンテンツの回収と観賞 – ツイッター上で見かけた不思議C++コードを見て実装を 看破する – Android上でJavaを書く(web系ではない)
3.
本日の発表は ● constexpr ● Cプリプロセッサ ●
C++老害トーク ● どれも明日から職場で即実践できる話ばかり ● 是非ご活用ください
4.
今日の話 ● Cプリプロセッサメタプログラミングについて ● Cプリプロセッサメタプログラミングの技法
5.
Cプリプロセッサメタプログラミング について
6.
Cプリプロセッサメタプログラミングとは ● Cプリプロセッサで(主にC++の)コードを出力する プログラムを書く行為 ● C++の構文木や意味論などは一切無視して字句 レベルでプログラムを生成する ●
やってることとしてはsedとかawkと変わらない
7.
10個の連番の関数を宣言する例 ● #define F(z,
i, d) void BOOST_PP_CAT(f, i)(int); BOOST_PP_REPEAT(10, F, ~) → void f0(int); void f1(int); …… void f9(int); ● 実際に void f0(int); … とかいうのがソースに書か れている,と扱われる
8.
使いどころ ● 他の言語機能で解決できないケース ● 例:
Scope Exit – 即席RAIIのためのマクロ – 簡単にいうと次のようなもの
9.
Scope Exit の簡単な実装 #define
SCOPE_EXIT scope_exit_t BOOST_PP_CAT(scope_exit_var, __LINE__) = [&] struct scope_exit_t { std::function<void()> f; template<typename F> scope_exit_t(F f) : f(f) {} ~scope_exit_t() { f(); } }; int main() { SCOPE_EXIT { std::cout << "hogen"; }; SCOPE_EXIT { std::cout << "fuga, "; }; } → fuga, hoge
10.
Scope Exit の簡単な実装 ●
RAIIで必要な処理をさせるためにはそれ用の変数 もしくはクラスに名前を付ける必要がある ● しかし単にデストラクタを呼ぶだけなので,人間側 の都合としてはどうでもよい ● そんなわけでこういうマクロを書けば余計な名前を 考える手間もコード上のノイズも減る
11.
よく言われる問題
12.
野蛮 ● 何事も暴力で解決するのが一番だ ――NINJASLAYER より
レッドゴリラ=サン ● CプリプロセッサはC++の構文も意味論も理解しな い ● まーでもこれ以外ないから暴力で解決だ
13.
では構文を解すればよいのでは? ● ついこの間提案された ● http://www.open- std.org/jtc1/sc22/wg21/docs/papers/2014/n3883 .html ●
10年単位で待てばいけるかもしれない
14.
名前が衝突する ● BOOST_PP_ とかそういうprefix付ければ普通衝 突しない ●
それでも衝突するのはただの当たり屋では?
15.
コンパイルエラーがひどい ● はい ● ただしそれは関数マクロでやった場合の話 ●
include主体でやればそうでもない(後述しようと 思ったが時間がなくなった)
16.
制限が多い ● はい ● 再帰展開できないとかそういうのがある ●
そもそも,アプリケーション開発でコード生成が必 要になったのなら別にCプリプロセッサでなくPHPと かPerlとかでよい ● わざわざCプリプロセッサでやるのは,PHPがない 環境でもコード生成したいから
17.
ここまでのまとめ ● PHP使えよ
18.
Cプリプロセッサメタプログラミング の技法
19.
選べる2つのスタイル ● 関数マクロ濫用スタイル ● include濫用スタイル(こっちの話はしない)
20.
関数マクロ濫用スタイルにおける基本 的な技法 ● 基本 – 連結と再展開 – 評価順の制御 –
引数の解体 ● 応用 – 数値と真理値 – 分岐 – 繰り返し – データ構造
21.
基本
22.
連結と再展開 ● #define CAT(a,
b) a ## b #define AB C CAT(A, B) →C ● 連結してできたトークンをマクロ名として定義してお くことで,更に展開する
23.
評価順の制御 ● #define CAT(a,
b) a ## b CAT(A, CAT(B, C)) ACAT(B, C) ● マクロの置換規則はC++の関数とは大きく違うの でなかなか分かりづらい
24.
評価順の制御 ● 置換の基本は – 外側のマクロを置き換えてから, –
トークン連結して, – それぞれの引数を個別に置換して, – 元の場所に挿入して, – その場所からもう一度置換する
25.
評価順の制御 ● #define CAT(a,
b) a ## b CAT(A, CAT(B, C)) →ACAT(B, C) ● 外側のマクロを置き換えると A ## CAT(B, C) ● 連結すると ACAT(B, C) なので引数としては扱わ れなくなる
26.
評価順の制御 ● #define CAT(a,
b) CAT_I(a, b) #define CAT_I(a, b) a ## b ーCAT(A, CAT(B, C)) →ABC ● 外側のマクロを置き換えると CAT_I(A, CAT(B, C)) ● トークン連結はないのでそのまま ● 引数について展開する(CATが意図通り動くとする)と CAT_I(A, BC) ● 元の場所に戻してそこからもう一度置換する ● 外側のマクロを置き換えると A ## BC ● 連結して ABC ● 以下省略
27.
引数の解体 ● #define REM(...)
__VA_ARGS__ #define AP(f, x) f(42, x) #define F(n, x) F_I(n, REM x) #define F_I(n, x) F_II(n, x) #define F(n, x, y, z) AP(F, (A, B, C)) → ● カッコで包めば一つの引数にできるので,任意長 のarityのマクロを固定長引数のマクロの中で扱え る ● このマクロの置換がどのように行われるかは自明 なので,手順は読者の課題とする(放棄)
28.
応用
29.
数値と真理値 ● Cプリプロセッサには数値という概念はない ● (一部の文脈を除く) ●
ソースコード上に現われる1とか2とかいうトークン を数値として扱うことにする ● 同様に0と1をそれぞれtrueとfalseとして扱うことに する ● マクロの上でどうやってそれらを数値や真理値とみ なすのかは次のページで
30.
分岐 ● 分岐は基本的な技法だけで書ける ● #define
IF(c, t, f) CAT(IF, c)(t, f) #define IF1(t, f) t #define IF0(t, f) f IF(1, hoge, fuga) →hoge IF(0, hoge, fuga) →fuga
31.
分岐 ● IFと1または0を連結することでそれが再度マクロ名となることで, 二つのマクロ (IF1,
IF0) を使い分ける ● C風の数値から整数への変換も次のように書ける ● #define BOOL(n) CAT(BOOL, n) #define BOOL0 0 #define BOOL1 1 #define BOOL2 1 #define BOOL3 1 … BOOL(3) →1
32.
分岐 ● IFと組み合わせればCのif風のものもできる ● #define
IF2(n, t, f) IF(BOOL(n), t, f)
33.
数値計算 ● とりあえず1加算するマクロだけ作る ● #define
INC(n) INC ## n #define INC0 1 #define INC1 2 #define INC2 3 … INC(INC(2)) →4 ● やってることはBOOLと同じ
34.
繰り返し ● 汎用の繰り返しマクロを作るにはいくつかの方法 があるが,全てについて ● 「どの繰り返しマクロも同じようなことを何個もコピ ペして書いている」 ●
というのは共通している
35.
繰り返し ● 最も単純な実装 ● #define REPEAT(n,
m) REPEAT ## n(m) #define REPEAT0(m) #define REPEAT1(m) m(0) #define REPEAT2(m) REPEAT1(m) m(1) #define REPEAT3(m) REPEAT2(m) m(2) #define F(n) hoge ## n REPEAT(3, F) →hoge0 hoge1 hoge2 ● mを関数マクロとみなすことで,繰り返す内容を自由に指定で きる
36.
繰り返し ● もっと複雑なもの ● #define WHILE
WHILE0 #define WHILE_END(p, st, op) st #define WHILE0(p, st, op) IF(p(st), WHILE1, WHILE_END)(p, st, op) #define WHILE1(p, st, op) IF(p(op(st)), WHILE2, WHILE_END)(p, op(st), op) #define WHILE2(p, st, op) IF(p(op(st)), WHILE3, WHILE_END)(p, op(st), op) #define WHILE3(p, st, op) IF(p(op(st)), WHILE4, WHILE_END)(p, op(st), op) … ● p(st)が偽だったらst ● 真だったらop(st)した結果で同じことを繰り返す
37.
繰り返し ● 次のように使う(DECはINCの逆として定義したマク ロとする) ● #define
ADD(m, n) ADD_I WHILE(P, (m, n), OP) #define P(st) P_I st #define P_I(m, n) BOOL(m) #define OP(x) OP_I x #define OP_I(m, n) (DEC(m), INC(n)) ADD(3, 4) →7
38.
注意 ● 今まで見てきた繰り返しマクロは,引数のマクロ (ループ内で使うマクロ)の中で同繰り返しマクロを 使うことができない ● そんなわけでこの制限を出し抜くために次のような 技法が考案された ●
http://www.slideshare.net/digitalghost/c- 35069539
39.
データ構造 ● いくつかあるがよく使われているのは次の二つ – (a)(b)(c) –
(a, b, c)
40.
Sequence ● (a)(b)(c) というような形式を
sequenceと言う ● 先頭または末尾への要素の追加,削除はO(1)で できるが要素へのアクセスはO(N)なデータ構造
41.
Sequence ● コード例はオンラインで
42.
Tuple ● (a, b,
c) のような形式をtupleという ● 長さを変更できないが要素へのアクセスはO(1)な データ構造 – だった ● C++11からVariadic Macroが導入されたので長さ の制限がなくなった
43.
Tuple ● コード例はオンラインで
44.
データ構造 ● だいたいこの2つがあればまともなプログラムが書 ける ● いずれのデータ構造用の関数マクロも,ループ同 様職人が丹精こめて書き上げたコピペ手直しコー ドでできている
45.
まとめ ● PHPを使え ● 置換とトークン連結だけでも足し算は作れる ●
たとえ貧弱な機能でも人間は濫用する. 抵抗は無意味だ
46.
参考になるソースコード ● http://boost.org/ ● https://github.com/dechimal/desalt
47.
質疑応答
48.
終わり
Download