+ All Categories
Home > Software > Александр Фокин, Рефлексия в C++

Александр Фокин, Рефлексия в C++

Date post: 16-Apr-2017
Category:
Upload: sergey-platonov
View: 1,063 times
Download: 6 times
Share this document with a friend
60
Transcript
Page 1: Александр Фокин, Рефлексия в C++
Page 2: Александр Фокин, Рефлексия в C++

Reflection в С++

Александр Фокин

Логотип партнераЛоготип Яндекса (Сервиса)

Page 3: Александр Фокин, Рефлексия в C++

3

План

1. Что такое Reflection?

2. Reflection наших дней.

3. Единый Reflection API дней грядущих.

4. Рефлексируем над услышанным.

Page 4: Александр Фокин, Рефлексия в C++

Что такое Reflection?

4

Page 5: Александр Фокин, Рефлексия в C++

5

Reflection в C++

• Получение свойств типов. Перечисление членов классов и их свойств. НЕ <type_traits>, НЕ has_xxx тесты через SFINAE.

Хранение и обработка свойств типов стандартными средствами языка.• Получение reflection данных в runtime в виде

классов.• Получение reflection данных в виде compile-time

структур.

Page 6: Александр Фокин, Рефлексия в C++

6

Reflection в C++

• Получение свойств типов. Перечисление членов классов и их свойств. НЕ <type_traits>, НЕ has_xxx тесты через SFINAE.

• Хранение и обработка свойств типов стандартными средствами языка. Получение reflection данных в runtime в виде

классов. Получение reflection данных в виде compile-time

структур.

Page 7: Александр Фокин, Рефлексия в C++

7

Runtime Reflection

void dump(QObject *o, QTextStream& s) { QMetaObject *m = o->metaObject();

for(int i = 0; i < m->propertyCount(); i++) { QMetaProperty p = m->property(i);

s << p.name() << “=” << p.read(o).toString() << endl; }}

Page 8: Александр Фокин, Рефлексия в C++

8

Compile-time Reflectiontemplate<int N>using int_ = std::integral_constant<int, N>;

template<class T>void dump(const T& o, std::ostream& s) { using fields = fieldsof(T);

do_dump(o, s, int_<fields::size - 1>());}

template<class T, int N>void do_dump(const T& o, std::ostream& s, int_<N>) { using field = typename fieldsof(T)::template at<N>;

s << field::name() << “=” << field::getter()(o) << std::endl; do_dump(o, s, int_<N - 1>());}

template<class T>void do_dump(const T&, std::ostream&, int_<-1>) {}

Page 9: Александр Фокин, Рефлексия в C++

9

Зачем программисту Reflection?

• Сериализация в XML / JSON / BSON / UBJSON / CSV / все что душе угодно.

• ORM для вашей любимой БД.

• Автоматическая генерация кода (operator==, и т.п.).

• Доступ к данным из скриптов / конфигов.

• «Data-driven programming» в широком смысле.

Page 10: Александр Фокин, Рефлексия в C++

10

Зачем программисту Reflection?

• Сериализация в XML / JSON / BSON / UBJSON / CSV / все что душе угодно.

• ORM для вашей любимой БД.

• Автоматическая генерация кода (operator==, и т.п.).

• Доступ к данным из скриптов / конфигов.

• «Data-driven programming» в широком смысле.

Page 11: Александр Фокин, Рефлексия в C++

11

Зачем программисту Reflection?

• Сериализация в XML / JSON / BSON / UBJSON / CSV / все что душе угодно.

• ORM для вашей любимой БД.

• Автоматическая генерация кода (operator== и т.п.).

• Доступ к данным из скриптов / конфигов.

• «Data-driven programming» в широком смысле.

Page 12: Александр Фокин, Рефлексия в C++

12

Зачем программисту Reflection?

• Сериализация в XML / JSON / BSON / UBJSON / CSV / все что душе угодно.

• ORM для вашей любимой БД.

• Автоматическая генерация кода (operator== и т.п.).

• Доступ к данным из скриптов / конфигов.

• .

Page 13: Александр Фокин, Рефлексия в C++

13

Зачем программисту Reflection?• Сериализация в XML / JSON / BSON / UBJSON / CSV / все

что душе угодно.

• ORM для вашей любимой БД.

• Автоматическая генерация кода (operator== и т.п.).

• Доступ к данным из скриптов / конфигов.

• Преобразования типов (вектор структур в структуру из векторов и т.п.).

• …

Page 14: Александр Фокин, Рефлексия в C++

Reflection наших дней

14

Page 15: Александр Фокин, Рефлексия в C++

15

Подходы

• Куча runtime велосипедов библиотек. CPGF, XCppRefl, Ponder…

• Workaround’ы на уровне фреймворков. Qt Moc.

• Куча domain-specific библиотек. Cereal, ODB, QxOrm, …

• Кирпичики для велосипедостроения Boost.Fusion / Boost.Hana / Boost.Reflect / Mirror.

• Полный DIY, или «сделай сам». Наиболее часто встречающийся в жизни подход.

Page 16: Александр Фокин, Рефлексия в C++

16

Подходы

• Куча runtime велосипедов библиотек. CPGF, XCppRefl, Ponder…

• Workaround’ы на уровне фреймворков. Qt Moc.

• Куча domain-specific библиотек. Cereal, ODB, QxOrm, …

• Кирпичики для велосипедостроения Boost.Fusion / Boost.Hana / Boost.Reflect / Mirror.

• Полный DIY, или «сделай сам». Наиболее часто встречающийся в жизни подход.

Page 17: Александр Фокин, Рефлексия в C++

17

Подходы

• Куча runtime велосипедов библиотек. CPGF, XCppRefl, Ponder…

• Workaround’ы на уровне фреймворков. Qt Moc.

• Куча domain-specific библиотек. Cereal, ODB, QxOrm, …

• Кирпичики для велосипедостроения Boost.Fusion / Boost.Hana / Boost.Reflect / Mirror.

• Полный DIY, или «сделай сам». Наиболее часто встречающийся в жизни подход.

Page 18: Александр Фокин, Рефлексия в C++

18

Подходы

• Куча runtime велосипедов библиотек. CPGF, XCppRefl, Ponder…

• Workaround’ы на уровне фреймворков. Qt Moc.

• Куча domain-specific библиотек. Cereal, ODB, QxOrm, …

• Кирпичики для велосипедостроения Boost.Fusion / Boost.Hana / Boost.Reflect / Mirror.

• Полный DIY, или «сделай сам». Наиболее часто встречающийся в жизни подход.

Page 19: Александр Фокин, Рефлексия в C++

19

Подходы

• Куча runtime велосипедов библиотек. CPGF, XCppRefl, Ponder…

• Workaround’ы на уровне фреймворков. Qt Moc.

• Куча domain-specific библиотек. Cereal, ODB, QxOrm, …

• Кирпичики для велосипедостроения Boost.Fusion / Boost.Hana / Boost.Reflect / Mirror.

• Полный DIY, или «сделай сам». Наиболее часто встречающийся в жизни подход.

Page 20: Александр Фокин, Рефлексия в C++

20

Мотивирующий пример• Один макрос генерирует 14 функций:

функции (де)сериализации в xml, json, ubjson, cvs; ORM-обвязки для sqlite; функции сравнения и вычисления хеша; функцию отладочной печати.

• Минус 100+ строк копипасты и отсутствие боли при необходимости что-то изменить в классе.

struct ApiLicenseData { Latin1Array key; Latin1Array licenseBlock;};#define ApiLicenseData_Fields (key)(licenseBlock)

ADAPT_STRUCT_FUNCTIONS( ApiLicenseData, (xml)(json)(ubjson)(csv_record)(sql_record)(eq)(less)(hash)(debug));

Page 21: Александр Фокин, Рефлексия в C++

21

Реализация на Boost.Fusion

struct ApiLicenseData { Latin1Array key; Latin1Array licenseBlock;};

BOOST_FUSION_ADAPT_STRUCT( ApiLicenseData, (Latin1Array, key) (Latin1Array, licenseBlock));

Page 22: Александр Фокин, Рефлексия в C++

22

Реализация на Boost.Fusion

template<class T>void serialize(const T &value, std::ostream &s) { using range = boost::mpl::range_c< size_t, 0, boost::fusion::result_of::size<T>::value>;

boost::fusion::for_each(range(), [&](auto arg) { constexpr size_t index = decltype(arg)::value;

auto name = boost::fusion::extension:: struct_member_name<T, index>::call();

s << "<" << name << ">"; serialize(boost::fusion::at_c<index>(value), s); s << "</" << name << ">"; });}

Page 23: Александр Фокин, Рефлексия в C++

23

Реализация на Boost.Fusion

using range = boost::mpl::range_c< size_t, 0, boost::fusion::result_of::size<T>::value>;

