Mircea Baja @ Sophos - 15th May 2018
Repeat of the presentation I gave @ ACCU Oxford - 29th January 2018
{ "name": "Kipper", "breed": "Labrador"}
class bad_dog{private: std::shared_ptr<std::string> name_; std::shared_ptr<std::string> breed_;public: std::shared_ptr<std::string> get_name(); void set_name(std::shared_ptr<std::string> value); std::shared_ptr<std::string> get_breed(); void set_breed(std::shared_ptr<std::string> value); void init(const Json & doc);};
struct dog{ std::string name; std::string breed;};dog dog_from_json(const Json & doc);
struct dog{ std::string name; std::string breed;};dog dog_from_json(const Json & doc);
Physical layout:
dog dog_from_json(const Json & doc){ dog x; x.name = doc['name'].as_string(); x.breed = doc['breed'].as_string(); return x;}
dog dog_from_json(const Json & doc){ dog x; x.name = doc['name'].as_string(); x.breed = doc['breed'].as_string(); return x;}
dog dog_from_json(const Json & doc){ return { doc['name'].as_string(), doc['breed'].as_string() };}
bool operator==(const dog & left, const dog & right) noexcept{ if (left.name != right.name) { return false; } return left.breed == right.breed;}
bool operator==(const dog & left, const dog & right) noexcept{ if (left.name != right.name) { return false; } return left.breed == right.breed;}
bool operator==(const dog & left, const dog & right) noexcept{ return (left.name == right.name) && (left.breed == right.breed);}
bool operator<(const dog & left, const dog & right) noexcept{ if (left.name < right.name) { return true; } if (left.name > right.name) { return false; } return left.breed < right.breed;}
bool operator<(const dog & left, const dog & right) noexcept{ if (left.name < right.name) { return true; } if (left.name > right.name) { return false; } return left.breed < right.breed;}
bool operator<(const dog & left, const dog & right) noexcept{ if (left.name != right.name) { return left.name < right.name; } return left.breed < right.breed;}
bool operator!=(const dog & left, const dog & right) noexcept{ return !(left == right);}bool operator<=(const dog & left, const dog & right) noexcept{ return !(right < left);}bool operator>(const dog & left, const dog & right) noexcept{ return right < left;}bool operator>=(const dog & left, const dog & right) noexcept{ return !(left < right);}
auto tie_members(const dog & x) noexcept{ return std::tie(x.name, x.breed);}// returns a std::tuple<const std::string &, const std::string &>
auto tie_members(const dog & x) noexcept{ return std::tie(x.name, x.breed);}// returns a std::tuple<const std::string &, const std::string &>
bool operator==(const dog & left, const dog & right) noexcept{ return tie_members(left) == tie_members(right);}bool operator<(const dog & left, const dog & right) noexcept{ return tie_members(left) < tie_members(right);}// etc.
template<class T, typename ... Args>auto tie_with_check(Args & ... args) noexcept{ static_assert(sizeof(T) == sizeof(std::tuple<Args...>), "You forgot a member variable"); return std::tie(args...);}
template<class T, typename ... Args>auto tie_with_check(Args & ... args) noexcept{ static_assert(sizeof(T) == sizeof(std::tuple<Args...>), "You forgot a member variable"); return std::tie(args...);}
auto tie_members(const dog & x) noexcept{ return tie_with_check<dog>(x.name, x.breed);}
template<class T, typename ... Args>auto tie_with_check(Args & ... args) noexcept{ static_assert(sizeof(T) == sizeof(std::tuple<Args...>), "You forgot a member variable"); return std::tie(args...);}
auto tie_members(const dog & x) noexcept{ return tie_with_check<dog>(x.name, x.breed);}
'tuple' is usually implemented using recursive derivation, not as side by side member declaration as a struct.
Therefore there is no guarantee that std::tuple has the same layout as the struct.
Padding might be different.
template<typename ... Args>struct struct_layout;template<typename T0>struct struct_layout<T0>{ T0 m0;};template<typename T0, typename T1>struct struct_layout<T0, T1>{ T0 m0; T1 m1;};// and so on up to the max number of members you support
template<typename ... Args>struct struct_layout;template<typename T0>struct struct_layout<T0>{ T0 m0;};template<typename T0, typename T1>struct struct_layout<T0, T1>{ T0 m0; T1 m1;};// and so on up to the max number of members you support
static_assert(sizeof(T) == sizeof(struct_layout<Args...>), "You forgot a member variable");
enum class foo_bar{ foo, bar};auto to_string(foo_bar x) noexcept{ switch (x) { case foo_bar::foo: return "foo"; case foo_bar::bar: return "bar"; default: std::terminate(); }}
currently they require macros
low hanging fruit from the code generation component of the problem
the steps above for equality or from_json:
A lot of languages support dynamic reflection:
For C++ we want static reflection metaprogramming
Yes:
Maybe:
Probably not:
Metadata:
Options:
Upstairs:
Downstairs:
Plumbing:
by Matus Chochlik, Axel Naumann, David Sankel
Type-based reflection with template metaprogramming
Advantages:
Disadvantages:
template<typename T>struct compare_data_members{ ...};template<typename T>bool generic_equal(const T & a, const T & b){ using metaT = $reflect(T); bool result = true; reflect::for_each<reflect::get_data_members_t<metaT>>( compare_data_members<T>{a, b, result}); return result;}
template<typename T>struct compare_data_members{ const T & a; const T & b; bool & result; template<reflect::Object MetaDataMember> void operator ()(MetaDataMember) const { auto mem_ptr = reflect::get_pointer_v<MetaDataMember>; result &= a.*mem_ptr == b.*mem_ptr; }};
by Andrew Sutton, Herb Sutter
Type-based reflection with heterogeneous containers
Code generation operators as 'typename', 'namespace', 'idexpr'
Requires more compiler intrinsics
Disadvantages:
void foo(int n) { int x; auto r1 = $int; // r1 has type meta::fundamental_type<X> auto r2 = $foo; // r2 has type meta::function<X> auto r3 = $n; // r3 has type meta::parameter<X> auto r4 = $x; // r4 has type meta::variable<X>}
Aka. unrolled loop, loop expansion, tuple-based for loop
for... (auto x : $s.member_variables()){ std::cout << x.name();}
Aka. unrolled loop, loop expansion, tuple-based for loop
for... (auto x : $s.member_variables()){ std::cout << x.name();}
Expands to
auto && tup = $s.member_variables();{ auto x = std::get<0>(tup); cout << x.name(); }{ auto x = std::get<1>(tup); cout << x.name(); }
constexpr int one_bigger(int x){ return ++x;}int some_array[one_bigger(6)];int main(int /*argc*/, char * argv[]){ std::cout << std::size(some_array) << '\n'; int argument = std::stoi(argv[1]); int result = one_bigger(argument); std::cout << result << '\n';}
constexpr{ // do some compile time programming here}
Can be placed in namespace, class, block scope.
constexpr{ // do some compile time programming here}
Can be placed in namespace, class, block scope.
Equivalent to:
constexpr void __unnamed_fn(){ // compile time code here}constexpr int __unnamed_var = (__unnamed_fn(), 0);
Can appear inside a constexpr block
constexpr{ -> namespace { int f() { return 0; } }} // injection site is here
Can appear inside a constexpr block
constexpr{ -> namespace { int f() { return 0; } }} // injection site is here
Equivalent to:
int f() { return 0; }
Can appear inside a constexpr block
constexpr{ -> namespace { int f() { return 0; } }} // injection site is here
Equivalent to:
int f() { return 0; }
Several options for the scope of the injection: namespace, class, block, expression.
template<Enum E>const char * to_string(E value){ switch(value) constexpr { for... (auto e : $E.enumerators()) { -> { case e.value(): return e.name(); } } }}
void foo() { ... }void foo_bar() { ... }void g(){ auto x = $foo; return declname(x "_bar")();}
constexpr void make_links(){ -> class C { C * next; C * prev; }}struct list{ constexpr { make_links(); }};
constexpr void make_links(){ -> class C { C * next; C * prev; }}struct list{ constexpr { make_links(); }};
Equivalent to:
struct list{ list * next; list * prev;};
$class interface{ constexpr { compiler.require($interface.variables().empty(), "interfaces may not contain data"); for (auto f : $interface.functions()) { compiler.require(!f.is_copy() && !f.is_move(), "interfaces may not copy or move; consider a " " virtual clone() instead"); if (!f.has_access()) f.make_public(); compiler.require(f.is_public(), "interface functions must be public"); f.make_pure_virtual(); } } virtual ~interface() noexcept { }};
interface Shape{ int area() const; void scale_by(double factor);};
struct legacy_point { int x; int y; }; // in C++17 this is not comparable...set<legacy_point> s; // and so this is an errorusing ordered_point = $legacy_point.as(ordered); // ... but this is orderedset<ordered_point> s; // so this is OK
by Herb Sutter
Advantages:
Maybe disadvantage:
$class ordered{ constexpr { if (! requires(ordered a) { a == a; }) -> { friend bool operator == (const ordered& a, const ordered& b) { constexpr { for (auto o : ordered.variables()) -> { if (!(a.o.name$ == b.(o.name)$)) return false; } } return true; } } //... }};
ordered dog{ std::string name; std::string breed;};
Andrew Sutton
CppCon 2017: Reflection
https://www.youtube.com/watch?v=N2G-Frv1z5Q
Andrew Sutton
CppCon 2017: Meta
https://www.youtube.com/watch?v=29IqPeKL_QY
Herb Sutter
CppCon 2017: Meta: Thoughts on generative C++
https://www.youtube.com/watch?v=4AfRAVcThyA
Herb Sutter
Thoughts on Metaclasses - Keynote [ACCU 2017]
https://www.youtube.com/watch?v=6nsyX37nsRs
Matúš Chochlík, Axel Naumann, David Sankel
Static Reflection in a Nutshell
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0578r1.html
Matúš Chochlík, Axel Naumann, David Sankel
Static reflection - Rationale, design and evolution
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0385r2.pdf
Matúš Chochlík, Axel Naumann, David Sankel
Static reflection
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0194r4.html
Matúš Chochlík, Axel Naumann, David Sankel
Static reflection of functions
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0670r1.html
Andrew Sutton, Herb Sutter
A design for static reflection
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0590r0.pdf
Andrew Sutton
Tuple-based for loops
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0589r0.pdf
Andrew Sutton, Herb Sutter
Implementing language support for compile-time metaprogramming
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0712r0.pdf
Herb Sutter
Metaclasses : Generative C++
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0707r2.pdf
Daveed Vandevoorde, Louis Dionne
Exploring the design space of metaprogramming and reflection
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0633r0.pdf
Daveed Vandevoorde
Reflect Through Values Instead of Types
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0598r0.html
Daveed Vandevoorde
Reflective Metaprogramming in C++ 2003
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1471.pdf
Mike Spertus
Use Cases for Compile-Time Reflection
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3492.pdf
Jeff Snyder, Chandler Carruth
Call for Compile-Time Reflection Proposals
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3814.html
Daveed Vandevoorde
std::constexpr_vector
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0597r0.html
Andrei Alexandrescu
Fastware - ACCU 2016
https://www.youtube.com/watch?v=AxnotgLql0k
Labrador picture
https://pixabay.com/en/animal-dog-puppy-pet-photography-2184791/
Thanks go to Nigel Lester for organizing the original ACCU talk and spell checking the presentation.
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |