1
(they/them), @TartanLlama C++ Developer Advocate at Microsoft 2
Dynamic Polymorphism 3
Polymorphism:
The provision of a single interface to entities of different types 4 Dynamic Static Polymorphism Polymorphism 5 Dynamic Static Polymorphism Polymorphism
Run-time Compile-time 6 Dynamic Static Polymorphism Polymorphism
Run-time Compile-time Different behaviour based on Different behaviour based on dynamic type static type 7 Dynamic Static Polymorphism Polymorphism
Run-time Compile-time Different behaviour based on Different behaviour based on dynamic type static type Typically implemented with Typically implemented with inheritance overloading and templates 8 9 10 Problems with inheritance 11
Often requires dynamic allocation Dynamic Allocation 12
struct base{}; struct a : base{}; struct b : base{};
base make_base();
std::vector
struct base{}; struct a : base{}; struct b : base{};
std::unique_ptr
std::vector
Often requires dynamic allocation Ownership and nullability considerations Problems with inheritance 15
Often requires dynamic allocation Ownership and nullability considerations Intrusive: requires modifying child classes Intrusive Polymorphism 16 namespace mylib { struct base { virtual void do_thing(); }; } namespace otherlib { struct x { virtual void do_thing(); }; } Intrusive Polymorphism 17 namespace mylib { struct base { virtual void do_thing(); }; } namespace otherlib { struct x { virtual void do_thing(); }; } mylib::base* b = new otherlib::x; Problems with inheritance 18
Often requires dynamic allocation Ownership and nullability considerations Intrusive: requires modifying child classes No more value semantics Problems with inheritance 19
Often requires dynamic allocation Ownership and nullability considerations Intrusive: requires modifying child classes No more value semantics Changes semantics for algorithms and containers 20 struct animal { virtual ~animal(); virtual void speak() = 0; }; struct cat : animal { void speak() override; }; struct dog : animal { void speak() override; }; 21 struct animal { virtual ~animal(); Felix virtual void speak() = 0; }; struct cat : animal { void speak() override; }; struct dog : animal { void speak() override; }; 22 struct animal { virtual ~animal(); Felix virtual void speak() = 0; }; struct cat : animal { void speak() override; }; vtable for cat struct dog : animal { void speak() override; }; 23 struct animal { virtual ~animal(); Felix virtual void speak() = 0; }; struct cat : animal { void speak() override; }; vtable for cat struct dog : animal { void speak() override; }; struct animal { 24 // magic }; struct cat { void speak(); }; struct dog { void speak(); }; struct animal { 25 // magic }; int main() { animal c = cat{}; struct cat { c.speak(); void speak(); }; animal d = dog{}; d.speak(); struct dog { } void speak(); }; Hand-written virtual functions 26
Declare vtable for the abstract interface Define vtable for a concrete type Capture the vtable pointers on construction Forward calls through the vtable 27 struct animal { virtual ~animal(); virtual void speak() = 0; }; struct cat : animal { void speak() override; }; struct dog : animal { void speak() override; }; Hand-written virtual functions 28
Declare vtable for the abstract interface Define vtable for a concrete type Capture the vtable pointers on construction Forward calls through the vtable 29
struct vtable { void (*speak)(void* ptr); void (*destroy_)(void* ptr); }; Hand-written virtual functions 30
Declare vtable for the abstract interface Define vtable for a concrete type Capture the vtable pointers on construction Forward calls through the vtable 31
template
template
Declare vtable for the abstract interface Define vtable for a concrete type Capture the vtable pointers on construction Forward calls through the vtable 34 struct animal { void* concrete_; vtable const* vtable_;
}; 35 struct animal { void* concrete_; vtable const* vtable_;
template
Declare vtable for the abstract interface Define vtable for a concrete type Capture the vtable pointers on construction Forward calls through the vtable 37
struct animal { // ...
void speak() { vtable_->speak(t_); } ~animal() { vtable_->destroy_(t_); } }; struct cat { 38 void speak(); }; struct dog { void speak(); }; struct cat { 39 void speak(); }; struct dog { void speak(); }; int main() { animal c = cat{}; c.speak();
animal d = dog{}; d.speak(); } Problems with inheritance 40
Often requires dynamic allocation Ownership and nullability considerations Intrusive: requires modifying child classes No more value semantics Changes semantics for algorithms and containers Problems with inheritance 41
Often requires dynamic allocation Ownership and nullability considerations Intrusive: requires modifying child classes No more value semantics Changes semantics for algorithms and containers Problems with inheritance 42
Often requires dynamic allocation Ownership and nullability considerations Intrusive: requires modifying child classes No more value semantics Changes semantics for algorithms and containers struct vtable { 43 void (*speak)(void* ptr); void (*destroy_)(void* ptr); void* (*clone_)(void* ptr); void* (*move_clone_)(void* ptr); }; struct vtable { 44 void (*speak)(void* ptr); void (*destroy_)(void* ptr); void* (*clone_)(void* ptr); void* (*move_clone_)(void* ptr); }; template
animal(animal const& rhs) : t_(rhs.vtable_->clone_(rhs.t_)), vtable_(rhs.vtable_) {}
animal(animal&& rhs) : t_(rhs.vtable_->move_clone_(rhs.t_)), vtable_(rhs.vtable_) {} }; int main() { 46 animal a = cat{}; a.speak(); a = dog{}; a.speak();
animal b = a; b.speak(); } int main() { 47 std::vector
for (auto&& a : animals) { a.speak(); }
} Problems with inheritance 48
Often requires dynamic allocation Ownership and nullability considerations Intrusive: requires modifying child classes No more value semantics Changes semantics for algorithms and containers Problems with inheritance 49
Often requires dynamic allocation Ownership and nullability considerations Intrusive: requires modifying child classes No more value semantics Changes semantics for algorithms and containers Problems with inheritance 50
Often requires dynamic allocation Ownership and nullability considerations Intrusive: requires modifying child classes No more value semantics Changes semantics for algorithms and containers struct vtable { void (*speak)(void* ptr); void (*destroy_)(void* ptr); void* (*clone_)(void* ptr); void* (*move_clone_)(void* ptr); 51 }; template
void speak() { vtable_->speak(t_); } ~animal() { vtable_->destroy_(t_); }
template
animal(animal const& rhs) : t_(rhs.vtable_->clone_(rhs.t_)), vtable_(rhs.vtable_) {}
animal(animal&& rhs) : t_(rhs.vtable_->move_clone_(rhs.t_)), vtable_(rhs.vtable_) {}
animal& operator=(animal const& rhs) { t_ = rhs.vtable_->clone_(rhs.t_); vtable_ = rhs.vtable_; return *this; }
animal& operator=(animal&& rhs) { t_ = rhs.vtable_->move_clone_(rhs.t_); vtable_ = rhs.vtable_; return *this; } }; 52
Static Reflection 53
Reflection:
The ability of a program to introspect its own structure 54
Static Reflection:
The ability of a program to introspect its own structure at compile-time 55
std::is_array
std::is_same
Enum to string? Iterate over members? 58 59 template
} 60 template
}
} 61 template
}
} 62 template
}
} 63 template
} }
} 64 template
} }
} 65 template
} 66 template
typename(reflection): reflection -> type Reifiers 69
typename(reflection): reflection -> type namespace(reflection): reflection -> namespace Reifiers 70
typename(reflection): reflection -> type namespace(reflection): reflection -> namespace template(reflection): reflection -> template Reifiers 71
typename(reflection): reflection -> type namespace(reflection): reflection -> namespace template(reflection): reflection -> template valueof(reflection): reflection -> value, so long as the reflection designates a constant expression Reifiers 72
typename(reflection): reflection -> type namespace(reflection): reflection -> namespace template(reflection): reflection -> template valueof(reflection): reflection -> value, so long as the reflection designates a constant expression exprid(reflection): reflection -> id-expression representing the entity Reifiers 73
typename(reflection): reflection -> type namespace(reflection): reflection -> namespace template(reflection): reflection -> template valueof(reflection): reflection -> value, so long as the reflection designates a constant expression exprid(reflection): reflection -> id-expression representing the entity [: reflection :] or unqualid(reflection): reflection -> identifier Reifiers 74
typename(reflection): reflection -> type namespace(reflection): reflection -> namespace template(reflection): reflection -> template valueof(reflection): reflection -> value, so long as the reflection designates a constant expression exprid(reflection): reflection -> id-expression representing the entity [: reflection :] or unqualid(reflection): reflection -> identifier [< reflection >] or templarg(reflection): reflection -> template argument 75
Code Injection 76
class point { int x; int y; };
77
class point { int x; int y; public: int get_x() const { return x; } int get_y() const { return y; } }; 78
class point { int x; int y;
consteval { generate_getters(reflexpr(point)); } }; 79
consteval void generate_getters(meta::info cls) { for (auto member : meta::members_of(cls)) { if (meta::is_nonstatic_data_member(member)) { generate_getter(member); } } } 80
consteval void generate_getter(meta::info member) { -> fragment struct {
}; } 81
class point { int x; int y;
consteval { generate_getters(reflexpr(point)); } }; 82
consteval void generate_getter(meta::info member) { -> fragment struct {
}; } 83
consteval void generate_getter(meta::info member) { -> fragment struct { int const& get_x() const {
} }; } 84
consteval void generate_getter(meta::info member) { -> fragment struct { int const& get_x() const {
} }; } 85
consteval void generate_getter(meta::info member) { -> fragment struct { typename(meta::type_of(%{member})) const& get_x() const {
} }; } 86
consteval void generate_getter(meta::info member) { -> fragment struct { typename(meta::type_of(%{member})) const& get_x() const {
} }; } 87
consteval void generate_getter(meta::info member) { -> fragment struct { typename(meta::type_of(%{member})) const& unqualid("get_", %{member})() const {
} }; } 88
consteval void generate_getter(meta::info member) { -> fragment struct { typename(meta::type_of(%{member})) const& unqualid("get_", %{member})() const { return this->idexpr(%{member}); } }; } 89
class point { int x; int y;
consteval { generate_getters(reflexpr(point)); } }; 90
class point { int x; int y; public: int const& get_x() const { return x; } int const& get_y() const { return y; } }; 91
Code Injection + Dynamic Polymorphism 92
struct animal { void* concrete_; vtable const* vtable_;
// ctors, forwarding functions, etc. }; 93 template
// ctors, forwarding functions, etc. }; 94 template
// ctors, forwarding functions, etc. }; struct animal_facade { void speak(); }; 95 template
// ctors, forwarding functions, etc. }; struct animal_facade { void speak(); }; using animal = typeclass_for
Declare vtable for the abstract interface Define vtable for a concrete type Capture the vtable pointers on construction Forward calls through the vtable Code-injected virtual functions 97
Declare vtable for the abstract interface Define vtable for a concrete type Capture the vtable pointers on construction Forward calls through the vtable 98 struct vtable { void (*speak)(void* ptr); void (*destroy_)(void* ptr); void* (*clone_)(void* ptr); void* (*move_clone_)(void* ptr); }; 99 struct vtable { void (*speak)(void* ptr); template
} template
});
} template
} template
} template
} template
} template
} template
} Code-injected virtual functions 109
Declare vtable for the abstract interface Define vtable for a concrete type Capture the vtable pointers on construction Forward calls through the vtable 110
vtable
table.speak = [] (void* ptr) { static_cast
-> fragment { table.speak = [] (void* ptr) { static_cast
113
-> fragment { table.unqualid(%{func}) = [] (void* ptr) { static_cast
114
-> fragment { table.unqualid(%{func}) = [] (void* ptr, ->%{params}) { static_cast
115
-> fragment { table.unqualid(%{func}) = [] (void* ptr, ->%{params}) { return static_cast
Code-injected virtual functions 116
Declare vtable for the abstract interface Define vtable for a concrete type Capture the vtable pointers on construction Forward calls through the vtable 117 template
}; 118 template
template
Declare vtable for the abstract interface Define vtable for a concrete type Capture the vtable pointers on construction Forward calls through the vtable 120 template
consteval { for_each_declared_function(reflexpr(Facade), [](auto func, auto ret, auto params) consteval { -> fragment struct { typename(%{ret}) unqualid(%{func}) (->%{params}) { return this->vtable_->unqualid(%{func}) (this->concrete_, unqualid(... %{params})); } }; }); } }; struct vtable { void (*speak)(void* ptr); void (*destroy_)(void* ptr); void* (*clone_)(void* ptr); void* (*move_clone_)(void* ptr); 121 }; template
void speak() { vtable_->speak(t_); } ~animal() { vtable_->destroy_(t_); }
template
animal(animal const& rhs) : t_(rhs.vtable_->clone_(rhs.t_)), vtable_(rhs.vtable_) {}
animal(animal&& rhs) : t_(rhs.vtable_->move_clone_(rhs.t_)), vtable_(rhs.vtable_) {}
animal& operator=(animal const& rhs) { t_ = rhs.vtable_->clone_(rhs.t_); vtable_ = rhs.vtable_; return *this; }
animal& operator=(animal&& rhs) { t_ = rhs.vtable_->move_clone_(rhs.t_); vtable_ = rhs.vtable_; return *this; } }; 122
struct animal_facade { void speak(); }; using animal = typeclass_for
template
// ctors, forwarding functions, etc. }; 124
template
// ctors, forwarding functions, etc. }; 125
template
// ctors, forwarding functions, etc. }; 126
typeclass_for
typeclass_for
typeclass_for
typeclass_for
Often requires dynamic allocation Ownership and nullability considerations Intrusive: requires modifying child classes No more value semantics Changes semantics for algorithms and containers Problems with inheritance 131
Often requires dynamic allocation Ownership and nullability considerations Intrusive: requires modifying child classes No more value semantics Changes semantics for algorithms and containers 132
struct animal_facade { void speak(); }; using animal = typeclass_for
class(typeclass) animal { void speak(); }; 134
Metaclasses 135
Metaclass functions let programmers write a new kind of efficient abstraction: a user- defined named subset of classes that share common characteristics, typically (but not limited to):
• user-defined rules • defaults, and • generated functions 136
class point { int x; int y; };
137
class(value) point { int x; int y; };
138
consteval void value (meta::info source) { //injection statements } 139
class(value) point { int x; int y; }; 140 namespace __hidden { struct prototype { int x; int y; class(value) point { }; int x; } int y; }; 141 namespace __hidden { struct prototype { int x; int y; class(value) point { }; int x; } int y; }; struct point { using prototype = __hidden::prototype; consteval { value(reflexpr(prototype)); } }; 142 class Shape { public: virtual int area() const =0; virtual void scale_by(double factor) =0; // ... etc. virtual ~Shape() noexcept { };
// be careful not to write nonpublic/nonvirtual function }; // or copy/move function or data member; no enforcement 143
class(interface) Shape { int area() const; void scale_by(double factor); }; consteval void interface(info source) { 144
}; consteval void interface(info source) { 145 bool has_dtor = false; for (auto mem : range(source)) {
}
}; consteval void interface(info source) { 146 bool has_dtor = false; for (auto mem : range(source)) { compiler_require(!is_data_member(mem), "interfaces may not contain data"); compiler_require(!is_copy(mem) && !is_move(mem), "interfaces may not copy or move; consider " "a virtual clone() instead");
}
}; consteval void interface(info source) { 147 bool has_dtor = false; for (auto mem : range(source)) { compiler_require(!is_data_member(mem), "interfaces may not contain data"); compiler_require(!is_copy(mem) && !is_move(mem), "interfaces may not copy or move; consider " "a virtual clone() instead"); if (has_default_access(mem)) make_public(mem); make_pure_virtual(mem); compiler_require(is_public(mem), "interface functions must be public"); has_dtor |= is_destructor(mem);
}
}; consteval void interface(info source) { 148 bool has_dtor = false; for (auto mem : range(source)) { compiler_require(!is_data_member(mem), "interfaces may not contain data"); compiler_require(!is_copy(mem) && !is_move(mem), "interfaces may not copy or move; consider " "a virtual clone() instead"); if (has_default_access(mem)) make_public(mem); make_pure_virtual(mem); compiler_require(is_public(mem), "interface functions must be public"); has_dtor |= is_destructor(mem); -> mem; }
}; consteval void interface(info source) { 149 bool has_dtor = false; for (auto mem : range(source)) { compiler_require(!is_data_member(mem), "interfaces may not contain data"); compiler_require(!is_copy(mem) && !is_move(mem), "interfaces may not copy or move; consider " "a virtual clone() instead"); if (has_default_access(mem)) make_public(mem); make_pure_virtual(mem); compiler_require(is_public(mem), "interface functions must be public"); has_dtor |= is_destructor(mem); -> mem; } if (!has_dtor) -> __fragment struct Z { virtual ~X() noexcept {} }; }; 150
consteval void typeclass (meta::info source) { //injection statements } 151
class(typeclass) animal { void speak(); }; std::vector
Runtime performance Compile-time performance New and incompatible way of specifying concepts Future Work 153
https://wg21.link/p0957 Future Wark 154
https://wg21.link/p2279 Links 155
Code for this talk: https://godbolt.org/z/McT4Tb Prototype implementation: https://github.com/TartanLlama/typeclasses Experimental compiler: https://github.com/lock3/meta On Compiler Explorer: https://cppx.godbolt.org/ Scalable Reflection in C++ Paper: https://wg21.link/P1240 Metaclasses paper: https://wg21.link/P0707