SlideShare a Scribd company logo
1
2
Некоторые паттерны
реализации полиморфного
поведения в C++
Дмитрий Леванов
Ведущий разработчик Крипта
3
Крипта
4
Разработка Крипты
 Много логов в разных форматах
 Сложные цепочки обработки
 Высокие требования к производительности
 Много одинаковой похожей логики
 Хочется делать всё однообразно
5
Полиморфизм
6
Полиморфизм
 Способ поставить в соответствие некой
грамматической конструкции контекстно-
зависимую семантику
или, по-русски:
 Текст программы [почти] один и тот же, а
смысл разный
7
Шаблоны проектирования
 Abstract server/client
 Adapter
 Decorator
 Proxy
 Template method
 Command
 Strategy
 …
8
Виртуальный полиморфизм
struct Base {
virtual void do() { std::cout << “base”; }
};
struct Derived : public Base {
virtual void do() override {
std::cout << “derived”;
}
};
std::unique_ptr<Base> b(new Derived);
b->do(); // derived
 Явные интерфейсы
 Типобезопасно
 Работают фичи, зависящие от _vptr
9
Виртуальный полиморфизм: минусы
 Медленный вызов методов
 Расход памяти на объекты
 Требует наличия иерархии классов
 Приходится иметь дело с T* или T&
 Грабли с виртуальными методами
 Инвариантность параметров
10
Виртуальный полиморфизм: минусы
 Медленный вызов методов
 Расход памяти на объекты
 Требует наличия иерархии классов
 Приходится иметь дело с T* или T&
 Грабли с виртуальными методами
 Инвариантность параметров
 Во многих случаях это не критично
11
Виртуальный полиморфизм: минусы
 Медленный вызов методов
 Расход памяти на объекты
 Требует наличия иерархии классов
 Приходится иметь дело с T* или T&
 Грабли с виртуальными методами
 Инвариантность параметров
 Во многих случаях это не критично
 Но иногда может стать проблемой
12
Простой пример
13
Простой пример
struct Handler {
virtual void handle(int) = 0;
virtual ~Handler() {}
};
void for_each(const std::vector<int>& v, const Handler& h) {
for (int i : v) {
h.handle(i);
}
}
//...
std::vector<int> vect = {1,2,3};
MyHandler handler;
for_each(vect, handler);
14
То же самое, но лучше
template<typename Handler>
void for_each(const std::vector<int>& v, const Handler& h) {
for (int i : v) {
h.handle(i);
}
}
//...
std::vector<int> vect = {1,2,3};
MyHandler handler;
for_each(vect, handler);
15
То же самое, но лучше
template<typename Handler>
void for_each(const std::vector<int>& v, Handler& h) {
for (int i : v) {
h.handle(i);
}
}
struct Sum {
int sum = 0;
void handle(int i) { sum += i; }
};
std::vector<int> vect(1000000000);
Sum handler;
for_each(vect, handler);
16
То же самое, но лучше
template<typename Handler>
void for_each(const std::vector<int>& v, Handler& h) {
for (int i : v) {
h.handle(i);
}
}
struct Sum {
int sum = 0;
void handle(int i) { sum += i; }
};
std::vector<int> vect(1000000000);
Sum handler;
for_each(vect, handler);
Бесплатное ускорение x9.2 (-5 тактов на вызов)
17
То же самое, но лучше
template<typename Handler>
void for_each(const std::vector<int>& v, Handler& h) {
for (int i : v) {
h.handle(i);
}
}
struct PositivesSum {
int sum = 0;
void handle(int i) { if (i > 0) sum += i; }
};
std::vector<int> vect(1000000000);
PositivesSum handler;
for_each(vect, handler);
Бесплатное ускорение x2.8
18
Продолжаем улучшать
template<typename Handler>
void for_each(const std::vector<int>& v, const Handler& h) {
for (int i : v) {
h(i);
}
}
//...
std::vector<int> vect = {1,2,3};
MyHandler handler;
for_each(vect, handler);
19
Совсем хорошо
template<typename Handler>
void for_each(const std::vector<int>& v, const Handler& h) {
for (int i : v) {
h(i);
}
}
//...
std::vector<int> vect = {1,2,3};
for_each(vect, [](int i){std::cout << i; });
20
Или так
template<typename Handler>
void for_each(const std::vector<int>& v) {
for (int i : v) {
Handler::handle(i);
}
}
//...
std::vector<int> vect = {1,2,3};
for_each<MyHandler>(vect);
21
Статический полиморфизм: плюсы
 Нет накладных расходов на вызов методов
 Не надо наследоваться
 Не надо иметь дело с указателями
 Контрвариантность параметров
 Можно использовать лямбды
