building a c++ reflection system · 2018-11-18 · building a c++ reflection system using llvm and...

68
Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Upload: others

Post on 15-Apr-2020

15 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Building a C++ Reflection SystemUsing LLVM and Clang

1 — Meeting C++ 2018 / @ArvidGerstmann

Page 2: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Storytime

2 — Meeting C++ 2018 / @ArvidGerstmann

Page 3: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Wouldn't it be great ...

3 — Meeting C++ 2018 / @ArvidGerstmann

Page 4: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

struct User { uint64_t id; string name; vector<string> pets;};

User user;user.id = 42;user.name = "John";user.pets.push_back("Buddy");user.pets.push_back("Cooper");

string json = json::Stringify(&user);4 — Meeting C++ 2018 / @ArvidGerstmann

Page 5: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

{ "id": 42, "name": "John", "pets": ["Buddy", "Cooper"]}

5 — Meeting C++ 2018 / @ArvidGerstmann

Page 6: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

How do we do this?

6 — Meeting C++ 2018 / @ArvidGerstmann

Page 7: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

stringjson::Stringify(User const *user){ JsonSerializer serializer; serializer.SerializeInt64("id", user->id); serializer.SerializeString("name", user->name); serializer.BeginArray("pets"); for (auto const &pet : user->pets) serializer.ArrayAddString(pet); serializer.EndArray(); return serializer.ToString();}

7 — Meeting C++ 2018 / @ArvidGerstmann

Page 8: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

class User{ public ulong id; public string name; public List<string> pets = new List<string>();}

User user = new User();user.id = 42;user.name = "John";user.pets.Add("Buddy");user.pets.Add("Cooper");string json = JsonConvert.SerializeObject(user);

8 — Meeting C++ 2018 / @ArvidGerstmann

Page 9: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Reflection

9 — Meeting C++ 2018 / @ArvidGerstmann

Page 10: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Type t = user.GetType();

10 — Meeting C++ 2018 / @ArvidGerstmann

Page 11: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

• GetField(String, BindingFlags)• GetFields(BindingFlags)• GetInterface(String, Boolean)• GetInterfaces()• GetMethodImpl(String, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[])• GetMethods(BindingFlags)• GetNestedType(String, BindingFlags)• GetNestedTypes(BindingFlags)• GetProperties(BindingFlags)• GetPropertyImpl(String, BindingFlags, Binder, Type, Type[], ParameterModifier[])• HasElementTypeImpl()• InvokeMember(String, BindingFlags, Binder, Object, Object[], ParameterModifier[], CultureInfo, String[])• IsArrayImpl()• IsByRefImpl()• IsCOMObjectImpl()• IsPointerImpl()• IsPrimitiveImpl()

11 — Meeting C++ 2018 / @ArvidGerstmann

Page 12: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

• GetField(String, BindingFlags)• GetFields(BindingFlags)• GetInterface(String, Boolean)• GetInterfaces()• GetMethodImpl(String, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[])• GetMethods(BindingFlags)• GetNestedType(String, BindingFlags)• GetNestedTypes(BindingFlags)• GetProperties(BindingFlags)• GetPropertyImpl(String, BindingFlags, Binder, Type, Type[], ParameterModifier[])• HasElementTypeImpl()• InvokeMember(String, BindingFlags, Binder, Object, Object[], ParameterModifier[], CultureInfo, String[])• IsArrayImpl()• IsByRefImpl()• IsCOMObjectImpl()• IsPointerImpl()• IsPrimitiveImpl()

12 — Meeting C++ 2018 / @ArvidGerstmann

Page 13: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Type t = user.GetType();FieldInfo[] fields = t.GetFields(...);

foreach (var field in fields) { Console.WriteLine("Name: {0}", field.Name); Console.WriteLine("Type: {0}", field.FieldType); Console.WriteLine();}

13 — Meeting C++ 2018 / @ArvidGerstmann

Page 14: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Name: idType: System.UInt64

Name: nameType: System.String

