Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

Григорий Демченко, Универсальный адаптер

5.817 visualizaciones

Publicado el

Доклад вводит в рассмотрение универсальный адаптер, позволяющий обернуть любой класс с целью добавления новых свойств, отсутствующих в оригинальном классе. Получаемые классы могут иметь в точности такой же интерфейс, как и первоначальные, что позволяет прозрачно заменять их и оборачивать любое количество раз.

Это позволяет добавлять необходимые свойства объектам, не переписывая его с нуля. Предложенная обобщенная концепция будет последовательно введена и проиллюстрирована простыми, но интересными примерами.

Publicado en: Software
  • Sé el primero en comentar

  • Sé el primero en recomendar esto

Григорий Демченко, Универсальный адаптер

  1. 1. Универсальный адаптер Григорий Демченко YT
  2. 2. План 4 ▌Поиспользовать продвинутые шаблоны ▌Поиспользовать продвинутые макросы
  3. 3. Постановка задачи
  4. 4. Умный мьютекс 6 Статья: https://habrahabr.ru/post/184436/ struct Data { int get() const { return val_; } void set(int v) { val_ = v; } private: int val_ = 0; }; SmartMutex<Data> d; d->set(4); std::cout << d->get() << std::endl;
  5. 5. Проблема №1 7 std::cout << d->get() << std::endl;
  6. 6. Проблема №2 8 int sum(const SmartMutex<Data>& x, const SmartMutex<Data>& y) { return x->get() + y->get(); }
  7. 7. Решение
  8. 8. Адаптер 10 using Lock = std::unique_lock<std::mutex>; struct DataLocked { int get() const { Lock _{mutex_}; return data_.get(); } void set(int v) { Lock _{mutex_}; data_.set(v); } private: mutable std::mutex mutex_; Data data_; };
  9. 9. Обобщенный адаптер 11 template<typename T_base> struct DataAdapter : T_base { void set(int v) { this->call([v](Data& data) { data.set(v); }); } }; struct MutexBase { protected: template<typename F> void call(F f) { Lock _{mutex_}; f(data_); } private: Data data_; std::mutex mutex_; };
  10. 10. Обобщение обобщенного адаптера 12 template<typename T_base> struct DataAdapter : T_base { void set(int v) { this->call([](Data& data, int v) { data.set(v); }, v); } };
  11. 11. Обобщение BaseLocker 13 template<typename T_base, typename T_locker> struct BaseLocker : T_base { protected: template<typename F, typename... V> auto call(F f, V&&... v) { Lock _{lock_}; return f(static_cast<T_base&>(*this), std::forward<V>(v)...); } private: T_locker lock_; };
  12. 12. Обобщение обобщенного обобщенного адаптера 14 void set(int v) { this->call([](Data& data, int v) { data.set(v); }, v); } template<typename... V> auto set(V&&... v) { return this->call([](auto& t, auto&&... x) { return t.set(std::forward<???>(x)...); }, std::forward<V>(v)...); } Какой тип?
  13. 13. Обобщение обобщенного обобщенного адаптера 15 void set(int v) { this->call([](Data& data, int v) { data.set(v); }, v); } template<typename... V> auto set(V&&... v) { return this->call([](auto& t, auto&&... x) { return t.set(std::forward<decltype(x)>(x)...); }, std::forward<V>(v)...); }
  14. 14. Универсальный адаптер 16 #define DECL_FN_ADAPTER(D_name) template<typename... V> auto D_name(V&&... v) { return this->call([](auto& t, auto&&... x) { return t.D_name(std::forward<decltype(x)>(x)...); }, std::forward<V>(v)...); } #define DECL_FN_ADAPTER_ITER(D_r, D_data, D_elem) DECL_FN_ADAPTER(D_elem) #define DECL_ADAPTER(D_type, ...) template<typename T_base> struct Adapter<D_type, T_base> : T_base { BOOST_PP_LIST_FOR_EACH(DECL_FN_ADAPTER_ITER, , BOOST_PP_TUPLE_TO_LIST((__VA_ARGS__))) };
  15. 15. Применение
  16. 16. Мьютекс 18 DECL_ADAPTER(Data, get, set) // сахарок template<typename T, typename T_locker = std::mutex, typename T_base = T> using AdaptedLocked = Adapter< T, BaseLocker<T_base, T_locker>>; using DataLocked = AdaptedLocked<Data>;
  17. 17. Адаптированный умный указатель 19 template<typename T> struct BaseShared { protected: template<typename F, typename... V> auto call(F f, V&&... v) { return f(*shared_, std::forward<V>(v)...); } private: std::shared_ptr<T> shared_; }; template<typename T, typename T_base = T> using AdaptedShared = Adapter<T, BaseShared<T_base>>;
  18. 18. Адаптированный умный указатель 20 using DataRefCounted = AdaptedShared<Data>; DataRefCounted data; data.set(42);
  19. 19. Комбинирование адаптеров 21 template<typename T, typename T_locker = std::mutex, typename T_base = T> using AdaptedSharedLocked = AdaptedShared< T, AdaptedLocked<T, T_locker, T_base>>; using DataTotallyShared = AdaptedSharedLocked<Data>; DataTotallyShared data; // ... auto newData = data; newData.set(42); assert(data.get() == 42);
  20. 20. Ленивость 22 struct Obj { Obj(); void action(); }; DECL_ADAPTER(Obj, action) Obj obj; // вызов Obj::Obj obj.action(); // вызов action obj.action(); // вызов action AdaptedLazy<Obj> lazyObj; // без Obj::Obj lazyObj.action(); // вызов Obj::Obj и action lazyObj.action(); // вызов action
  21. 21. Реализация ленивости 23 template<typename T> struct BaseLazy { template<typename... V> BaseLazy(V&&... v) { state_ = [v...] { return T{std::move(v)...}; }; } protected: template<typename F, typename... V> auto call(F f, V&&... v) { auto* t = boost::get<T>(&state_); if (t == nullptr) { state_ = boost::get<std::function<T()>>(state_)(); t = boost::get<T>(&state_); } return f(*t, std::forward<V>(v)...); } private: boost::variant<std::function<T()>, T> state_; };
  22. 22. Batching 24 template<typename T_base> struct BaseBatch : T_base { void execute() { for (auto&& action: actions_) { action(); } actions_.clear(); } protected: template<typename F, typename... V> auto call(F f, V&&... v) { actions_.push_back([this, f, v...] { f(*this, v...); }); } private: std::vector<std::function<void()>> actions_; };
  23. 23. Disk Batching 25 struct Disk { void write(const Buffer& buffer); }; AdaptedBatch<Disk> disk; disk.write(buf1); disk.write(buf2); disk.execute(); // запишет buf1 и buf2
  24. 24. Интегральный пример 26 DECL_ADAPTER(Disk, write) struct DiskExecute; DECL_ADAPTER(DiskExecute, write, execute) AdaptedLazy<DiskExecute, AdaptedShared<DiskExecute, AdaptedLocked<DiskExecute, AdaptedBatch<Disk>>>> disk; disk.write(buf1); // лениво создаст disk disk.write(buf2); disk.execute(); // запишет buf1 и buf2
  25. 25. Накладные расходы 27 ▌ Тестовый класс template<typename T> struct BaseValue : T { protected: template<typename F, typename... V> auto call(F f, V&&... v) { return f(t, std::forward<V>(v)...); } }; struct X { int on(int v) { return v + 1; } };
  26. 26. Сравнение 28 // эталонная функция int f1(int v) { X x; return x.on(v); } // с адаптером int f2(int v) { Adapter<X, BaseValue<X>> x; return x.on(v); }
  27. 27. Результаты 29 ▌ GCC 4.9.2: ▌ Clang 3.5.1: f1(int): leal 1(%rdi), %eax ret f2(int): leal 1(%rdi), %eax ret f1(int): # @f1(int) leal 1(%rdi), %eax retq f2(int): # @f2(int) leal 1(%rdi), %eax retq
  28. 28. Выводы 30 ▌ Универсальный способ обернуть объект и придать ему новое поведение. ▌ Комбинация базовых примитивов. ▌ Расширяемость. ▌ Разделение вызова и изменения поведения.
  29. 29. Дальнейшие направления развития 31 ▌ Многопоточные примитивы и асинхронность. ▌ Транзакционность. ▌ Распределенность и консистентность.
  30. 30. Григорий Демченко gridem@yandex-team.com Умный мьютекс: https://habrahabr.ru/post/184436/ https://video.yandex.ru/users/ya-events/view/2931 Код: https://github.com/gridem/GodAdapter РГ21 https://stdcpp.ru │ Вопросы?

×