147

Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

  • Upload
    others

  • View
    9

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches
Page 2: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Antony Polukhin

Полухин Антон

Better C++14 Reflections

Without Macro, Markup nor external Tooling

Page 3: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

[Boost.]PFRhttps://github.com/apolukhin/magic_get

Better C++14 Reflections

Page 4: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Some structure

struct complicated_struct {

int i;

short s;

double d;

unsigned u;

};

Better C++14 Reflections 4 / 147

Page 5: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Something that should not work...

#include <iostream>

#include <boost/pfr/precise.hpp>

struct complicated_struct { /*...*/ };

int main() {

using namespace boost::pfr::ops;

complicated_struct s {1, 2, 3.0, 4};

std::cout << "s == " << s << std::endl; // Compile time error?

}

Better C++14 Reflections 5 / 147

Page 6: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

But it works!..

antoshkka@home:~$ ./test

s == {1, 2, 3.0, 4}

Better C++14 Reflections 6 / 147

Page 7: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

What's in the header?

#include <iostream>

#include <boost/pfr/precise.hpp>

struct complicated_struct { /*...*/ };

int main() {

using namespace boost::pfr::ops;

complicated_struct s {1, 2, 3.0, 4};

std::cout << "s == " << s << std::endl; // Compile time error?

}

Better C++14 Reflections 7 / 147

Page 8: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

We need to go deeper

template <class Char, class Traits, class T>

detail::enable_not_ostreamable_t<std::basic_ostream<Char, Traits>, T>

operator<<(std::basic_ostream<Char, Traits>& out, const T& value)

{

boost::pfr::write(out, value);

return out;

}

Better C++14 Reflections 8 / 147

Page 9: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

We need to go deeper

template <class Char, class Traits, class T>

void write(std::basic_ostream<Char, Traits>& out, const T& val) {

out << '{';

detail::print_impl<0, boost::pfr::tuple_size_v<T> >::print(out, val);

out << '}';

}

Better C++14 Reflections 9 / 147

Page 10: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

That's suspicious....

template <class Char, class Traits, class T>

void write(std::basic_ostream<Char, Traits>& out, const T& val) {

out << '{';

detail::print_impl<0, boost::pfr::tuple_size_v<T> >::print(out, val);

out << '}';

}

Better C++14 Reflections 10 / 147

Page 11: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

O_O

template <std::size_t FieldIndex, std::size_t FieldsCount>

struct print_impl {

template <class Stream, class T>

static void print (Stream& out, const T& value) {

if (!!FieldIndex) out << ", ";

out << boost::pfr::get<FieldIndex>(value); // std::get<FieldIndex>(value)

print_impl<FieldIndex + 1, FieldsCount>::print(out, value);

}

};

Better C++14 Reflections 11 / 147

Page 12: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

O_O

template <std::size_t FieldIndex, std::size_t FieldsCount>

struct print_impl {

template <class Stream, class T>

static void print (Stream& out, const T& value) {

if (!!FieldIndex) out << ", ";

out << boost::pfr::get<FieldIndex>(value); // std::get<FieldIndex>(value)

print_impl<FieldIndex + 1, FieldsCount>::print(out, value);

}

};

Better C++14 Reflections 12 / 147

Page 13: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

O_O

template <std::size_t FieldIndex, std::size_t FieldsCount>

struct print_impl {

template <class Stream, class T>

static void print (Stream& out, const T& value) {

if (!!FieldIndex) out << ", ";

out << boost::pfr::get<FieldIndex>(value); // std::get<FieldIndex>(value)

print_impl<FieldIndex + 1, FieldsCount>::print(out, value);

}

};

Better C++14 Reflections 13 / 147

Page 14: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

O_O

template <std::size_t FieldIndex, std::size_t FieldsCount>

struct print_impl {

template <class Stream, class T>

static void print (Stream& out, const T& value) {

if (!!FieldIndex) out << ", ";

out << boost::pfr::get<FieldIndex>(value); // std::get<FieldIndex>(value)

print_impl<FieldIndex + 1, FieldsCount>::print(out, value);

}

};

Better C++14 Reflections 14 / 147

Page 15: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

What's going on here?

/// Returns reference or const reference to a field with index `I` in aggregate T

/// Requires: <skipped>

template <std::size_t I, class T>

decltype(auto) get(const T& val) noexcept;

/// `tuple_size_v` is a template variable that contains fields count in a T

/// Requires: C++14

template <class T>

constexpr std::size_t tuple_size_v = /*...*/;

Better C++14 Reflections 15 / 147

Page 16: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