Name: petsType: System.Collections.Generic.List`1[System.String]

14 — Meeting C++ 2018 / @ArvidGerstmann

Page 15: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Back to C++

15 — Meeting C++ 2018 / @ArvidGerstmann

Page 16: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Class const *c = GetClass<User>();

for (auto &field : c->Fields()) { printf("Name: %s\n", field.Name()); printf("Type: %s\n", field.Type().Name()); printf("\n");}

16 — Meeting C++ 2018 / @ArvidGerstmann

Page 17: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Blueprint

17 — Meeting C++ 2018 / @ArvidGerstmann

Page 18: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

struct Type { char const *name; size_t size;};

18 — Meeting C++ 2018 / @ArvidGerstmann

Page 19: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

struct Class : public Type { Field fields[N]; Function functions[N];};

19 — Meeting C++ 2018 / @ArvidGerstmann

Page 20: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

struct Field { Type *type; char const *name; size_t offset;};

struct Function { Field returnValue; Field parameters[N]; char const *name;};

20 — Meeting C++ 2018 / @ArvidGerstmann

Page 21: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

struct Type { char const *name; size_t size;};

struct Field { Type *type; char const *name; size_t offset;};

struct Function { Field returnValue; Field parameters[N]; char const *name;};

struct Class : public Type { Field fields[N]; Function functions[N];};

21 — Meeting C++ 2018 / @ArvidGerstmann

Page 22: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Data?

22 — Meeting C++ 2018 / @ArvidGerstmann

Page 23: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

struct User { uint64_t id; string name; vector<string> pets;};

23 — Meeting C++ 2018 / @ArvidGerstmann

Page 24: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Class const *GetClass<User>(){ static Class clazz; clazz.fields[0].type = GetType<uint64_t>(); clazz.fields[0].name = "id"; clazz.fields[0].offset = offsetof(User, id); clazz.fields[1].type = GetType<string>(); clazz.fields[1].name = "name"; clazz.fields[1].offset = offsetof(User, name); clazz.fields[2].type = GetType<vector<user>>(); clazz.fields[2].name = "pets"; clazz.fields[2].offset = offsetof(User, pets); return &clazz;}

24 — Meeting C++ 2018 / @ArvidGerstmann

Page 25: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Undefined symbols for architecture x86_64: "Type const* GetType<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >()", referenced from: _main in Untitled 7-0fb8bc.o "Type const* GetType<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >()", referenced from: _main in Untitled 7-0fb8bc.o "Type const* GetType<unsigned long long>()", referenced from: _main in Untitled 7-0fb8bc.old: symbol(s) not found for architecture x86_64

25 — Meeting C++ 2018 / @ArvidGerstmann

Page 26: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

"Primitive" Types

26 — Meeting C++ 2018 / @ArvidGerstmann

Page 27: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

template<>Type const *GetType<int>(){ static Type t{"int", sizeof(int)}; return &t;}

27 — Meeting C++ 2018 / @ArvidGerstmann

Page 28: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

template<class T>Type const *GetType(){ return detail::GetTypeImpl(TypeTag<T>{});}

template<class T>Type const *GetTypeImpl(TypeTag<vector<T>>){ /* ... */}

28 — Meeting C++ 2018 / @ArvidGerstmann

Page 29: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Class const *GetClassImpl(ClassTag<User>){ static Class clazz; clazz.fields[0].type = GetType<uint64_t>(); clazz.fields[0].name = "id"; clazz.fields[0].offset = offsetof(User, id); clazz.fields[1].type = GetType<string>(); clazz.fields[1].name = "name"; clazz.fields[1].offset = offsetof(User, name); clazz.fields[2].type = GetType<vector<user>>(); clazz.fields[2].name = "pets"; clazz.fields[2].offset = offsetof(User, pets); return &clazz;}

29 — Meeting C++ 2018 / @ArvidGerstmann

Page 30: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Class const *c = GetClass<User>();

for (auto &field : c->Fields()) { printf("Name: %s\n", field.Name()); printf("Type: %s\n", field.Type().Name()); printf("\n");}

30 — Meeting C++ 2018 / @ArvidGerstmann

Page 31: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Name: idType: uint64_t

Name: nameType: std::string

Name: petsType: std::vector<std::string>

31 — Meeting C++ 2018 / @ArvidGerstmann

Page 32: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

LLVM

32 — Meeting C++ 2018 / @ArvidGerstmann

Page 33: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Clang

33 — Meeting C++ 2018 / @ArvidGerstmann

Page 34: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

LibTooling

34 — Meeting C++ 2018 / @ArvidGerstmann

Page 35: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

How?

35 — Meeting C++ 2018 / @ArvidGerstmann

Page 36: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Hello, AST

36 — Meeting C++ 2018 / @ArvidGerstmann

Page 37: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

struct Foo { volatile int bar; float baz;};

37 — Meeting C++ 2018 / @ArvidGerstmann

Page 38: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

% clang -Xclang -ast-dump -fsyntax-only foo.h

TranslationUnitDecl 0x7ff8b00264d0 <<invalid sloc>> <invalid sloc>`-RecordDecl 0x7f9f2a827120 <foo.h:1:1, line:4:1> line:1:8 struct Foo definition |-FieldDecl 0x7f9f2a877400 <line:2:5, col:18> col:18 bar 'volatile int' `-FieldDecl 0x7f9f2a877460 <line:3:5, col:11> col:11 baz 'float'

38 — Meeting C++ 2018 / @ArvidGerstmann

Page 39: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

libTooling AST Visitor

39 — Meeting C++ 2018 / @ArvidGerstmann

Page 40: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

struct DumpASTAction : public ASTFrontendAction{ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef inFile) override { return clang::CreateASTDumper( nullptr,/* dump to stdout */ "", /* no filter */ true, /* dump decls */ true, /* deserialize */ false /* don't dump lookups */ ); }};

static llvm::cl::OptionCategory gToolCategory("metareflect options");int main(int argc, char **argv){ CommonOptionsParser optionsParser(argc, argv, gToolCategory); ClangTool tool(optionsParser.getCompilations(), optionsParser.getSourcePathList()); return tool.run(newFrontendActionFactory<DumpASTAction>().get());}

40 — Meeting C++ 2018 / @ArvidGerstmann

Page 41: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

% ./metareflect foo.h -- -I. $CFLAGS

TranslationUnitDecl 0x7ff8b00264d0 <<invalid sloc>> <invalid sloc>`-RecordDecl 0x7f9f2a827120 <foo.h:1:1, line:4:1> line:1:8 struct Foo definition |-FieldDecl 0x7f9f2a877400 <line:2:5, col:18> col:18 bar 'volatile int' `-FieldDecl 0x7f9f2a877460 <line:3:5, col:11> col:11 baz 'float'

41 — Meeting C++ 2018 / @ArvidGerstmann

Page 42: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

#include <stdint.h>#include <vector>#include <string>

struct User{ uint64_t id; std::string name; std::vector<std::string> pets;};

42 — Meeting C++ 2018 / @ArvidGerstmann

Page 43: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

106320 lines of AST

43 — Meeting C++ 2018 / @ArvidGerstmann

Page 44: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

2370 CXXRecordDecl nodes

44 — Meeting C++ 2018 / @ArvidGerstmann

Page 45: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

1004 FieldDecl nodes

45 — Meeting C++ 2018 / @ArvidGerstmann

Page 46: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

We need a better plan

46 — Meeting C++ 2018 / @ArvidGerstmann

Page 47: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

struct __attribute__((annotate("reflect"))) User{ __attribute__((annotate("reflect"))) uint64_t id; __attribute__((annotate("reflect"))) string name; __attribute__((annotate("reflect"))) vector<string> pets;};

47 — Meeting C++ 2018 / @ArvidGerstmann

Page 48: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

#define CLASS() class __attribute__((annotate("reflect-class")))#define PROPERTY() __attribute__((annotate("reflect-property")))

CLASS() User{public: PROPERTY() uint64_t id;

PROPERTY() string name;

PROPERTY() vector<string> pets;};

48 — Meeting C++ 2018 / @ArvidGerstmann

Page 49: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

ClassFinder classFinder;MatchFinder finder;

DeclarationMatcher classMatcher = cxxRecordDecl(decl().bind("id"), hasAttr(attr::Annotate));DeclarationMatcher propertyMatcher = fieldDecl(decl().bind("id"), hasAttr(attr::Annotate));DeclarationMatcher functionMatcher = functionDecl(decl().bind("id"), hasAttr(attr::Annotate));

finder.addMatcher(classMatcher, &classFinder);finder.addMatcher(propertyMatcher, &classFinder);finder.addMatcher(functionMatcher, &classFinder);

49 — Meeting C++ 2018 / @ArvidGerstmann

Page 50: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

struct ClassFinder : public MatchFinder::MatchCallback{ virtual void run(MatchFinder::MatchResult const &result);

virtual void onStartOfTranslationUnit();

virtual void onEndOfTranslationUnit();};

50 — Meeting C++ 2018 / @ArvidGerstmann

Page 51: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

virtual voidClassFinder::run(MatchFinder::MatchResult const &result) override{ CXXRecordDecl const *record = result.Nodes.getNodeAs<clang::CXXRecordDecl>("id"); if (record) return FoundRecord(record);

FieldDecl const *field = result.Nodes.getNodeAs<clang::FieldDecl>("id"); if (field) return FoundField(field);

FunctionDecl const *function = result.Nodes.getNodeAs<clang::FunctionDecl>("id"); if (function) return FoundFunction(function);}

51 — Meeting C++ 2018 / @ArvidGerstmann

Page 52: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

voidClassFinder::FoundRecord(CXXRecordDecl const *record){ record->dump();}

voidClassFinder::FoundField(FieldDecl const *field){ field->dump();}

voidClassFinder::FoundFunction(FunctionDecl const *function){ function->dump();}

52 — Meeting C++ 2018 / @ArvidGerstmann

Page 53: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

int main(int argc char **argv){ /* ... */

return tool.run(newFrontendActionFactory(&finder).get());}

53 — Meeting C++ 2018 / @ArvidGerstmann

Page 54: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

CXXRecordDecl 0x7fcda1bae7e0 <./metareflect.hxx:19:24, test.hxx:130:1> line:115:9 class User definition|-DefinitionData aggregate standard_layout| |-DefaultConstructor exists non_trivial needs_implicit| |-CopyConstructor simple non_trivial has_const_param needs_overload_resolution implicit_has_const_param| |-MoveConstructor exists simple non_trivial needs_overload_resolution| |-CopyAssignment non_trivial has_const_param needs_implicit implicit_has_const_param| |-MoveAssignment exists simple non_trivial needs_overload_resolution| `-Destructor simple non_trivial needs_overload_resolution|-AnnotateAttr 0x7fcda1bae908 <./metareflect.hxx:19:45, col:83> "reflect-class;"|-CXXRecordDecl 0x7fcda1bae960 <col:24, test.hxx:115:9> col:9 implicit class User|-AccessSpecDecl 0x7fcda1bae9f8 <line:118:1, col:7> col:1 public|-FieldDecl 0x7fcda1baea80 <./metareflect.hxx:21:27, test.hxx:121:14> col:14 id 'uint64_t':'unsigned long long'| `-AnnotateAttr 0x7fcda1baeac8 <./metareflect.hxx:21:42, col:83> "reflect-property;Serialized"|-FieldDecl 0x7fcda1baebb0 <col:27, test.hxx:125:12> col:12 name 'string':'std::__1::basic_string<char>'| `-AnnotateAttr 0x7fcda1baebf8 <./metareflect.hxx:21:42, col:83> "reflect-property;Serialized"|-FieldDecl 0x7fcda227a228 <col:27, test.hxx:129:20> col:20 pets 'vector<string>':'std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >'| `-AnnotateAttr 0x7fcda227a270 <./metareflect.hxx:21:42, col:83> "reflect-property;Serialized"|-CXXConstructorDecl 0x7fcda227a328 <test.hxx:115:9> col:9 implicit User 'void (const User &)' inline default noexcept-unevaluated 0x7fcda227a328| `-ParmVarDecl 0x7fcda227a460 <col:9> col:9 'const User &'|-CXXConstructorDecl 0x7fcda227a4f8 <col:9> col:9 implicit User 'void (User &&)' inline default noexcept-unevaluated 0x7fcda227a4f8| `-ParmVarDecl 0x7fcda227a630 <col:9> col:9 'User &&'|-CXXMethodDecl 0x7fcda227a6c8 <col:9> col:9 implicit operator= 'User &(User &&)' inline default noexcept-unevaluated 0x7fcda227a6c8| `-ParmVarDecl 0x7fcda227a7f0 <col:9> col:9 'User &&'`-CXXDestructorDecl 0x7fcda227a878 <col:9> col:9 implicit ~User 'void ()' inline default noexcept-unevaluated 0x7fcda227a878

FieldDecl 0x7fcda1baea80 <./metareflect.hxx:21:27, test.hxx:121:14> col:14 id 'uint64_t':'unsigned long long'`-AnnotateAttr 0x7fcda1baeac8 <./metareflect.hxx:21:42, col:83> "reflect-property;Serialized"