Эквивалентно:

using range = boost::mpl::vector< std::integral_constant<size_t, 0>, std::integral_constant<size_t, 1>, /* ... */>;

Page 24: Александр Фокин, Рефлексия в C++

24

Реализация на Boost.Fusion

boost::fusion::for_each(range(), lambda);

Эквивалентно:

lambda(std::integral_constant<size_t, 0>());lambda(std::integral_constant<size_t, 1>());/* ... */

Page 25: Александр Фокин, Рефлексия в C++

25

Реализация на Boost.Fusion

template<class T>void serialize(const T &value, std::ostream &s) { using range = boost::mpl::range_c< size_t, 0, boost::fusion::result_of::size<T>::value>;

boost::fusion::for_each(range(), [&](auto arg) { constexpr size_t index = decltype(arg)::value;

auto name = boost::fusion::extension:: struct_member_name<T, index>::call();

s << "<" << name << ">"; serialize(boost::fusion::at_c<index>(value), s); s << "</" << name << ">"; });}

Page 26: Александр Фокин, Рефлексия в C++

26

Что-то посложнее на Boost.Fusion

class QPoint {public: int x() const; int y() const; void setX(int x); void setY(int y); /* ... */};

BOOST_FUSION_ADAPT_ADT(QPoint, (int, int, obj.x(), obj.setX(val)) (int, int, obj.y(), obj.setY(val)))

int main() { QPoint point(0, 1); serialize(point, std::cout);}

Page 27: Александр Фокин, Рефлексия в C++

27

Что-то посложнее на Boost.Fusion

class QPoint {public: int x() const; int y() const; void setX(int x); void setY(int y); /* ... */};

BOOST_FUSION_ADAPT_ADT(QPoint, (int, int, obj.x(), obj.setX(val)) (int, int, obj.y(), obj.setY(val)))

int main() { QPoint point(0, 1); serialize(point, std::cout);}

error: use of undefined type'boost::fusion::extension::struct_member_name<T,0>'

Page 28: Александр Фокин, Рефлексия в C++

28

Что-то посложнее на Boost.Fusion

class QPoint {public: int x() const; int y() const; void setX(int x); void setY(int y); /* ... */};

BOOST_FUSION_ADAPT_ADT(QPoint, (int, int, obj.x(), obj.setX(val)) (int, int, obj.y(), obj.setY(val)))

int main() { QPoint point(0, 1); serialize(point, std::cout);}

CHALLENGE ACCEPTED

error: use of undefined type'boost::fusion::extension::struct_member_name<T,0>'

Page 29: Александр Фокин, Рефлексия в C++

29

Что-то посложнее на Boost.Fusion

class QPoint {public: int x() const; int y() const; void setX(int x); void setY(int y); /* ... */};

MY_ADAPT_ADT(QPoint, ((x, int, int, obj.x(), obj.setX(val))) ((y, int, int, obj.y(), obj.setY(val))))

int main() { QPoint point(0, 1); serialize(point, std::cout);}

Page 30: Александр Фокин, Рефлексия в C++

30

Что-то посложнее на Boost.Fusion

#define MY_ADAPT_ADT(TYPE, PARAMS) \ BOOST_FUSION_ADAPT_ADT(TYPE, MY_TO_FUSION_PARAMS(PARAMS)) \ MY_ADAPT_ADT_I(TYPE, PARAMS)

/** * ((a, b, c, d, e))((a, b, c, d, e)) -> (b, c, d, e)(b, c, d, e) */#define MY_TO_FUSION_PARAMS(PARAMS) \ BOOST_PP_SEQ_FOR_EACH(MY_TO_FUSION_PARAMS_I, ~, PARAMS)

/** * (a, b, c, d, e) -> (b, c, d, e) */#define MY_TO_FUSION_PARAMS_I(R, D, PARAM) \ BOOST_PP_TUPLE_POP_FRONT(PARAM)

Page 31: Александр Фокин, Рефлексия в C++

31

Что-то посложнее на Boost.Fusion

#define MY_ADAPT_ADT(TYPE, PARAMS) \ BOOST_FUSION_ADAPT_ADT(TYPE, MY_TO_FUSION_PARAMS(PARAMS)) \ MY_ADAPT_ADT_I(TYPE, PARAMS)

#define MY_ADAPT_ADT_I(TYPE, PARAMS) \ BOOST_PP_SEQ_FOR_EACH_I(MY_ADAPT_ADT_II, TYPE, PARAMS)