How to count fields(the basics of PFR)

Better C++14 Reflections

Page 17: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Basic idea for counting fields

static_assert(std::is_pod<T>::value, "")

Better C++14 Reflections 17 / 147

Page 18: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Basic idea for counting fields

static_assert(std::is_pod<T>::value, "")

T { args... }

Better C++14 Reflections 18 / 147

Page 19: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Basic idea for counting fields

static_assert(std::is_pod<T>::value, "")

T { args... }

sizeof...(args) <= fields count

Better C++14 Reflections 19 / 147

Page 20: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Basic idea for counting fields

static_assert(std::is_pod<T>::value, "")

T { args... }

sizeof...(args) <= fields count typeid(args)... == typeid(fields)...

Better C++14 Reflections 20 / 147

Page 21: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Basic idea for counting fields

static_assert(std::is_pod<T>::value, "")

T { args... }

sizeof...(args) <= fields count typeid(args)... == typeid(fields)...

sizeof(char) == 1

sizeof...(args) <= sizeof(T)

Better C++14 Reflections 21 / 147

Page 22: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Basic idea for counting fields

static_assert(std::is_pod<T>::value, "")

T { args... }

sizeof...(args) <= fields count typeid(args)... == typeid(fields)...

sizeof(char) == 1

sizeof...(args) <= sizeof(T) * CHAR_BITS

Better C++14 Reflections 22 / 147

Page 23: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Basic idea for counting fields

static_assert(std::is_pod<T>::value, "")

T { args... }

sizeof...(args) <= fields count typeid(args)... == typeid(fields)...

sizeof(char) == 1 ???

sizeof...(args) <= sizeof(T) * CHAR_BITS

Better C++14 Reflections 23 / 147

Page 24: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Basic idea for counting fields

static_assert(std::is_pod<T>::value, "")

T { args... }

sizeof...(args) <= fields count typeid(args)... == typeid(fields)...

sizeof(char) == 1 ???

sizeof...(args) <= sizeof(T) * CHAR_BITS

Better C++14 Reflections 24 / 147

Page 25: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Ubiq

struct ubiq {

template <class Type>

constexpr operator Type&() const;

};

Better C++14 Reflections 25 / 147

Page 26: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Ubiq

struct ubiq {

template <class Type>

constexpr operator Type&() const;

};

int i = ubiq{};

double d = ubiq{};

char c = ubiq{};

Better C++14 Reflections 26 / 147

Page 27: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Done

static_assert(std::is_pod<T>::value, "")

T { args... }

sizeof...(args) <= fields count typeid(args)... == typeid(fields)...

sizeof(char) == 1 ubiq{}

sizeof...(args) <= sizeof(T) * CHAR_BITS

Better C++14 Reflections 27 / 147

Page 28: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Putting all together

struct ubiq_constructor {

std::size_t ignore;

template <class Type>

constexpr operator Type&() const noexcept; // Undefined

};

Better C++14 Reflections 28 / 147

Page 29: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Putting all together. Simplified version ]:->

// #1

template <class T, std::size_t I0, std::size_t... I>

constexpr auto detect_fields_count(std::size_t& out, std::index_sequence<I0, I...>)

-> decltype( T{ ubiq_constructor{I0}, ubiq_constructor{I}... } )

{ out = sizeof...(I) + 1; /*...*/ }

// #2

template <class T, std::size_t... I>

constexpr void detect_fields_count(std::size_t& out, std::index_sequence<I...>) {

detect_fields_count<T>(out, std::make_index_sequence<sizeof...(I) - 1>{});

}

Better C++14 Reflections 29 / 147

Page 30: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Putting all together. Simplified version ]:->

// #1

template <class T, std::size_t I0, std::size_t... I>

constexpr auto detect_fields_count(std::size_t& out, std::index_sequence<I0, I...>)

-> decltype( T{ ubiq_constructor{I0}, ubiq_constructor{I}... } )

{ out = sizeof...(I) + 1; /*...*/ }

// #2

template <class T, std::size_t... I>

constexpr void detect_fields_count(std::size_t& out, std::index_sequence<I...>) {

detect_fields_count<T>(out, std::make_index_sequence<sizeof...(I) - 1>{});

}

Better C++14 Reflections 30 / 147

Page 31: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Putting all together. Simplified version ]:->

// #1

template <class T, std::size_t I0, std::size_t... I>

constexpr auto detect_fields_count(std::size_t& out, std::index_sequence<I0, I...>)

-> decltype( T{ ubiq_constructor{I0}, ubiq_constructor{I}... } )

