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

31

Upload: sergey-platonov

Post on 03-Mar-2017

1.733 views

Category:

Software


5 download

TRANSCRIPT

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

YT

Универсальный адаптер

Григорий Демченко

+ Сервис

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

4

План

▌Поиспользовать продвинутые шаблоны

▌Поиспользовать продвинутые макросы

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

Постановка задачи

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

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;

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

7

Проблема №1

std::cout << d->get() << std::endl;

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

8

Проблема №2

int sum(const SmartMutex<Data>& x, const SmartMutex<Data>& y) { return x->get() + y->get();}

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

Решение

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

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

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

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

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

12

Обобщение обобщенного адаптера

template<typename T_base>struct DataAdapter : T_base { void set(int v) { this->call([](Data& data, int v) { data.set(v); }, v); }};

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

13

Обобщение BaseLockertemplate<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_;};

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

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

Какой тип?

пользователь Microsoft Office
Page 14: Григорий Демченко, Универсальный адаптер

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

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

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

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

Применение

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

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

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

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

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

20

Адаптированный умный указатель

using DataRefCounted = AdaptedShared<Data>; DataRefCounted data;data.set(42);

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

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

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

22

Ленивость

struct Obj { Obj(); void action();};DECL_ADAPTER(Obj, action) Obj obj; // вызов Obj::Objobj.action(); // вызов actionobj.action(); // вызов action AdaptedLazy<Obj> lazyObj; // без Obj::ObjlazyObj.action(); // вызов Obj::Obj и actionlazyObj.action(); // вызов action

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

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

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

24

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

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

25

Disk Batching

struct Disk { void write(const Buffer& buffer);}; AdaptedBatch<Disk> disk; disk.write(buf1);disk.write(buf2);disk.execute(); // запишет buf1 и buf2

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

26

Интегральный пример

DECL_ADAPTER(Disk, write) struct DiskExecute;DECL_ADAPTER(DiskExecute, write, execute) AdaptedLazy<DiskExecute, AdaptedShared<DiskExecute, AdaptedLocked<DiskExecute, AdaptedBatch<Disk>>>> disk; disk.write(buf1); // лениво создаст diskdisk.write(buf2);disk.execute(); // запишет buf1 и buf2

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

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

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

28

Сравнение

// эталонная функцияint f1(int v) { X x; return x.on(v);}

// с адаптеромint f2(int v) { Adapter<X, BaseValue<X>> x; return x.on(v);}

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

29

Результаты▌GCC 4.9.2:

▌Clang 3.5.1:

f1(int): leal 1(%rdi), %eax retf2(int): leal 1(%rdi), %eax ret

f1(int): # @f1(int) leal 1(%rdi), %eax retq f2(int): # @f2(int) leal 1(%rdi), %eax retq

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

30

Выводы

▌Универсальный способ обернуть объект и придать ему новое поведение.

▌Комбинация базовых примитивов.

▌Расширяемость.

▌Разделение вызова и изменения поведения.

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

31

Дальнейшие направления развития

▌Многопоточные примитивы и асинхронность.

▌Транзакционность.

▌Распределенность и консистентность.

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

Григорий Демченко[email protected]

Умный мьютекс:https://habrahabr.ru/post/184436/https://video.yandex.ru/users/ya-events/view/2931

Код: https://github.com/gridem/GodAdapter

РГ21 https://stdcpp.ru

│ Вопросы?