#define MY_ADAPT_ADT_II(R, TYPE, INDEX, PARAM) \namespace boost { namespace fusion { namespace extension { \ template<> \ struct struct_member_name<TYPE, INDEX> { \ typedef char const *type; \ static type call() { \ return BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(0, PARAM));\ } \ }; \}}}

Page 32: Александр Фокин, Рефлексия в C++

32

Что-то посложнее на Boost.Fusion

class QPoint {public: int x() const; int y() const; void setX(int x); void setY(int y); /* ... */};

MY_ADAPT_ADT(QPoint, ((x, int, int, obj.x(), obj.setX(val))) ((y, int, int, obj.y(), obj.setY(val))))

int main() { QPoint point(0, 1); serialize(point, std::cout);}

error: 'size': is not a member of 'boost::fusion::extension::adt_attribute_proxy<QPoint,0,true>'

Page 33: Александр Фокин, Рефлексия в C++

33

Что-то посложнее на Boost.Fusion

template <typename T, int N, bool Const>void serialize( const boost::fusion::extension:: adt_attribute_proxy<T, N, Const>& value, std::ostream &s) { s << value.get();}

Page 34: Александр Фокин, Рефлексия в C++

34

Что-то посложнее на Boost.Fusion

template <typename T, int N, bool Const>void serialize( const boost::fusion::extension:: adt_attribute_proxy<T, N, Const>& value, std::ostream &s) { s << value.get();}

int main() { QPoint point(0, 1); serialize(point, std::cout);}

<x>0</x><y>1</y>

Page 35: Александр Фокин, Рефлексия в C++

35

Phew!

Image © Energy Live News

PHEW!

Page 36: Александр Фокин, Рефлексия в C++

36

Взгляд со стороны“This is what you get when C++ meets Reflection.

Whatever you choose, it'll probably have horrible macros, hard to debug code or weird build steps.”

–Skizz @ stackoverflow

Image © Wikimedia commons

Page 37: Александр Фокин, Рефлексия в C++

37

Проблемы

• Нет единого Reflection API.

• Приходится использовать макросы и нарушать DRY.

• Нет ни одного готового решения / фреймворка, в котором все работало бы «из коробки».

Page 38: Александр Фокин, Рефлексия в C++

38

Проблемы

• Нет единого Reflection API.

• Приходится использовать макросы и нарушать DRY.

• Нет ни одного готового решения / фреймворка, в котором все работало бы «из коробки».

Page 39: Александр Фокин, Рефлексия в C++

39

Проблемы

• Нет единого Reflection API.

• Приходится использовать макросы и нарушать DRY.

• Нет ни одного готового решения / фреймворка, в котором все работало бы «из коробки»*.

Page 40: Александр Фокин, Рефлексия в C++

40

Как должно быть

Стандартный механизм аннотированияReflection API

Фреймворки для (де)сериализации

RPC и все, что душе угодно

Стан

дарт

ные

сред

ства

С+

+

Библ

иоте

киПо

льзо

вате

льс

кий

код Аннотации для

составных пользовательских

типов

Реализация стандартных функций

для элементарных пользовательских

типов

Page 41: Александр Фокин, Рефлексия в C++

Единый Reflection API дней грядущих

41

Page 42: Александр Фокин, Рефлексия в C++

42

Дизайн

Насколько глубокий Reflection действительно нужен?

• Нужен ли Reflection для шаблонов?

• А для переменных?

• А для шаблонных переменных?

• А для namespace’ов?

• А для private полей?

Page 43: Александр Фокин, Рефлексия в C++

43

Дизайн

Насколько глубокий Reflection действительно нужен?

• Нужен ли Reflection для шаблонов?

• А для переменных?

• А для шаблонных переменных?

• А для namespace’ов?

• А для private полей?

Page 44: Александр Фокин, Рефлексия в C++

44

Дизайн

Насколько глубокий Reflection действительно нужен?

• Нужен ли Reflection для шаблонов?

• А для переменных?

• А для шаблонных переменных?

• А для namespace’ов?

• А для private полей?

Image © Wikifur wiki

Page 45: Александр Фокин, Рефлексия в C++

45

Дизайн

Насколько глубокий Reflection действительно нужен?

• Нужен ли Reflection для шаблонов?

• А для переменных?

• А для шаблонных переменных?

• А для namespace’ов?

• А для private полей?

Page 46: Александр Фокин, Рефлексия в C++

46

Дизайн

Насколько глубокий Reflection действительно нужен?