{ out = sizeof...(I) + 1; /*...*/ }

// #2

template <class T, std::size_t... I>

constexpr void detect_fields_count(std::size_t& out, std::index_sequence<I...>) {

detect_fields_count<T>(out, std::make_index_sequence<sizeof...(I) - 1>{});

}

Better C++14 Reflections 31 / 147

Page 32: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Putting all together. Simplified version ]:->

// #1

template <class T, std::size_t I0, std::size_t... I>

constexpr auto detect_fields_count(std::size_t& out, std::index_sequence<I0, I...>)

-> decltype( T{ ubiq_constructor{I0}, ubiq_constructor{I}... } )

{ out = sizeof...(I) + 1; /*...*/ }

// #2

template <class T, std::size_t... I>

constexpr void detect_fields_count(std::size_t& out, std::index_sequence<I...>) {

detect_fields_count<T>(out, std::make_index_sequence<sizeof...(I) - 1>{});

}

Better C++14 Reflections 32 / 147

Page 33: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Putting all together. Simplified version ]:->

// #1

template <class T, std::size_t I0, std::size_t... I>

constexpr auto detect_fields_count(std::size_t& out, std::index_sequence<I0, I...>)

-> decltype( T{ ubiq_constructor{I0}, ubiq_constructor{I}... } )

{ out = sizeof...(I) + 1; /*...*/ }

// #2

template <class T, std::size_t... I>

constexpr void detect_fields_count(std::size_t& out, std::index_sequence<I...>) {

detect_fields_count<T>(out, std::make_index_sequence<sizeof...(I) - 1>{});

}

Better C++14 Reflections 33 / 147

Page 34: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Putting all together. Simplified version ]:->

// #1

template <class T, std::size_t I0, std::size_t... I>

constexpr auto detect_fields_count(std::size_t& out, std::index_sequence<I0, I...>)

-> decltype( T{ ubiq_constructor{I0}, ubiq_constructor{I}... } )

{ out = sizeof...(I) + 1; /*...*/ }

// #2

template <class T, std::size_t... I>

constexpr void detect_fields_count(std::size_t& out, std::index_sequence<I...>) {

detect_fields_count<T>(out, std::make_index_sequence<sizeof...(I) - 1>{});

}

Better C++14 Reflections 34 / 147

Page 35: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

It works!..but...

Better C++14 Reflections

Page 36: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Bad news!

It compiles for eternity or reaches the compiler instantiation depth limit

Better C++14 Reflections

Page 37: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields

Better C++14 Reflections 37 / 147

Page 38: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields

T{}

Better C++14 Reflections 38 / 147

Page 39: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields

T{}

T{ubiq{}}

Better C++14 Reflections 39 / 147

Page 40: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields

T{}

T{ubiq{}}

T{ubiq{}, ubiq{}}

Better C++14 Reflections 40 / 147

Page 41: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields

T{}

T{ubiq{}}

T{ubiq{}, ubiq{}}

T{ubiq{}, ubiq{}, ubiq{}}

Better C++14 Reflections 41 / 147

Page 42: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields

T{}

T{ubiq{}}

T{ubiq{}, ubiq{}}

T{ubiq{}, ubiq{}, ubiq{}}

T{ubiq{}, ….., ubiq{}, …..., ubiq{}}

Better C++14 Reflections 42 / 147

Page 43: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields

T{}

T{ubiq{}}

T{ubiq{}, ubiq{}}

T{ubiq{}, ubiq{}, ubiq{}}

T{ubiq{}, ….., ubiq{}, …..., ubiq{}}

Better C++14 Reflections 43 / 147

+ + + + + + + + + + + + + + - - - - - -

Page 44: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling:

Better C++14 Reflections 44 / 147

+ + + + + + + + + + + + + + - - - - - -

Page 45: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling:

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 45 / 147

+ + + + + + + + + + + + + + - - - - - -

Page 46: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 46 / 147

Page 47: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 47 / 147

Page 48: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 48 / 147

?

Page 49: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 49 / 147

+

Page 50: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 50 / 147

+

Page 51: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 51 / 147

+ ?

Page 52: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 52 / 147

+ -

Page 53: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 53 / 147

+ -

Page 54: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 54 / 147

+ - -

Page 55: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 55 / 147

+ - -

Page 56: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 56 / 147

+ - - -

Page 57: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it! Aggregates without deleted constructors and with default constructible fields have a single point after which increasing ubiq counts stop compiling.

Oh! Binary search fits perfectly:– log(N) instantiations depth– log(N) instantiations

Better C++14 Reflections 57 / 147