22
Статический полиморфизм: минусы
 Нельзя положить в коллекцию
 Сложно проверять правильность кода
 Медленно компилируется
 Может распухнуть бинарник
 Нельзя явно задать интерфейсы
 Мало помощи от IDE
23
Синтаксис иногда довольно
странный…
this->do();
24
Синтаксис иногда довольно
странный…
this->do();
typename T::iter f(typename T::iter i);
25
Синтаксис иногда довольно
странный…
this->do();
typename T::iter f(typename T::iter i);
this->template do<T>(); // WTF???
26
Curiously Recurring
Template Pattern
27
Template method
28
Template method
struct Game {
void play() {
while(!end()) {
makeTurn();
switchActivePlayer();
}
}
virtual bool end() = 0;
virtual void makeTurn() = 0;
virtual void switchActivePlayer() = 0;
virtual ~Game() {}
};
29
«Виртуальный» вызов без virtual
a.k.a. Curiously Recurring Template Pattern
30
«Виртуальный» вызов без virtual
a.k.a. Curiously Recurring Template Pattern
template<typename Derived>
class Game {
void end() {
static_cast<Derived*>(this)->end();
}
};
31
«Виртуальный» вызов без virtual
a.k.a. Curiously Recurring Template Pattern
template<typename Derived>
class Game {
void end() {
static_cast<Derived*>(this)->end();
}
};
class Chess : public Game<Chess> {
void end() {/*Check if king surrounded*/}
};
32
«Виртуальный» вызов без virtual
a.k.a. Curiously Recurring Template Pattern
template<typename Derived>
class Game {
void end() {
static_cast<Derived*>(this)->end();
}
};
class Chess : public Game<Chess> {
void end() {/*Check if king surrounded*/}
};
std::unique_ptr<Game<Chess>> game(new Chess);
game->play(); // calls Chess::end() inside
33
CRTP
 «Виртуальный» метод может быть
статическим
 Может работать в ~7 раз быстрее
виртуальной версии*
⃰ http://bit.ly/crtp_vs_virtual
34
Tag dispatching
35
Tag dispatching
template <class InputIter, class Dist>
void advance(InputIter& it, Dist n);
template <class InputIter, class Dist>
void advance(InputIter& i, Dist n) {
while (n--) ++i;
}
template <class RndAcsIter, class Dist>
void advance(RndAcsIter& i, Dist n) {
i += n;
}
36
Tag dispatching
template <class InputIter, class Dist>
void advance(InputIter& it, Dist n);
template <class InputIter, class Dist>
void advance(InputIter& i, Dist n, input_iter_tag) {
while (n--) ++i;
}
template <class RndAcsIter, class Dist>
void advance(RndAcsIter& i, Dist n, rnd_acs_iter_tag) {
i += n;
}
37
Tag dispatching
template <class InputIter, class Dist>
void advance(InputIter& it, Dist n) {
typename iter_traits<InputIter>::iter_category cat;
advance(i, n, cat);
}
template <class InputIter, class Dist>
void advance(InputIter& i, Dist n, input_iter_tag) {
while (n--) ++i;
}
template <class RndAcsIter, class Dist>
void advance(RndAcsIter& i, Dist n, rnd_acs_iter_tag) {
i += n;
}
38
External polymorphism
39
Задача
 Пишем инструмент для отладки
 Есть множество объектов, не связанных
