Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

5,785 views

Published on

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

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

Published in: Software
  • Be the first to comment

  • Be the first to like this

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

  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 │ Вопросы?

×