+ - - -

Page 58: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

What about other aggregates?

Better C++14 Reflections

Page 59: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it (2)! Aggregates with deleted constructor or with some non default constructible fields have gaps:

Better C++14 Reflections 59 / 147

- - - + + + + + + + + + + + - - - - - -

Page 60: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it (2)! Aggregates with deleted constructor or with some non default constructible fields have gaps:

We need an eager version of search that does:– log(N) instantiations depth– N instantiations

Better C++14 Reflections 60 / 147

- - - + + + + + + + + + + + - - - - - -

Page 61: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it (2)! Aggregates with deleted constructor or with some non default constructible fields have gaps:

We need a bad version Binary search:– log(N) instantiations depth– N instantiations

Better C++14 Reflections 61 / 147

- - - + + + + + + + + + + + - - - - - -

Page 62: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it (2)! Aggregates with deleted constructor or with some non default constructible fields have gaps:

We need a bad version Binary search:– log(N) instantiations depth– N instantiations

Better C++14 Reflections 62 / 147

Page 63: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it (2)! Aggregates with deleted constructor or with some non default constructible fields have gaps:

We need a bad version Binary search:– log(N) instantiations depth– N instantiations

Better C++14 Reflections 63 / 147

Page 64: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it (2)! Aggregates with deleted constructor or with some non default constructible fields have gaps:

We need a bad version Binary search:– log(N) instantiations depth– N instantiations

Better C++14 Reflections 64 / 147

Page 65: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it (2)! Aggregates with deleted constructor or with some non default constructible fields have gaps:

We need a bad version Binary search:– log(N) instantiations depth– N instantiations

Better C++14 Reflections 65 / 147

Page 66: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it (2)! Aggregates with deleted constructor or with some non default constructible fields have gaps:

We need a bad version Binary search:– log(N) instantiations depth– N instantiations

Better C++14 Reflections 66 / 147

Page 67: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it (2)! Aggregates with deleted constructor or with some non default constructible fields have gaps:

We need a bad version Binary search:– log(N) instantiations depth– N instantiations

Better C++14 Reflections 67 / 147

1

Page 68: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it (2)! Aggregates with deleted constructor or with some non default constructible fields have gaps:

We need a bad version Binary search:– log(N) instantiations depth– N instantiations

Better C++14 Reflections 68 / 147

- - + + + + + + + - - - - - - - - - - -

Page 69: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Let's fix it (2)! Aggregates with deleted constructor or with some non default constructible fields have gaps:

We need a bad version Binary search:– log(N) instantiations depth– N instantiations

Better C++14 Reflections 69 / 147

- - + + + + + + + - - - - - - - - - - -

Page 70: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

How to get the field type?

Better C++14 Reflections

Page 71: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type

T{ ubiq_constructor<I>{}... }

Better C++14 Reflections 71 / 147

Page 72: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type

T{ ubiq_constructor<I>{}... }

ubiq_constructor<I>{}::operator Type&() const

Better C++14 Reflections 72 / 147

Page 73: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type

T{ ubiq_constructor<I>{}... }

ubiq_constructor<I>{}::operator Type&() const

Type

Better C++14 Reflections 73 / 147

Page 74: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type

T{ ubiq_constructor<I>{}... }

ubiq_constructor<I>{}::operator Type&() const

Type

ubiq_constructor<I>{ TypeOut& }

Better C++14 Reflections 74 / 147

Page 75: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type

T{ ubiq_constructor<I>{}... }

ubiq_constructor<I>{}::operator Type&() const

Type

ubiq_constructor<I>{ TypeOut& }

Better C++14 Reflections 75 / 147

Page 76: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Naïve solution

POD = { (public|private|protected) + (fundamental | POD)* };

Better C++14 Reflections 76 / 147

Page 77: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Naïve solution

fundamental (not a pointer) int→

int output→

output[I]... Types...→

Better C++14 Reflections 77 / 147

Page 78: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Naïve solution

template <std::size_t I>

struct ubiq_val {

std::size_t* ref_;

template <class Type>

constexpr operator Type() const noexcept {

ref_[I] = typeid_conversions::type_to_id(identity<Type>{});

return Type{};

}

};

Better C++14 Reflections 78 / 147

Page 79: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Naïve solution

#define BOOST_MAGIC_GET_REGISTER_TYPE(Type, Index) \

constexpr std::size_t type_to_id(identity<Type>) noexcept { \

return Index; \

} \

constexpr Type id_to_type( size_t_<Index > ) noexcept { \

Type res{}; \

return res; \

} \

/**/

