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
840
ClangにReflection提案っぽいものを実装してみた話
Koudai Iwahori
April 17, 2019
Tweet
Share
Featured
See All Featured
VelocityConf: Rendering Performance Case Studies
addyosmani
326
24k
Imperfection Machines: The Place of Print at Facebook
scottboms
266
13k
The Art of Programming - Codeland 2020
erikaheidi
53
13k
Adopting Sorbet at Scale
ufuk
73
9.1k
Stop Working from a Prison Cell
hatefulcrawdad
267
20k
The World Runs on Bad Software
bkeepers
PRO
65
11k
Docker and Python
trallard
42
3.1k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
45
2.2k
jQuery: Nuts, Bolts and Bling
dougneiner
61
7.5k
Designing for humans not robots
tammielis
250
25k
Into the Great Unknown - MozCon
thekraken
33
1.5k
Fireside Chat
paigeccino
34
3.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