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

Post on 16-Apr-2017

1.066 views 6 download

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

Reflection в С++

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

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

3

План

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

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

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

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

Что такое Reflection?

4

5

Reflection в C++

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

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

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

структур.

6

Reflection в C++

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

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

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

структур.

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; }}

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>) {}

9

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

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

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

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

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

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

10

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

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

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

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

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

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

11

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

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

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

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

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

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

12

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

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

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

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

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

• .

13

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

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

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

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

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

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

• …

Reflection наших дней

14

15

Подходы

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

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

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

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

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

16

Подходы

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

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

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

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

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

17

Подходы

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

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

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

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

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

18

Подходы

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

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

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

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

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

19

Подходы

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

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

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

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

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

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));

21

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

struct ApiLicenseData { Latin1Array key; Latin1Array licenseBlock;};

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

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 << ">"; });}

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>, /* ... */>;

24

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

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

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

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

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 << ">"; });}

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);}

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>'

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>'

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);}

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)

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));\ } \ }; \}}}

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>'

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();}

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>

35

Phew!

Image © Energy Live News

PHEW!

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

37

Проблемы

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

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

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

38

Проблемы

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

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

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

39

Проблемы

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

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

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

40

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

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

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

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

Стан

дарт

ные

сред

ства

С+

+

Библ

иоте

киПо

льзо

вате

льс

кий

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

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

типов

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

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

типов

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

41

42

Дизайн

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

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

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

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

• А для namespace’ов?

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

43

Дизайн

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

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

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

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

• А для namespace’ов?

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

44

Дизайн

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

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

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

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

• А для namespace’ов?

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

Image © Wikifur wiki

45

Дизайн

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

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

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

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

• А для namespace’ов?

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

46

Дизайн

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

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

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

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

• А для namespace’ов?

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

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 и возвращаемых им

типах.

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 и возвращаемых им

типах.

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 и возвращаемых им

типах.

50

Type Property Queries

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

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

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

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

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; }; };

/* ... */};

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; }; };

/* ... */};

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 << ">"; });}

54

Будущее

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

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

3. …

4. PROFIT!

Вопросы?

55

Контакты

elric@yandex-team.ru

+7 915 3705385 retgone

retgone

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

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

Спасибо!

57

Addendum

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; }; };};