Better C++14 Reflections 79 / 147

Page 80: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Naïve solution

BOOST_MAGIC_GET_REGISTER_TYPE(unsigned char , 1)

BOOST_MAGIC_GET_REGISTER_TYPE(unsigned short , 2)

BOOST_MAGIC_GET_REGISTER_TYPE(unsigned int , 3)

BOOST_MAGIC_GET_REGISTER_TYPE(unsigned long , 4)

BOOST_MAGIC_GET_REGISTER_TYPE(unsigned long long , 5)

BOOST_MAGIC_GET_REGISTER_TYPE(signed char , 6)

BOOST_MAGIC_GET_REGISTER_TYPE(short , 7)

BOOST_MAGIC_GET_REGISTER_TYPE(int , 8)

BOOST_MAGIC_GET_REGISTER_TYPE(long , 9)

BOOST_MAGIC_GET_REGISTER_TYPE(long long , 10)

Better C++14 Reflections 80 / 147

Page 81: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Naïve solution

template <class T, std::size_t N, std::size_t... I>

constexpr auto type_to_array_of_type_ids(std::size_t* types) noexcept

-> decltype(T{ ubiq_constructor<I>{}... })

{

T tmp{ ubiq_val< I >{types}... };

return tmp;

}

Better C++14 Reflections 81 / 147

Page 82: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Naïve solution

template <class T, std::size_t... I>

constexpr auto as_tuple_impl(std::index_sequence<I...>) noexcept {

constexpr auto a = array_of_type_ids<T>(); // #0

return std::tuple< // #3

decltype(typeid_conversions::id_to_type( // #2

size_t_<a[I]>{} // #1

))...

>{};

}

Better C++14 Reflections 82 / 147

Page 83: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

T to tuple

Better C++14 Reflections

Page 84: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

T to tuple

template <size_t I, class T>

auto get(T& v) noexcept {

using tuple = decltype(as_tuple_impl<T>(...));

return ???;

}

Better C++14 Reflections 84 / 147

Page 85: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

T to tuple

template <size_t I, class T>

auto get(T& v) noexcept {

using tuple = decltype(as_tuple_impl<T>(...));

return ???;

}

Better C++14 Reflections 85 / 147

Page 86: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

T to tuple

template <size_t I, class T>

auto get(T& v) noexcept {

using tuple = decltype(as_tuple_impl<T>(...));

return std::get<I>(

reinterpret_cast<tuple&>(v) // UB

);

}

Better C++14 Reflections 86 / 147

Page 87: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

T to tuple

template <size_t I, class T>

auto get(T& v) noexcept {

using tuple = decltype(as_tuple_impl<T>(...));

return std::get<I>(

reinterpret_cast<tuple&>(v) // UB :-(

);

}

Better C++14 Reflections 87 / 147

Page 88: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

T to tuple

template <size_t I, class T>

auto get(T& v) noexcept {

using tuple = decltype(as_tuple_impl<T>(...));

return *reinterpret_cast<tuple_element_t<I, tuple>* >(

reinterpret_cast<unsigned char*>(&v) + offset_for<tuple, I>()

);

}

Better C++14 Reflections 88 / 147

Page 89: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

T to tuple

template <size_t I, class T>

auto get(T& v) noexcept {

using tuple = decltype(as_tuple_impl<T>(...));

return *reinterpret_cast<tuple_element_t<I, tuple>* >(

reinterpret_cast<unsigned char*>(&v) + offset_for<tuple, I>()

);

}

Better C++14 Reflections 89 / 147

Page 90: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

T to tuple

template <size_t I, class T>

auto get(T& v) noexcept {

using tuple = decltype(as_tuple_impl<T>(...));

return *reinterpret_cast<tuple_element_t<I, tuple>* >(

reinterpret_cast<unsigned char*>(&v) + offset_for<tuple, I>()

);

}

Better C++14 Reflections 90 / 147

Page 91: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

T to tuple

template <size_t I, class T>

auto get(T& v) noexcept {

using tuple = decltype(as_tuple_impl<T>(...));

return *reinterpret_cast<tuple_element_t<I, tuple>* >(

reinterpret_cast<unsigned char*>(&v) + offset_for<tuple, I>()

);

}

Better C++14 Reflections 91 / 147

Page 92: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls of naive reflection

Better C++14 Reflections

Page 93: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Enums are represented as an underlying type

Better C++14 Reflections 93 / 147

Page 94: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Enums are represented as an underlying type● Does not work with nested structures

Better C++14 Reflections 94 / 147

Page 95: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Enums are represented as an underlying type● Does not work with nested structures