• Нужен ли Reflection для шаблонов?

• А для переменных?

• А для шаблонных переменных?

• А для namespace’ов?

• А для private полей?

Page 47: Александр Фокин, Рефлексия в C++

47

Предложения в стандарт• N4428, Type Property Queries (Andrew Tomazos &

Christian Kaeser). Легковесный Reflection. Простой API а-ля <type_traits>.

• N4447, From a type T, gather members name and type information, via variadic template expansion (Cleiton Santoia Silva & Daniel Auresco). Легковесный Reflection. Простой API с синтаксисом typeid<T>...,

обсуждение атрибутов.• N4451, Static Reflection (Matus Chochlik).

Глубокий Reflection. API на операторе reflected и возвращаемых им

типах.

Page 48: Александр Фокин, Рефлексия в C++

48

Предложения в стандарт• N4428, Type Property Queries (Andrew Tomazos &

Christian Kaeser). Легковесный Reflection. Простой API а-ля <type_traits>.

• N4447, From a type T, gather members name and type information, via variadic template expansion (Cleiton Santoia Silva & Daniel Auresco). Легковесный Reflection. Простой API с синтаксисом typeid<T>...,

обсуждение атрибутов.• N4451, Static Reflection (Matus Chochlik).

Глубокий Reflection. API на операторе reflected и возвращаемых им

типах.

Page 49: Александр Фокин, Рефлексия в C++

49

Предложения в стандарт• N4428, Type Property Queries (Andrew Tomazos &

Christian Kaeser). Легковесный Reflection. Простой API а-ля <type_traits>.

• N4447, From a type T, gather members name and type information, via variadic template expansion (Cleiton Santoia Silva & Daniel Auresco). Легковесный Reflection. Простой API с синтаксисом typeid<T>...,

обсуждение атрибутов.• N4451, Static Reflection (Matus Chochlik).

Глубокий Reflection. API на операторе reflected и возвращаемых им

типах.

Page 50: Александр Фокин, Рефлексия в C++

50

Type Property Queries

• Добавляет два новых трейта: std::class_traits – для доступа к информации о

классах. std::enum_traits – для доступа к информации о

перечислениях.• Существует реализация для clang.

• Есть план дальнейшего развития с поддержкой namespace’ов и шаблонов.

Page 51: Александр Фокин, Рефлексия в C++

51

Reflection для классов

template<class C>struct class_traits { struct base_classes { static constexpr size_t size;

template<size_t I> struct get { typedef /* ... */ type; static constexpr bool is_virtual; }; };

/* ... */};

Page 52: Александр Фокин, Рефлексия в C++

52

Reflection для классов

template<class C>struct class_traits { /* ... */

struct class_members { static constexpr size_t size;

template<size_t I> struct get { static constexpr string_literal name; static constexpr /* ... */ pointer; }; };

/* ... */};

Page 53: Александр Фокин, Рефлексия в C++

53

Reflection для классов

template<class T>void serialize(const T &value, std::ostream &s) { using members = typename std::class_traits<T>::class_members; using range = boost::mpl::range_c<size_t, 0, members::size>;

boost::fusion::for_each(range(), [&](auto arg) { constexpr size_t index = decltype(arg)::value;

auto name = members::template get<index>::name; auto pointer = members::template get<index>::pointer;

s << "<" << name << ">"; serialize(value.*pointer, s); s << "</" << name << ">"; });}

Page 54: Александр Фокин, Рефлексия в C++

54

Будущее

1. Стандартизации в C++17 ждать не стоит. Но получить Reflection в одном из TS после C++17 шансы вполне есть.

2. После стандартизации появятся библиотеки, использующие стандартный API.

3. …

4. PROFIT!

Page 55: Александр Фокин, Рефлексия в C++

Вопросы?

55

Page 56: Александр Фокин, Рефлексия в C++

Контакты

[email protected]

+7 915 3705385 retgone

retgone

Александр Фокин

Руководитель службы разработки поисковых компонент

Page 57: Александр Фокин, Рефлексия в C++

Спасибо!

57

Page 58: Александр Фокин, Рефлексия в C++
Page 59: Александр Фокин, Рефлексия в C++

Addendum

Page 60: Александр Фокин, Рефлексия в C++

60

Reflection для enum’ов

template<class E>struct enum_traits { struct enumerators { static constexpr size_t size;

template<size_t I> struct get { static constexpr string_literal identifier; static constexpr E value; }; };};


Recommended