FieldDecl 0x7fcda1baebb0 <./metareflect.hxx:21:27, test.hxx:125:12> col:12 name 'string':'std::__1::basic_string<char>'`-AnnotateAttr 0x7fcda1baebf8 <./metareflect.hxx:21:42, col:83> "reflect-property;Serialized"

FieldDecl 0x7fcda227a228 <./metareflect.hxx:21:27, test.hxx:129:20> col:20 pets 'vector<string>':'std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >'`-AnnotateAttr 0x7fcda227a270 <./metareflect.hxx:21:42, col:83> "reflect-property;Serialized"

54 — Meeting C++ 2018 / @ArvidGerstmann

Page 55: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

voidClassFinder::FoundRecord(CXXRecordDecl const *record){ m_fileName = m_sourceman->getFilename(record->getLocation()); m_fileName.erase(m_fileName.end() - 4, m_fileName.end()); m_fileName.append(".generated.hxx"); m_classes.emplace_back(ReflectedClass(record));}

voidClassFinder::FoundField(FieldDecl const *field){ m_classes.back().AddField(field);}

voidClassFinder::FoundFunction(FunctionDecl const *function){ m_classes.back().AddFunction(function);}

55 — Meeting C++ 2018 / @ArvidGerstmann

Page 56: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

virtual voidClassFinder::onEndOfTranslationUnit() override{ std::error_code ec; raw_fd_ostream os(m_fileName, ec); assert(!ec && "error opening file"); for (auto &ref : m_classes) ref.Generate(m_context, os); m_classes.clear();}