– Or does the reflection recursively

Better C++14 Reflections 95 / 147

Page 96: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Enums are represented as an underlying type● Does not work with nested structures

– Or does the reflection recursively (flat reflection)

Better C++14 Reflections 96 / 147

Page 97: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Enums are represented as an underlying type● Does not work with nested structures

– Or does the reflection recursively (flat reflection)

struct struct0 {

int i;

short s;

};

struct struct1 {

struct0 st;

double d;

unsigned u;

};

Better C++14 Reflections 97 / 147

Page 98: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Enums are represented as an underlying type● Does not work with nested structures

– Or does the reflection recursively (flat reflection)

struct struct_flat {

int i;

short s;

double d;

unsigned u;

};

Better C++14 Reflections 98 / 147

Page 99: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Enums are represented as an underlying type● Does not work with nested structures

– Or does the reflection recursively (flat reflection)● A lot of computations to encode cv pointers as an

integer

Better C++14 Reflections 99 / 147

Page 100: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Enums are represented as an underlying type● Does not work with nested structures

– Or does the reflection recursively (flat reflection)● A lot of computations to encode cv pointers as an

integer● Works only with PODs

Better C++14 Reflections 100 / 147

Page 101: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

That’s the best we can do!

Better C++14 Reflections

Page 102: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

That’s the best we can do!...was I thinking

Better C++14 Reflections

Page 103: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

That’s NOT the best we can do!

Better C++14 Reflections

Page 104: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Better field type detectionVersion #1; creepy

Better C++14 Reflections

Page 105: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type

T{ ubiq_constructor<I>{}... }

ubiq_constructor<I>{}::operator Type&() const

Type

Better C++14 Reflections 105 / 147

Page 106: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type

T{ ubiq_constructor<I>{}... }

ubiq_constructor<I>{}::operator Type&() const

Type

Better C++14 Reflections 106 / 147

Page 107: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type

T{ ubiq_constructor<I>{}... }

ubiq_constructor<I>{}::operator Type&() const

Type

call T{ ubiq_constructor<I>{}... } again from within the operator Type&()

Better C++14 Reflections 107 / 147

Page 108: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type recursively

for_each_field_in_depth<T, I, Types...>(t, callback, ... );

Better C++14 Reflections 108 / 147

Page 109: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type recursively

for_each_field_in_depth<T, I, Types...>(t, callback, ... );

T{ ubiq_constructor_next<T, I>{}... }

Better C++14 Reflections 109 / 147

Page 110: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type recursively

for_each_field_in_depth<T, I, Types...>(t, callback, ... );

T{ ubiq_constructor_next<T, I>{}... }

ubiq_constructor_next<I>{}::operator Type&() const

Better C++14 Reflections 110 / 147

Page 111: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type recursively

for_each_field_in_depth<T, I, Types...>(t, callback, ... );

T{ ubiq_constructor_next<T, I>{}... }

ubiq_constructor_next<I>{}::operator Type&() const

{ for_each_field_in_depth<T, I+1, Types..., Type>(t, callback, ... ); }

Better C++14 Reflections 111 / 147

Page 112: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type recursively

for_each_field_in_depth<T, I, Types...>(t, callback, ... );

T{ ubiq_constructor_next<T, I>{}... }

ubiq_constructor_next<I>{}::operator Type&() const

{ for_each_field_in_depth<T, I+1, Types..., Type>(t, callback, ... ); }

Better C++14 Reflections 112 / 147

Page 113: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type recursively

for_each_field_in_depth<T, I, Types...>(t, callback, ... );

T{ ubiq_constructor_next<T, I>{}... }

ubiq_constructor_next<I>{}::operator Type&() const

{ for_each_field_in_depth<T, I+1, Types..., Type>(t, callback, ... ); }

...

Better C++14 Reflections 113 / 147

Page 114: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Getting the field type recursively

for_each_field_in_depth<T, I, Types...>(t, callback, ... );

T{ ubiq_constructor_next<T, I>{}... }

ubiq_constructor_next<I>{}::operator Type&() const

{ for_each_field_in_depth<T, I+1, Types..., Type>(t, callback, ... ); }

...

callback<Types...>(t, make_tuple_of_references<Types...>(t))

Better C++14 Reflections 114 / 147

Page 115: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Big compile time overhead

Better C++14 Reflections 115 / 147

Page 116: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Big compile time overhead● We rely on compiler cleverness for eliminating

unnecessary copies

Better C++14 Reflections 116 / 147

Page 117: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Big compile time overhead● We rely on compiler cleverness for eliminating

