君はまだ,本当のプリプロセスを知らない

3,927 views
3,758 views

Published on

0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,927
On SlideShare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
18
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide

君はまだ,本当のプリプロセスを知らない

  1. 1. 君はまだ,本当のプリプロセスを知らないC Preprocessors Counterattack
  2. 2. あいさつ● でちまる (@decimalbloat)● http://libdechimal.so/● http://github.com/dechimal/desalt/● 仕事募集中● 本日の誰得発表第2部
  3. 3. 今日の話を三行で● ある型と振る舞いが同じな別の型が欲しい● できるだけ簡単に生成するマクロ書いたよ● みなさんと一緒にそのコードを探検することで,Cプリプロセッサの深淵を共有したい
  4. 4. 目次● Strong Typedef とは● Desalt.Newtype とは● Desalt.Newtype の内部(今日の主題)
  5. 5. Strong Typedef とは
  6. 6. よくある事例● “A:” >> qi::int_ % ,| “B:” >> qi::int_ % ,| “C:” >> qi::string● 結果は variant<vector<int>, string> 相当● しかし A: と B: の場合で区別したい● variant<vector<int>, vector<int>,string> になりませんか?
  7. 7. よくある事例● struct player {unsigned hp;unsigned atk;void attack(player & target) {target.hp -= this->hp;}};_人人人人人人_> 体力勝負 < ̄Y^Y^Y^Y^Y ̄
  8. 8. 解決法● 単純に元の型の値をメンバに持つだけのラッパを書く– おもんない– 取り出したあとは同じ型なので後者の例だとやはり取り違える可能性がある● 元の型と同じI/Fと振る舞いを持つ型で代用する– その型を作るのが果てしなく面倒– Strong Typedef を使えば楽– 既存の実装は整数限定だし汎用的に使えるの作ろう
  9. 9. Desalt.Newtype とは
  10. 10. Desalt.Newtype● https://github.com/dechimal/desalt/blob/master/test/newtype.cpp● Strong Typedef の一種● Boost.Serialization のものとは違って整数以外に適用できる
  11. 11. お詫び● 今 github に上がってるコードは gcc のバージョン上げたら動かなくなってました☆(・ω<)– マクロではなく TMP の部分でエラーになってるので C++ コンパイラ各位にはデバッグをお願いしたく
  12. 12. コード例● std::vector<int> の begin と end メンバと全てのコンストラクタのみを公開し,as_base という名前で元の型の値として扱える ivector クラスの定義● DESALT_NEWTYPE(ivector, std::vector<int>,as_base,begin,end,this);
  13. 13. コード例● std::basic_string<T> の機能のうち, append とostream への出力だけができる mystring 型– (append は引数も戻り値も mystring 型)● DESALT_NEWTYPE(mystring, std::string,as_base,this,auto append,namespace explicit (operator<<)(std::ostream &, mystring const &));
  14. 14. ↑ここまでC++ Advent Calendar 2012 への投稿 +α今日の本題↓ここから
  15. 15. Desalt.Newtype の内部
  16. 16. 構文要素 this● DESALT_NEWTYPE(hoge, fuga,as_fuga,piyo, // using fuga::piyo;this // using fuga::fuga;);● this は Inheriting Ctor を使うための(このマクロにとっての)キーワードで,つまり中で usingfuga::fuga; をしている● どうなってるのか?
  17. 17. 構文要素 this● ある識別子(らしきもの)がトークン列の先頭に含まれているかを調べればよい● #define KEYWORDthis ,IS_EMPTY(TUPLE_ELEM(0, (CAT(KEYWORD,this))))● 要するに,KEYWORD をトークン連結した相手がthis だった場合のみ空トークンになるようにして判定している
  18. 18. 構文要素 this● DESALT_NEWTYPE(hoge, fuga,as_fuga,piyo, // using fuga::piyo;this // using fuga::fuga;);● 先の方法で this 以外の未知のトークンも判定できるので,知らないトークンだったらそのまま using する
  19. 19. 構文要素 auto● struct fuga {fuga piyo(int, fuga const &);};DESALT_NEWTYPE(hoge, fuga,as_fuga,auto piyo // hoge piyo(int x, hoge const & y) {); // return piyo(x, y);// }● auto キーワードを使うと元の関数の引数と戻り値型を新しい型で置き換えた関数を定義する(関係ない型はそのまま)● これをするには auto piyo から piyo を取り出す必要がある
  20. 20. 構文要素 auto● this の件でやった方法をそのまま流用● #define KEYWORDauto ,IS_EMPTY(BOOST_PP_TUPLE_ELEM(1,(CAT(KEYWORD, auto piyo))))● 要するに KEYWORD をくっ付けたら auto が消えてカンマになるのでそれをタプルとして扱って最初の要素(空トークン)を読み飛ばす
  21. 21. オンデマンド括弧● template<typename T, typename U>DESALT_NEWTYPE(hoge, (fuga<T, U>),as_fuga);● クラステンプレートの場合だけ元のクラスの周りに括弧が必要● 括弧があるときだけ外す必要がある
  22. 22. オンデマンド括弧● トークン列の先頭に括弧があるかどうかを調べられたらよい● #define IS_PAREN(...) IS_PAREN_I(CAT(A, B __VA_ARGS__))#define B(...) OK#define AB 0,#define AOK 1,#define IS_PAREN_I(...) IS_PAREN_II(__VA_ARGS__)#define IS_PAREN_II(x, ...) x● トークン列の前にテスト用の関数マクロを置くと,展開されたときは OK に,されなかったときはそのまま残るので,this のときと同じ手法で判定すればよい
  23. 23. 空トークン● DESALT_NEWTYPE(hoge, fuga,as_fuga,, // do nothingpiyo ,);● 何も定義しない行が存在してもよい● ところが任意のトークン列が空かどうかを判定するのに Boost.PP の IS_EMPTY は使えない
  24. 24. 空トークン● Boost.PP の IS_EMPTY の実装● #define IS_EMPTY(x) IS_EMPTY_I(x A)#define IS_EMPTY_I(y) TUPLE_ELEM(2, 1, B ## y ())#define A() , 0#define BA() 1, 1 EMPTY#define EMPTY()
  25. 25. 空トークン● この実装の問題– カンマを扱えない– 先頭のトークンが識別子(の一部になれる)でないといけない● 今の時代 __VA_ARGS__ 使えばよい
  26. 26. 空トークン● 先頭に何があるのか分からない場合,連結でのチェックはできない● そこで IS_PAREN を使って実装することで連結せずに空トークンかどうかを判定する
  27. 27. 空トークン● #define IS_EMPTY(...) IF(IS_PAREN(__VA_ARGS__ ()), IS_EMPTY_I, 0 TUPLE_EAT())(__VA_ARGS__)#define IS_EMPTY_I(...) IF(IS_PAREN(__VA_ARGS__), 0 TUPLE_EAT(), IS_EMPTY_II)(__VA_ARGS__)#define IS_EMPTY_II(...) IS_PAREN(CAT(A, __VA_ARGS__) ())#define A() ()
  28. 28. 空トークン● 重要な部分はこの2つ– IS_PAREN(__VA_ARGS__()) … A– IS_PAREN(__VA_ARGS__) … B● A が 1 である場合– 空– 括弧で始まっている– 末尾の括弧により展開されて上のどちらかになる … α● 上の場合で,かつ,B が 0 になる場合– 空– α によって A が 1 になった場合
  29. 29. 空トークン● A が 1 で B が 0 になるケースでは,次のどちらかになる– 空– 識別子っぽいもの● IS_PAREN(CAT(A, __VA_ARGS__) ())#define A() ()● ここまでくるとトークン連結が使えるので,補助マクロを使って,__VA_ARGS__ が空であれば () になるようにすれば判定できる
  30. 30. 空トークン● 実は Desalt.Newtype では有効に使ってないことに気付いた
  31. 31. 終わりに● Cプリプロセッサを使えばまぁまぁそれっぽいDSLを作れる● Template だけではコピペを軽減できないときには迷わず使おう● Cプリプロセッサは(まだ)友達
  32. 32. このマクロを書いた人はこんなマクロを書いています● PPLambda– #define で定義せずにマクロを作って適用する– http://patch-tag.com/r/digitalghost/pplambda/home● InitWithTuple– std::tie みたいにタプルを使って複数の変数を初期化する– https://github.com/dechimal/init-with-tuple● AttoTest– 自分用ユニットテストフレームワーク– https://github.com/dechimal/atto-test
  33. 33. Special Thanks (アルファベット順)● Cryolite さん– 本日私の隣で同じく資料を作成する傍ら励ましをいただいた● fadis_ さん– LibreOfficeのダウンロードを手伝っていただいた● Flast_RO さん– LibreOfficeのダウンロードを手伝っていただいた● melponn さん– 無線LAN環境を貸していただいた● ボレロさん,昼食で遅れたみなさん– おかげで発表資料を書く時間をいただいた

×