56 — Meeting C++ 2018 / @ArvidGerstmann

Page 57: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Code Generation

57 — Meeting C++ 2018 / @ArvidGerstmann

Page 58: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

voidReflectedClass::Generate(ASTContext *ctx, raw_ostream &os){ /* ... */ os << "template<>\n" << "Class const *\n" << "detail::GetClassImpl(ClassTag<" << type << ">)\n" << "{\n" << "static Class c;\n";

FieldGenerator fieldGenerator(ctx, type); for (size_t i = 0, n = m_fields.size(); i < n; ++i) fieldGenerator.Generate(m_fields[i], i, os);

os << "}\n";}

58 — Meeting C++ 2018 / @ArvidGerstmann

Page 59: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

voidFieldGenerator::Generate(FieldDecl const *field, size_t i, raw_ostream &os){ os << "c.fields[" << i << "].type = " << typeName << ";\n";

os << "c.fields[" << i << "].name = " << fieldName << ";\n";

os << "c.fields[" << i << "].offset = " << "offsetof(" << typeName << ", " << fieldName << ");\n";}

59 — Meeting C++ 2018 / @ArvidGerstmann

Page 60: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Demo

60 — Meeting C++ 2018 / @ArvidGerstmann

Page 61: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Future

61 — Meeting C++ 2018 / @ArvidGerstmann