necessary copies– But no too much, so we assert that the compiler is

clever enough:

constexpr T tmp{ ubiq{I}... };

Better C++14 Reflections 117 / 147

Page 118: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Pitfalls ● Big compile time overhead● We rely on compiler cleverness for eliminating

unnecessary copies– But no too much, so we assert that the compiler is

clever enough:

constexpr T tmp{ ubiq{I}... };● Not always works

Better C++14 Reflections 118 / 147

Page 119: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Even better field type detectionVersion #2; very very creepy

Better C++14 Reflections

Page 120: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

The essence

Better C++14 Reflections 120 / 147

Page 121: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

The essence ● To register id type relations we need a way to save a ↔global state in metafunctions

Better C++14 Reflections 121 / 147

Page 122: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

The essence ● To register id type relations we need a way to save a ↔global state in metafunctions

● Statefull metaprogramming is not permitted by the standard

Better C++14 Reflections 122 / 147

Page 123: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

The essence ● To register id type relations we need a way to save a ↔global state in metafunctions

● Statefull metaprogramming is not permitted by the standard– CWG is very serious about that

Better C++14 Reflections 123 / 147

Page 124: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

The essence ● To register id type relations we need a way to save a ↔global state in metafunctions

● Statefull metaprogramming is not permitted by the standard– CWG is very serious about that– There's even a CWG 2118 issue to deal with some

border cases of impure metafunctions

Better C++14 Reflections 124 / 147

Page 125: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

The essence ● To register id type relations we need a way to save a ↔global state in metafunctions

● Statefull metaprogramming is not permitted by the standard– CWG is very serious about that– There's even a CWG 2118 issue to deal with some

border cases of impure metafunctions

Huh!

Better C++14 Reflections 125 / 147

Page 126: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Type Loophole

template <class T, std::size_t N>

struct tag {

// forward declaration of loophole(tag<T,N>) without a result type

friend auto loophole(tag<T,N>);

};

Better C++14 Reflections 126 / 147

Page 127: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Type Loophole

template <class T, std::size_t N>

struct tag {

// forward declaration of loophole(tag<T,N>) without a result type

friend auto loophole(tag<T,N>);

};

template <class T, class FieldType, std::size_t N, bool B>

struct fn_def {

// definition of loophole(tag<T,N>) with a result type

friend auto loophole(tag<T,N>) { return FieldType{}; }

};

Better C++14 Reflections 127 / 147

Page 128: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Type Loophole

template <class T, std::size_t N>

struct loophole_ubiq {

template<class U, std::size_t M> static std::size_t ins(...);

template<class U, std::size_t M, std::size_t = sizeof(loophole(tag<T,M>{})) >

static char ins(int);

template<class U, std::size_t = sizeof(fn_def<

T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)

>)>

constexpr operator U&() const noexcept;

};

Better C++14 Reflections 128 / 147

Page 129: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Type Loophole

template <class T, std::size_t N>

struct loophole_ubiq {

template<class U, std::size_t M> static std::size_t ins(...);

template<class U, std::size_t M, std::size_t = sizeof(loophole(tag<T,M>{})) >

static char ins(int);

template<class U, std::size_t = sizeof(fn_def<

T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)

>)>

constexpr operator U&() const noexcept;

};

Better C++14 Reflections 129 / 147

Page 130: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Type Loophole

template <class T, std::size_t N>

struct loophole_ubiq {

template<class U, std::size_t M> static std::size_t ins(...);

template<class U, std::size_t M, std::size_t = sizeof(loophole(tag<T,M>{})) >

static char ins(int);

template<class U, std::size_t = sizeof(fn_def<

T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)

>)>

constexpr operator U&() const noexcept;

};

Better C++14 Reflections 130 / 147

Page 131: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Type Loophole

template <class T, std::size_t N>

struct loophole_ubiq {

template<class U, std::size_t M> static std::size_t ins(...);

template<class U, std::size_t M, std::size_t = sizeof(loophole(tag<T,M>{})) >

static char ins(int);

template<class U, std::size_t = sizeof(fn_def<

T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)

>)>

constexpr operator U&() const noexcept;

};

Better C++14 Reflections 131 / 147

Page 132: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Type Loophole

template <class T, std::size_t N>

struct loophole_ubiq {

template<class U, std::size_t M> static std::size_t ins(...);

template<class U, std::size_t M, std::size_t = sizeof(loophole(tag<T,M>{})) >

static char ins(int);

template<class U, std::size_t = sizeof(fn_def<

T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)

>)>

constexpr operator U&() const noexcept;

};