какой-либо иерархией
 Хотим сложить их в одну коллекцию,
проитерироваться по ней, и распечатать
содержимое объектов
int x = 10;
Foo bar;
MagicCollection objects;
objects.add(x);
objects.add(bar);
for (const auto& obj : objects) {
obj.dump();
}
40
External polymorphism
struct Dumpable {
virtual void dump() const = 0;
};
41
External polymorphism
struct Dumpable {
virtual void dump() const = 0;
};
template<typename T>
class ConcreteDumpable<T> : public Dumpable {
const T& value;
public:
ConcreteDumpable(const T& value) : value(value) {}
virtual void dump() const override {
::dump(value);
}
};
42
External polymorphism
struct Dumpable {
virtual void dump() const = 0;
};
template<typename T>
class ConcreteDumpable<T> : public Dumpable {
const T& value;
public:
ConcreteDumpable(const T& value) : value(value) {}
virtual void dump() const override {
::dump(value);
}
};
void dump(const Foo& foo) {
foo.printToConsole();
}
43
External polymorphism
class Dumper {
using DumpablePtr = std::unique_ptr<Dumpable>;
std::vector<DumpablePtr> dumpables;
public:
template<typename T>
void add(const T& obj) {
dumpables.push_back(
DumpablePtr(new ConcreteDumpable<T>(obj)));
}
void dumpAll() const {
for (const auto& d : dumpables) { d->dump(); }
}
};
44
External polymorphism
class Dumper {
using DumpablePtr = std::unique_ptr<Dumpable>;
std::vector<DumpablePtr> dumpables;
public:
template<typename T>
void add(const T& obj) {
dumpables.push_back(
DumpablePtr(new ConcreteDumpable<T>(obj)));
}
void dumpAll() const {
for (const auto& d : dumpables) { d->dump(); }
}
} dumper;
dumper.add(10);
dumper.add(Foo());
dumper.dumpAll();
45
External polymorphism
 Симбиоз виртуального и статического
полиморфизма
 Для поддержки нового типа T надо добавить
только ::dump(T)
 Можно строить параллельные иерархии
46
Новые возможности C++
47
Новые возможности C++11: лямбды
int x = 10;
std::vector<int> v = { 1, 2, 3 };
for_each(v.begin(), v.end(), [x](int i){cout << i+x;});
48
Новые возможности C++11: лямбды
int x = 10;
std::vector<int> v = { 1, 2, 3 };
for_each(v.begin(), v.end(), [x](int i){cout << i+x;});
class Lambda {
int x;
public:
Lambda(int x) : x(x) {}
void operator( )(int i) const {std::cout << i+x;}
};
for_each(v.begin(), v.end(), Lambda());
49
Новые возможности C++14: лямбды
int x = 10;
std::vector<int> v = { 1, 2, 3 };
for_each(v.begin(), v.end(), [x](auto i){cout << i+x;});
50
Новые возможности C++14: лямбды
int x = 10;
std::vector<int> v = { 1, 2, 3 };
for_each(v.begin(), v.end(), [x](auto i){cout << i+x;});
class Lambda {
int x;
public:
Lambda(int x) : x(x) {}
template<typename T>
void operator()(T i) const {std::cout << i+x;}
};
51
Новые возможности C++11:
std::function
void print(int i) { std::cout << i; }
struct Print {
void operator()(int i) { std::cout << i+x; }
void print(int i) { std::cout << i+x; }
}
Print p;
std::function<void(int)> p1 = print;
std::function<void(int)> p2 = Print();
std::function<void(int)> p3 = std::bind(Print::print, p, _1);
std::function<void(int)> p4 = std::bind(Print::print, &p, _1);
std::function<void(const Print&, int)> p5 = &Print::print;
std::function<void(int)> p6 =
[](int i) { std::cout << i; };
52
Новые возможности C++11:
лямбды и std::function
auto lambda1 = []() {};
auto lambda2 = []() {};
lambda1(); // fast
std::function<void()> f = []() {};
f(); // slow
template<typename Handler>
void for_each(const std::vector<int>& v,
const Handler& h);
void for_each(const std::vector<int>& v,
std::function<void(int)> f);
53
Новые возможности C++11:
std::function
 Позволяют сохранить исполняемые объекты
