Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ClangにReflection提案っぽいものを実装してみた話
Search
Koudai Iwahori
April 17, 2019
0
830
ClangにReflection提案っぽいものを実装してみた話
Koudai Iwahori
April 17, 2019
Tweet
Share
Featured
See All Featured
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
169
50k
Become a Pro
speakerdeck
PRO
25
5k
Git: the NoSQL Database
bkeepers
PRO
427
64k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
What's new in Ruby 2.0
geeforr
343
31k
GraphQLの誤解/rethinking-graphql
sonatard
67
10k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
10
720
Building an army of robots
kneath
302
43k
Put a Button on it: Removing Barriers to Going Fast.
kastner
59
3.5k
Making the Leap to Tech Lead
cromwellryan
133
8.9k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
665
120k
How to Think Like a Performance Engineer
csswizardry
20
1.1k
Transcript
ClangにReflection提案っぽい ものを実装してみた話 @HO_RI9191 C++ MIX #3
お詫び • 今日はMeta classの話はしません • MetaClassっぽいものは実装はしましたがReflectionの話でスライドがいっぱいに なってしまいました
何したの? • C++コンパイラのお勉強がてらReflection提案(P1240)とMetaclass提案 (P0707) を雑にClangに実装してみた ◦ https://github.com/ho-ri1991/clang-reflection-metaclass • コンパイラ初心者なのでガチプロの方はマサカリをご準備ください ※真面目にメンテが必要なプロジェクトの場合は
Clang Toolingなどで頑張る方が良いと思います c.f. CppCon 2018 “Building a C++ Reflection System in One Weekend Using Clang and LLVM” ※提案者(Andrew Sutton)の実装はこれ https://github.com/asutton/clang (読んでいないが、間違いなくこっちのほうが良い実装になっていると思います)
Clang概観
Clang/LLVM https://www.aosabook.org/en/llvm.html#fig.llvm.lcom
Clang Parser Lexer AST CodeGen Sema main.cpp “int” “main” (“
...... LLVM IR https://github.com/llvm-mirror/clang/tree/master/include/clang
Clang Parser Lexer AST CodeGen Sema main.cpp “int” “main” “(“
“)” LLVM IR clang -Xclang -dump-tokens -fsyntax-only main.cpp
Clang clang main.cpp -Xclang -ast-dump -fsyntax-only Parser Lexer AST CodeGen
Sema main.cpp “int” “main” “(“ “)” LLVM IR
Clang clang main.cpp -S -emit-llvm -o - Parser Lexer AST
CodeGen Sema main.cpp “int” “main” “(“ “)” LLVM IR
Reflection
Reflection? • プログラムがプログラム自身の構造を読み取れるようにする言語機能 • メタプログラミングを可能にする ◦ ソースコードをデータとして取り込む ◦ 何らかの操作をしてコード生成をする •
Dynamic Reflectionはいろんな言語で入っている ◦ なんだかんだあると便利だがコンパイル時計算とかには使えない
Reflection in C++ • ご存知のようにコンパイル時計算の需要がある ◦ Type traitsとかでいろいろ情報がとれるし 頑張ればいろいろ情報がとれる ◦
Conceptも入る • ただ、取れないものも多い ◦ クラス名 ◦ メンバ変数・関数、enumフィールドのリスト ◦ 足りないものが多い
Reflection TS (N4766, P0385) isocpp.org/std/status Approved for publication @ Kona
meeting https://isocpp.org/files/img/wg21-timeline-2019-04.png
Reflection TS (N4766, P0385) • Reflection TSではStatic Reflectionを入れようとしている ◦ より包括的なリフレクションのサポート
• コンパイル時にクラス・名前空間などのメタ情報を取れるようになる ◦ クラス名、Enumerator名、名前空間名 ◦ メンバ変数/関数のリスト (名前と型)、public/private、ポインタなど ◦ 基本は名前がついているものから取れる • 式の構文木は取れない • TMPをベースとした設計 • 今回は省略
Reflection TSの構文 • reflexpr ( operand ) という構文 ◦ メタ情報が入った実装依存の型が返ってくる
※非表示スライド
具体的に ※非表示スライド
Reflection TSの構文 • 名前を持ったローカルではないものであればReflectionを取れる ◦ 何が取れるかは今後変わると思います 1+1とかの式はダメ function-call-expr func-type-conv-exprだけ 関数名、テンプレート名はダメ
ローカルなEntityもダメ 関数引数、ラムダキャプチャは OK ※非表示スライド
Value Based Reflection (P1240) • 最近のconstexpr強化提案を踏まえてconstexprベースのStatic Reflectionにし ようとしている ◦ TMPはコンパイル時にメモリを食いまくる
◦ TMPはできれば読みたくない ◦ constexpr vectorやExpansion Statementを前提として作っている
Value Based Reflectionの構文 • reflexpr ( operand ) という構文 ◦
メタ情報が入った実装依存の値が返ってくる
Enum to String (Value Based Reflection) (P1240)
Enum to String (Value Based Reflection) Enumのメタ情報を保持した値
Enum to String (Value Based Reflection) EnumのreflectoinからEnumメンバのreflectionが入っ たconstexpr vector(P0784)を構築してくれるconstexpr 関数
Enumのメタ情報を保持した値
Enum to String (Value Based Reflection) Enumメンバの情報が入った constexprな vector(P0784)が返ってくる reflectionを値・型などに戻す演算子
Enumのメタ情報を保持した値
Enum to String (Value Based Reflection) Expansion Statement (P1306) (コンパイル時vectorやtupleのloop
expansion) Enumメンバの情報が入った constexprな vector(P0784)が返ってくる Enumのメタ情報を保持した値
Enum to String (Value Based Reflection)
Enum to String (Value Based Reflection)
Value Based Reflectionを実装してみる
Value Based Reflectionを実装してみる Enumのメタ情報を保持した値
Enumのメタ情報を保持した値 ? • コンパイラ内部のASTノードのポインタ = std::intptr_t (※実装例の一つ) ◦ P1240, P0721では関係ない整数型への暗黙の型変換を防ぐために
enum classやstructしてる clang main.cpp -Xclang -ast-dump -fsyntax-only
Enumのメタ情報を保持した値 ? • コンパイラ内部のASTノードのポインタ = std::intptr_t (※実装例の一つ) ◦ P1240, P0721では関係ない整数型への暗黙の型変換を防ぐために
enum classやstructしてる clang main.cpp -Xclang -ast-dump -fsyntax-only ASTノードのアドレス
Enumのメタ情報を保持した値 ? ASTノードのポインタを返すようにする このASTポインタを用いてReflectionのライブラリ関数群を実装する
構文を追加する Parser Lexer AST CodeGen Sema main.cpp “int” “main” “{“
...... LLVM IR このあたりを拡張する Parser, Semaは省略 ※予約後にする場合はLexerも必要 ※Reflectionはコンパイル時計算なので コード生成は触らなくてもできる ※実際どうやるのが正攻法かはわからない
Parser • Parse*()というメンバ関数が大量に生えている(再帰下降パーサ) • ParseAssignmentExpressionが走る直前にreflexprかどうかチェックする ◦ reflexprだったらreflexpr(T);まで読んでからSemaのメンバ関数を呼ぶ ◦ 具体的な方針は “Syntax
macros - a case study in extending Clang - LLVM Cauldron 2016” https://clang.llvm.org/doxygen/classclang_1_1Parser.html ※非表示スライド
Sema • ActOn*()/Build*()というメンバ関数が大量に生えている(AST組み立て) • reflexpr用の意味解析メンバ関数を生やす ◦ Parserから呼ばれてreflexpr用のASTノードを作る https://clang.llvm.org/doxygen/classclang_1_1Sema.html ※非表示スライド
AST (Node) • clang::Stmt ◦ If/For/BinaryOp/IntegerLiteralなどの文/式を表すASTノードの基底クラス • clang::Decl ◦ Enum/EnumField/class/struct/variable/Functionなどを表すASTノードの基底クラス
clang::EnumDecl clang::Decl clang::EnumConstantDecl
sizeof/alignofを参考にする • reflexprの構造はsizeofとだいたい同じ clang main.cpp -Xclang -ast-dump -fsyntax-only
sizeof/alignofのASTノード
reflexprのASTノード • siezof/alignofのコピペ clang::Reflexpr
コンパイル時評価方法を実装する コンパイル時計算のときにASTノードのポインタを返すようにする このASTを用いてReflectionのライブラリ関数群を実装する
Constexpr関数/変数の評価 • Constexprの評価は再帰的にASTノードを評価することでできる ◦ 各ノードの評価方法を実装する( c.f. clang/lib/AST/ExprConstant.cpp) • if statmentの具体例は次のページ
※実際のコードとは少し違います if condition Then else
コンパイル時評価方法を実装する • 戻り値の型はstd::uintptr_t • 値は型T(もしくは式)のAST(もしくはそれ導出できる内部ハンドル)
リフレクションライブラリ関数 • ASTポインタを整数にしたものをポインタに戻してから計算を行う ◦ 本当はちょっと面倒なことがいくつかあるが略 ※enum_sizeなんてものは 多分作られないと思います
Enum to String in Value Base Reflection Expansion Statement (P1306)
(コンパイル時vectorやtupleのloop expansion)
Expansion Statement (P1360) • Reflection自体は最終的なバイナリには残ってはいけない ◦ コンパイラ内のASTのポインタをバイナリに残してもアクセスできない • loop expansionしないと実行時にASTが必要になってしまう
偽Expansion Statementを実装する • constexpr vectorを作るのはちょっと大変 • やりたいことはメタ情報のリストを返すリフレクションのloop expansionなのでそれ だけ対応する •
やることは単純 ◦ forのbodyをパースしてbodyのASTを作る ◦ リストサイズの分だけその bodyをコピーする ◦ ループ変数の初期値をそれぞれ書き換える ▪ 実際には細かいところで結構ハマった
Reflection TSを越えて • identifierを生成して変数・関数宣言 ◦ 今はプリプロセッサマクロでしかできない ◦ Type/Value Based Reflection
(P0385/P1240)提案に含まれている • より簡単なCode Injectionの機構 (P0712, p0633) • Metaclass ◦ constexpr関数でクラスの生成規則を作る
References • clang ◦ https://clang.llvm.org/doxygen/index.html ◦ https://clang.llvm.org/docs/InternalsManual.html ◦ https://llvm.org/devmtg/2016-09/slides/Rink-SyntaxMacros.pdf ◦
https://www.youtube.com/watch?v=DUiUBt-fqEY • Reflection ◦ Reflection TS : n4750 ◦ Type Based Reflection : p0385 ◦ Value Based Reflection : p1240 ◦ Expansion Statements : p1306 ◦ p0712 ◦ p0993 ◦ https://github.com/asutton/clang