Better C++14 Reflections 132 / 147

Page 133: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Type Loophole

int main() {

struct test { char c; int i; };

test t{loophole_ubiq<test, 0>{} };

static_assert(

std::is_same<

char,

decltype( loophole(tag<test, 0>{}) )

>::value, ""

);

}

Better C++14 Reflections 133 / 147

Page 134: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Type Loophole

int main() {

struct test { char c; int i; };

test t{loophole_ubiq<test, 0>{} };

static_assert(

std::is_same<

char,

decltype( loophole(tag<test, 0>{}) )

>::value, ""

);

}

Better C++14 Reflections 134 / 147

Page 135: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Perfect field type detectionVersion #3; C++17 required

Better C++14 Reflections

Page 136: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

C++17

template <class T>

constexpr auto as_tuple_impl(T&& val) noexcept {

constexpr auto count = fields_count<T>();

if constexpr (count == 1) {

auto& [a] = std::forward<T>(val);

return detail::make_tuple_of_references(a);

} else if constexpr (count == 2) {

auto& [a,b] = std::forward<T>(val);

return detail::make_tuple_of_references(a,b);

} // ...

}

Better C++14 Reflections 136 / 147

Page 137: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

C++17

template <class T>

constexpr auto as_tuple_impl(T&& val) noexcept {

constexpr auto count = fields_count<T>();

if constexpr (count == 1) {

auto& [a] = std::forward<T>(val);

return detail::make_tuple_of_references(a);

} else if constexpr (count == 2) {

auto& [a,b] = std::forward<T>(val);

return detail::make_tuple_of_references(a,b);

} // ...

}

Better C++14 Reflections 137 / 147

Page 138: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

C++17

template <class T>

constexpr auto as_tuple_impl(T&& val) noexcept {

constexpr auto count = fields_count<T>();

if constexpr (count == 1) {

auto& [a] = std::forward<T>(val);

return detail::make_tuple_of_references(a);

} else if constexpr (count == 2) {

auto& [a,b] = std::forward<T>(val);

return detail::make_tuple_of_references(a,b);

} // ...

}

Better C++14 Reflections 138 / 147

Page 139: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

C++17

template <class T>

constexpr auto as_tuple_impl(T&& val) noexcept {

constexpr auto count = fields_count<T>();

if constexpr (count == 1) {

auto& [a] = std::forward<T>(val);

return detail::make_tuple_of_references(a);

} else if constexpr (count == 2) {

auto& [a,b] = std::forward<T>(val);

return detail::make_tuple_of_references(a,b);

} // ...

}

Better C++14 Reflections 139 / 147

Page 140: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Is it useful?or what features are available in [Boost.]PFR

Better C++14 Reflections

Page 141: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Features ● Implemented (flat and precise):– Comparisons : <, <=, >, >=, !=, ==– Heterogeneous comparators: less<>, flat_equal<>– IO stream operators: operator <<, operator>>– Hashing: flat_hash, hash– Tie to/from structure

Better C++14 Reflections 141 / 147

Page 142: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Features ● Implemented (flat and precise):– Comparisons : <, <=, >, >=, !=, ==– Heterogeneous comparators: less<>, flat_equal<>– IO stream operators: operator <<, operator>>– Hashing: flat_hash, hash– Tie to/from structure

● Do it on your own:– User defined serializers– Basic reflections– New type_traits: is_continuous_layout<T>,

is_padded<T>, has_unique_object_representation<T>– New features for containers: punch_hole<T, Index>– More generic algorithms: vector_mult, parse to struct

Better C++14 Reflections 142 / 147

Page 143: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Acknowledgementspeople who helped and invented stuff

Better C++14 Reflections

Page 144: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Acks ● Alexandr Poltavsky — for the Loophole● Bruno Dutra — for the recursive reflection● Chris Beck — for removing UBs from reinterpret_casts● Abutcher-gh — for adding some tie functions

● Lisa Lippincott and Alexey Moiseytsev — for finding issues with alignments

● Nikita Kniazev, Anton Bikineev and others – for testing reporting and finding issues.

Better C++14 Reflections 144 / 147

Page 145: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Спасибо!

Page 146: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Спасибо!Thanks for listening!

Page 147: Better C++14 Reflections - GitHub Pages Reflections.pdf · Better C++14 Reflections 34 / 147. It works!.. but... Better C++14 Reflections. Bad news! It compiles for eternity or reaches

Antony PolukhinDeveloper in Yandex.Taxi

[email protected]

[email protected]

https://github.com/apolukhin

https://stdcpp.ru/