More Related Content Similar to Григорий Демченко, Универсальный адаптер (20) More from Sergey Platonov (20) Григорий Демченко, Универсальный адаптер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_;
};
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_;
};
12. Обобщение 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_;
};
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)...);
}
Какой тип?
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)...);
}
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__)))
};
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>;
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>>;
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);
21. Ленивость
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
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_;
};
23. 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_;
};
24. Disk Batching
25
struct Disk {
void write(const Buffer& buffer);
};
AdaptedBatch<Disk> disk;
disk.write(buf1);
disk.write(buf2);
disk.execute(); // запишет buf1 и buf2
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); // лениво создаст disk
disk.write(buf2);
disk.execute(); // запишет buf1 и buf2
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;
}
};
28. Результаты
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
29. Выводы
30
▌ Универсальный способ обернуть объект и придать ему
новое поведение.
▌ Комбинация базовых примитивов.
▌ Расширяемость.
▌ Разделение вызова и изменения поведения.