Page 62: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Thank You

62 — Meeting C++ 2018 / @ArvidGerstmann

Page 63: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Links

→Working implementation:github.com/leandros/metareflect

→Twitter:twitter.com/ArvidGerstmann

→My Blog:arvid.io

63 — Meeting C++ 2018 / @ArvidGerstmann

Page 64: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Bonus

64 — Meeting C++ 2018 / @ArvidGerstmann

Page 65: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Class Storage

class Class { /* ... */protected: Class *m_baseClass; Field *m_fields; Field *m_fieldsEnd; Function *m_functions; Function *m_functionsEnd; char const *m_name; size_t m_flags;};

65 — Meeting C++ 2018 / @ArvidGerstmann

Page 66: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Class Storage

template<class Type, size_t NFields, size_t NFunctions, size_t NTemplateArgs>struct ClassStorage { template<class Lambda> ClassStorage(Lambda &&ctor) noexcept { ctor(this); } size_t const numFields = NFields; size_t const numFunctions = NFunctions; size_t const numTemplateArgs = NTemplateArgs; Field fields[NFields + 1]; Function functions[NFunctions + 1]; TemplateArgument templateArgs[NTemplateArgs + 1];};

66 — Meeting C++ 2018 / @ArvidGerstmann

Page 67: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

LLVM Setup

1. Clone LLVM

$ git clone https://git.llvm.org/git/llvm.git/ llvm2. Clone clang into '$LLVM/tools/'

$ cd llvm/tools$ git clone https://git.llvm.org/git/clang.git/

3. Clone clang-extra-tools into '$LLVM/tools/clang/tools/extra'

$ cd clang/tools$ git clone https://git.llvm.org/git/clang-tools-extra.git/ extra

4. Add your project project

$ mkdir yourproject$ touch yourproject/CMakeLists.txt$ echo "add_subdirectory(yourproject)" >> extra/CMakeLists.txt

5. Generate the project using CMake

$ cmake -G"Ninja"

67 — Meeting C++ 2018 / @ArvidGerstmann

Page 68: Building a C++ Reflection System · 2018-11-18 · Building a C++ Reflection System Using LLVM and Clang 1 — Meeting C++ 2018 / @ArvidGerstmann

Annotations

#define CLASS(...) class __attribute__((annotate("reflect-class;" #__VA_ARGS__)))#define UNION(...) union __attribute__((annotate("reflect-class;" #__VA_ARGS__)))#define PROPERTY(...) __attribute__((annotate("reflect-property;" #__VA_ARGS__)))#define FUNCTION(...) __attribute__((annotate("reflect-function;" #__VA_ARGS__)))

CLASS(Serialized) User{ PROPERTY(Serialized) uint64_t id;

PROPERTY(Serialized) string name;

PROPERTY(Serialized) vector<string> pets;};

68 — Meeting C++ 2018 / @ArvidGerstmann