(включая лямбды), в том числе в коллекцию
 Может быть медленной (~10 раз медленнее
шаблонной функции)
 Обеспечивает явную спецификацию
интерфейса
54
Пример из жизни
55
Исходные условия
id=1234 t time=2014.26.09 19:00
struct RecordBase {
void Load(const std::string& str);
std::string GetValue(const std::string& key) const;
void SetValue(const std::string& key,
const std::string& value);
};
 Хотим быстро создавать абстракции, позволяющие
типизированно работать с записями
56
Версия 1.0
struct EventRecord : RecordBase {
int GetId() const {
const auto& str = GetValue("id");
// Parse id from str
return id;
}
void SetId(int id) {
// Serialize id to str
SetValue("id", str);
}
time_t GetTs() const {
const auto& str = GetValue("time");
// Parse ts from str
return ts;
}
void SetTs(time_t ts) {
// Serialize ts to str
SetValue("date", str);
}
};
57
Версия 1.1
struct EventRecord : RecordBase {
int GetId() const {
return ::FromString(GetValue("id"));
}
void SetId(int id) {
SetValue("id", ::ToString(id));
}
int GetTs() const {
return ::FromString(GetValue("time"));
}
void SetTs(time_t ts) {
SetValue("time", ::ToString(ts));
}
};
58
Уходим и�� ::
struct Serializer {
template<typename T>
static T FromString(const std::string& str) {
return ::FromString<T>(str);
}
template<typename T>
static string ToString(const T& value) {
return ::ToString(value);
}
};
59
Инкапсулируем логику RecordBase
struct RecordBase {
std::string GetValue(const std::string& key) const;
void SetValue(const std::string& key,
const std::string& value);
template<typename T, typename Szr = Serializer>
T Get(const string& key) const {
return Szr::FromString<T>(GetValue(key));
}
template<typename Szr = Serializer, typename T>
void Set(const std::string& key, const T& value) {
SetValue(key, Szr::ToString(ts));
}
};
60
Упрощаем EventRecord
struct EventRecord : RecordBase {
int GetId() const {
return Get<int>("id");
}
void SetId(int id) {
Set<>("id", id);
}
int GetTs() const {
return Get<time_t, DateSerializer>("time");
}
void SetTs(time_t ts) {
Set<DateSerializer>("time", ts);
}
};
61
Еще больше инкапсуляции
template<typename T, typename Szr = Serializer>
class Property {
RecordBase* record;
std::string key;
public:
Property(RecordBase* record, const std::string& key)
: record(record), key(key) {}
T Get() const {
return Szr::FromString(record->GetValue(key));
}
void Set(const T& value) {
record->SetValue(key, Szr::ToString(value));
}
};
62
Итоговая версия
struct EventRecord : RecordBase {
Property<int> Id;
Property<time_t, DateSerializer> Ts;
EventRecord() : Id(this, "id"), Ts(this, "time") {}
};
EventRecord record;
record.Id.Set(123);
time_t ts = record.Ts.Get();
63
Заключение
64
Статический полиморфизм
 Большая гибкость
 Ограниченность в этапе компиляции
 Поощряется свежими стандартами
 Сложно писать библиотечный код
 Просто писать клиентский код
65
Спасибо за внимание!
Дмитрий Леванов
Ведущий разработчик Крипта
levanov@yandex-team.ru
66
Источники
1. http://www.inteks.ru/OOAD/P-Cources.OOAD.part1.pdf
2. http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
3. http://www.cs.wustl.edu/~schmidt/PDF/External-Polymorphism.pdf
4. http://www.generic-programming.org/languages/cpp/techniques.php
5. http://eli.thegreenplace.net/2013/12/05/the-cost-of-dynamic-virtual-
calls-vs-static-crtp-dispatch-in-c/

More Related Content

Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов, Яндекс

  • 1. 1
  • 2. 2 Некоторые паттерны реализации полиморфного поведения в C++ Дмитрий Леванов Ведущий разработчик Крипта
  • 4. 4 Разработка Крипты  Много логов в разных форматах  Сложные цепочки обработки  Высокие требования к производительности  Много одинаковой похожей логики  Хочется делать всё однообразно
  • 6. 6 Полиморфизм  Способ поставить в соответствие некой грамматической конструкции контекстно- зависимую семантику или, по-русски:  Текст программы [почти] один и тот же, а смысл разный
  • 7. 7 Шаблоны проектирования  Abstract server/client  Adapter  Decorator  Proxy  Template method  Command  Strategy  …
  • 8. 8 Виртуальный полиморфизм struct Base { virtual void do() { std::cout << “base”; } }; struct Derived : public Base { virtual void do() override { std::cout << “derived”; } }; std::unique_ptr<Base> b(new Derived); b->do(); // derived  Явные интерфейсы  Типобезопасно  Работают фичи, зависящие от _vptr
  • 9. 9 Виртуальный полиморфизм: минусы  Медленный вызов методов  Расход памяти на объекты  Требует наличия иерархии классов  Приходится иметь дело с T* или T&  Грабли с виртуальными методами  Инвариантность параметров
  • 10. 10 Виртуальный полиморфизм: минусы  Медленный вызов методов  Расход памяти на объекты  Требует наличия иерархии классов  Приходится иметь дело с T* или T&  Грабли с виртуальными методами  Инвариантность параметров  Во многих случаях это не критично
  • 11. 11 Виртуальный полиморфизм: минусы  Медленный вызов методов  Расход памяти на объекты  Требует наличия иерархии классов  Приходится иметь дело с T* или T&  Грабли с виртуальными методами  Инвариан��ность параметров  Во многих случаях это не критично  Но иногда может стать проблемой
  • 13. 13 Простой пример struct Handler { virtual void handle(int) = 0; virtual ~Handler() {} }; void for_each(const std::vector<int>& v, const Handler& h) { for (int i : v) { h.handle(i); } } //... std::vector<int> vect = {1,2,3}; MyHandler handler; for_each(vect, handler);
  • 14. 14 То же самое, но лучше template<typename Handler> void for_each(const std::vector<int>& v, const Handler& h) { for (int i : v) { h.handle(i); } } //... std::vector<int> vect = {1,2,3}; MyHandler handler; for_each(vect, handler);
  • 15. 15 То же самое, но лучше template<typename Handler> void for_each(const std::vector<int>& v, Handler& h) { for (int i : v) { h.handle(i); } } struct Sum { int sum = 0; void handle(int i) { sum += i; } }; std::vector<int> vect(1000000000); Sum handler; for_each(vect, handler);
  • 16. 16 То же самое, но лучше template<typename Handler> void for_each(const std::vector<int>& v, Handler& h) { for (int i : v) { h.handle(i); } } struct Sum { int sum = 0; void handle(int i) { sum += i; } }; std::vector<int> vect(1000000000); Sum handler; for_each(vect, handler); Бесплатное ускорение x9.2 (-5 тактов на вызов)
  • 17. 17 То же самое, но лучше template<typename Handler> void for_each(const std::vector<int>& v, Handler& h) { for (int i : v) { h.handle(i); } } struct PositivesSum { int sum = 0; void handle(int i) { if (i > 0) sum += i; } }; std::vector<int> vect(1000000000); PositivesSum handler; for_each(vect, handler); Бесплатное ускорение x2.8
  • 18. 18 Продолжаем улучшать template<typename Handler> void for_each(const std::vector<int>& v, const Handler& h) { for (int i : v) { h(i); } } //... std::vector<int> vect = {1,2,3}; MyHandler handler; for_each(vect, handler);
  • 19. 19 Совсем хорошо template<typename Handler> void for_each(const std::vector<int>& v, const Handler& h) { for (int i : v) { h(i); } } //... std::vector<int> vect = {1,2,3}; for_each(vect, [](int i){std::cout << i; });
  • 20. 20 Или так template<typename Handler> void for_each(const std::vector<int>& v) { for (int i : v) { Handler::handle(i); } } //... std::vector<int> vect = {1,2,3}; for_each<MyHandler>(vect);
  • 21. 21 Статический полиморфизм: плюсы  Нет накладных расходов на вызов методов  Не надо наследоваться  Не надо иметь дело с указателями  Контрвариантность параметров  Можно использовать лямбды
  • 22. 22 Статический полиморфизм: минусы  Нельзя положить в коллекцию  Сложно проверять правильность кода  Медленно компилируется  Может распухнуть бинарник  Нельзя явно задать интерфейсы  Мало помощи от IDE
  • 25. 25 Синтаксис иногда довольно странный… this->do(); typename T::iter f(typename T::iter i); this->template do<T>(); // WTF???
  • 28. 28 Template method struct Game { void play() { while(!end()) { makeTurn(); switchActivePlayer(); } } virtual bool end() = 0; virtual void makeTurn() = 0; virtual void switchActivePlayer() = 0; virtual ~Game() {} };
  • 29. 29 «Виртуальный» вызов без virtual a.k.a. Curiously Recurring Template Pattern
  • 30. 30 «Виртуальный» вызов без virtual a.k.a. Curiously Recurring Template Pattern template<typename Derived> class Game { void end() { static_cast<Derived*>(this)->end(); } };
  • 31. 31 «Виртуальный» вызов без virtual a.k.a. Curiously Recurring Template Pattern template<typename Derived> class Game { void end() { static_cast<Derived*>(this)->end(); } }; class Chess : public Game<Chess> { void end() {/*Check if king surrounded*/} };
  • 32. 32 «Виртуальный» вызов без virtual a.k.a. Curiously Recurring Template Pattern template<typename Derived> class Game { void end() { static_cast<Derived*>(this)->end(); } }; class Chess : public Game<Chess> { void end() {/*Check if king surrounded*/} }; std::unique_ptr<Game<Chess>> game(new Chess); game->play(); // calls Chess::end() inside
  • 33. 33 CRTP  «Виртуальный» метод может быть статическим  Может работать в ~7 раз быстрее виртуальной версии* ⃰ http://bit.ly/crtp_vs_virtual
  • 35. 35 Tag dispatching template <class InputIter, class Dist> void advance(InputIter& it, Dist n); template <class InputIter, class Dist> void advance(InputIter& i, Dist n) { while (n--) ++i; } template <class RndAcsIter, class Dist> void advance(RndAcsIter& i, Dist n) { i += n; }
  • 36. 36 Tag dispatching template <class InputIter, class Dist> void advance(InputIter& it, Dist n); template <class InputIter, class Dist> void advance(InputIter& i, Dist n, input_iter_tag) { while (n--) ++i; } template <class RndAcsIter, class Dist> void advance(RndAcsIter& i, Dist n, rnd_acs_iter_tag) { i += n; }
  • 37. 37 Tag dispatching template <class InputIter, class Dist> void advance(InputIter& it, Dist n) { typename iter_traits<InputIter>::iter_category cat; advance(i, n, cat); } template <class InputIter, class Dist> void advance(InputIter& i, Dist n, input_iter_tag) { while (n--) ++i; } template <class RndAcsIter, class Dist> void advance(RndAcsIter& i, Dist n, rnd_acs_iter_tag) { i += n; }
  • 39. 39 Задача  Пишем инструмент для отладки  Есть множество объектов, не связанных какой-либо иерархией  Хотим сложить их в одну коллекцию, проитерироваться по ней, и распечатать содержимое объектов int x = 10; Foo bar; MagicCollection objects; objects.add(x); objects.add(bar); for (const auto& obj : objects) { obj.dump(); }
  • 40. 40 External polymorphism struct Dumpable { virtual void dump() const = 0; };
  • 41. 41 External polymorphism struct Dumpable { virtual void dump() const = 0; }; template<typename T> class ConcreteDumpable<T> : public Dumpable { const T& value; public: ConcreteDumpable(const T& value) : value(value) {} virtual void dump() const override { ::dump(value); } };
  • 42. 42 External polymorphism struct Dumpable { virtual void dump() const = 0; }; template<typename T> class ConcreteDumpable<T> : public Dumpable { const T& value; public: ConcreteDumpable(const T& value) : value(value) {} virtual void dump() const override { ::dump(value); } }; void dump(const Foo& foo) { foo.printToConsole(); }
  • 43. 43 External polymorphism class Dumper { using DumpablePtr = std::unique_ptr<Dumpable>; std::vector<DumpablePtr> dumpables; public: template<typename T> void add(const T& obj) { dumpables.push_back( DumpablePtr(new ConcreteDumpable<T>(obj))); } void dumpAll() const { for (const auto& d : dumpables) { d->dump(); } } };
  • 44. 44 External polymorphism class Dumper { using DumpablePtr = std::unique_ptr<Dumpable>; std::vector<DumpablePtr> dumpables; public: template<typename T> void add(const T& obj) { dumpables.push_back( DumpablePtr(new ConcreteDumpable<T>(obj))); } void dumpAll() const { for (const auto& d : dumpables) { d->dump(); } } } dumper; dumper.add(10); dumper.add(Foo()); dumper.dumpAll();
  • 45. 45 External polymorphism  Симбиоз виртуального и статического полиморфизма  Для поддержки нового типа T надо добавить только ::dump(T)  Можно строить параллельные иерархии
  • 47. 47 Новые возможности C++11: лямбды int x = 10; std::vector<int> v = { 1, 2, 3 }; for_each(v.begin(), v.end(), [x](int i){cout << i+x;});
  • 48. 48 Новые возможности C++11: лямбды int x = 10; std::vector<int> v = { 1, 2, 3 }; for_each(v.begin(), v.end(), [x](int i){cout << i+x;}); class Lambda { int x; public: Lambda(int x) : x(x) {} void operator( )(int i) const {std::cout << i+x;} }; for_each(v.begin(), v.end(), Lambda());
  • 49. 49 Новые возможности C++14: лямбды int x = 10; std::vector<int> v = { 1, 2, 3 }; for_each(v.begin(), v.end(), [x](auto i){cout << i+x;});
  • 50. 50 Новые возможности C++14: лямбды int x = 10; std::vector<int> v = { 1, 2, 3 }; for_each(v.begin(), v.end(), [x](auto i){cout << i+x;}); class Lambda { int x; public: Lambda(int x) : x(x) {} template<typename T> void operator()(T i) const {std::cout << i+x;} };
  • 51. 51 Новые возможности C++11: std::function void print(int i) { std::cout << i; } struct Print { void operator()(int i) { std::cout << i+x; } void print(int i) { std::cout << i+x; } } Print p; std::function<void(int)> p1 = print; std::function<void(int)> p2 = Print(); std::function<void(int)> p3 = std::bind(Print::print, p, _1); std::function<void(int)> p4 = std::bind(Print::print, &p, _1); std::function<void(const Print&, int)> p5 = &Print::print; std::function<void(int)> p6 = [](int i) { std::cout << i; };
  • 52. 52 Новые возможности C++11: лямбды и std::function auto lambda1 = []() {}; auto lambda2 = []() {}; lambda1(); // fast std::function<void()> f = []() {}; f(); // slow template<typename Handler> void for_each(const std::vector<int>& v, const Handler& h); void for_each(const std::vector<int>& v, std::function<void(int)> f);
  • 53. 53 Новые возможности C++11: std::function  Позволяют сохранить исполняемые объекты (включая лямбды), в том числе в коллекцию  Может быть медленной (~10 раз медленнее шаблонной функции)  Обеспечивает явную спецификацию интерфейса
  • 55. 55 Исходные условия id=1234 t time=2014.26.09 19:00 struct RecordBase { void Load(const std::string& str); std::string GetValue(const std::string& key) const; void SetValue(const std::string& key, const std::string& value); };  Хотим быстро создавать абстракции, позволяющие типизированно работать с записями
  • 56. 56 Версия 1.0 struct EventRecord : RecordBase { int GetId() const { const auto& str = GetValue("id"); // Parse id from str return id; } void SetId(int id) { // Serialize id to str SetValue("id", str); } time_t GetTs() const { const auto& str = GetValue("time"); // Parse ts from str return ts; } void SetTs(time_t ts) { // Serialize ts to str SetValue("date", str); } };
  • 57. 57 Версия 1.1 struct EventRecord : RecordBase { int GetId() const { return ::FromString(GetValue("id")); } void SetId(int id) { SetValue("id", ::ToString(id)); } int GetTs() const { return ::FromString(GetValue("time")); } void SetTs(time_t ts) { SetValue("time", ::ToString(ts)); } };
  • 58. 58 Уходим из :: struct Serializer { template<typename T> static T FromString(const std::string& str) { return ::FromString<T>(str); } template<typename T> static string ToString(const T& value) { return ::ToString(value); } };
  • 59. 59 Инкапсулируем логику RecordBase struct RecordBase { std::string GetValue(const std::string& key) const; void SetValue(const std::string& key, const std::string& value); template<typename T, typename Szr = Serializer> T Get(const string& key) const { return Szr::FromString<T>(GetValue(key)); } template<typename Szr = Serializer, typename T> void Set(const std::string& key, const T& value) { SetValue(key, Szr::ToString(ts)); } };
  • 60. 60 Упрощаем EventRecord struct EventRecord : RecordBase { int GetId() const { return Get<int>("id"); } void SetId(int id) { Set<>("id", id); } int GetTs() const { return Get<time_t, DateSerializer>("time"); } void SetTs(time_t ts) { Set<DateSerializer>("time", ts); } };
  • 61. 61 Еще больше инкапсуляции template<typename T, typename Szr = Serializer> class Property { RecordBase* record; std::string key; public: Property(RecordBase* record, const std::string& key) : record(record), key(key) {} T Get() const { return Szr::FromString(record->GetValue(key)); } void Set(const T& value) { record->SetValue(key, Szr::ToString(value)); } };
  • 62. 62 Итоговая версия struct EventRecord : RecordBase { Property<int> Id; Property<time_t, DateSerializer> Ts; EventRecord() : Id(this, "id"), Ts(this, "time") {} }; EventRecord record; record.Id.Set(123); time_t ts = record.Ts.Get();
  • 64. 64 Статический полиморфизм  Большая гибкость  Ограниченность в этапе компиляции  Поощряется свежими стандартами  Сложно писать библиотечный код  Просто писать клиентский код
  • 65. 65 Спасибо за внимание! Дмитрий Леванов Ведущий разработчик Крипта levanov@yandex-team.ru
  • 66. 66 Источники 1. http://www.inteks.ru/OOAD/P-Cources.OOAD.part1.pdf 2. http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern 3. http://www.cs.wustl.edu/~schmidt/PDF/External-Polymorphism.pdf 4. http://www.generic-programming.org/languages/cpp/techniques.php 5. http://eli.thegreenplace.net/2013/12/05/the-cost-of-dynamic-virtual- calls-vs-static-crtp-dispatch-in-c/