Статический и динамический полиморфизм в C++, Дмитрий...

63
1

description

На примере некоторых архитектурных решений Крипты Дмитрий расскажет о способах реализации полиморфного поведения в программах на C++, о преимуществах и недостатках этих способов, а также о новых возможностях C++11.

Transcript of Статический и динамический полиморфизм в C++, Дмитрий...

1

2

Статический и динамический полиморфизм в C++

Дмитрий ЛевановВедущий разработчик Крипта

3

Крипта

Отвечает на вопрос «Кто?»Определяет интересы по поведению в

интернетеИспользуется для таргетинга рекламыОт др.-греч. κρυπτή — крытый подземный

ход, тайник

4

Как учили Крипту

Матрикснет

Обучение Контроль

Логи+

5

Крипта

6

Разработка Крипты

Много логов в разных форматахСложные цепочки обработкиВысокие требования к производительности

Много одинаковой похожей логикиХочется делать всё однообразно

7

Полиморфизм

8

Полиморфизм

Способ поставить в соответствие некой грамматической конструкции контекстно-зависимую семантику

или, по-русски:

Текст программы [почти] один и тот же, а смысл разный

9

Виртуальный полиморфизм

struct Base {virtual void do() { std::cout << “base”; }

};

struct Derived : public Base {virtual void do() { std::cout << “derived”;

}};

Base* b = new Derived();b->do(); // derived

ООП-шненькоТипобезопасноРаботают фичи, зависящие от _vptr

10

Виртуальный полиморфизм: минусы

Медленный вызов методовНадо поддерживать иерархию классовПриходится иметь дело с T* или T&Грабли с виртуальными методамиСтатический метод не может быть

виртуальнымИнвариантность параметров

11

Зачастую все сводится к…

void for_each(const vector<int>& v, const Handler& h) {for (int i : v) {

h.handle(i);}

}

//...

vector<int> vect = {1,2,3};MyHandler handler;for_each(vect, handler);

12

То же самое, но лучше

template<typename Handler>void for_each(const vector<int>& v, const Handler& h) {

for (int i : v) {h.handle(i);

}}

//...

vector<int> vect = {1,2,3};MyHandler handler;for_each(vect, handler);

13

Продолжаем улучшать

template<typename Handler>void for_each(const vector<int>& v, const Handler& h) {

for (int i : v) {h(i);

}}

//...

vector<int> vect = {1,2,3};MyHandler handler;for_each(vect, handler);

14

Совсем хорошо

template<typename Handler>void for_each(const vector<int>& v, const Handler& h) {

for (int i : v) {h(i);

}}

//...

vector<int> vect = {1,2,3};for_each(vect, [](int i){ cout << i; });

15

Или так

template<typename Handler>void for_each(const vector<int>& v) {

for (int i : v) {Handler::handle(i);

}}

//...

vector<int> vect = {1,2,3};MyHandler handler;for_each<MyHandler>(vect);

16

Статический полиморфизм: плюсы

ТипобезопасноБыстрый вызов методовНе надо наследоватьсяНе надо иметь дело с указателямиКонтрвариантность параметровМожно использовать лямбды

17

Статический полиморфизм: минусы

Нельзя положить в коллекциюСложно проверять правильность кодаМедленно компилируетсяМожет распухнуть бинарникНет поддержки концептовЕсть ограничения компилятораНе во всех IDE правильно работает

автокомплит

18

Синтаксис иногда довольно странный…

this->do();

19

Синтаксис иногда довольно странный…

this->do();

typename T::iter f(typename T::iter i);

20

Синтаксис иногда довольно странный…

this->do();

typename T::iter f(typename T::iter i);

this->template do<T>(); // WTF???

21

Замещениевиртуального полиморфизма статическим

22

«Виртуальный» вызов без virtuala.k.a. Curiously Recurring Template Pattern

23

«Виртуальный» вызов без virtuala.k.a. Curiously Recurring Template Pattern

template<typename Derived>class Base { void do() { Derived::do(); }};

24

«Виртуальный» вызов без virtuala.k.a. Curiously Recurring Template Pattern

template<typename Derived>class Base { void do() { Derived::do(); }};

class MyDerived : public Base<MyDerived> { void do() { std::cout << "my derived"; }};

25

«Виртуальный» вызов без virtuala.k.a. Curiously Recurring Template Pattern

template<typename Derived>class Base { void do() { Derived::do(); }};

class MyDerived : public Base<MyDerived> { void do() { std::cout << "my derived"; }};

Base<MyDerived>* b = new MyDerived();b->do(); // my derived

26

«Виртуальный» вызов без virtuala.k.a. Curiously Recurring Template Pattern

template<typename Derived>class Base { void do() { static_cast<Derived*>(this)->do(); }};

class MyDerived : public Base<MyDerived> { void do() { std::cout << "my derived"; }};

Base<MyDerived>* b = new MyDerived();b->do(); // my derived

27

CRTP

Идеально подходит для шаблона проектирования «Template method»

«Виртуальный» метод может быть статическим

Работает в ~7 раз быстрее виртуальной версии

28

Иногда без статического полиморфизма не обойтись

29

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

30

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

31

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

32

Совмещаем концепции

33

ЗадачаНапример, мы пишем дебаггерЕсть множество объектов, не связанных

какой-либо иерархиейХотим сложить их в одну коллекцию,

проитерироваться по ней, и сдампить объекты

int x = 10;Foo bar;objects.add(x);objects.add(bar);

for (const auto& obj : objects) { obj.dump();}

34

External polymorphismstruct Dumpable { virtual void dump() const = 0;};

35

External polymorphismstruct 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 { ::dump(value); }};

36

External polymorphismstruct 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 { ::dump(value); }};

void dump(const Foo& foo) { foo.printToConsole();}

37

External polymorphismclass Dumper { std::vector<Dumpable*> dumpables;public: template<typename T> void add(T& obj) { auto dumpable = new ConcreteDumpable<T>(obj); dumpables.push_back(dumpable); } void dumpAll() const { for (auto d : dumpables) { d->dump(); } }};

38

External polymorphismclass Dumper { std::vector<Dumpable*> dumpables;public: template<typename T> void add(T& obj) { auto dumpable = new ConcreteDumpable<T>(obj); dumpables.push_back(dumpable); } void dumpAll() const { for (auto d : dumpables) { d->dump(); } }} dumper;

int x = 10;Foo bar;

dumper.add(x);dumper.add(foo);dumper.dumpAll();

39

External polymorphism

Симбиоз виртуального и статического полиморфизма

Для поддержки нового типа T надо добавить только ::dump(T)

Можно строить параллельные иерархии

40

Новые возможности C++

41

Новые возможности C++11: лямбдыint x = 10;vector<int> v = { 1, 2, 3 };

for_each(v.begin(), v.end(), [x](int i){cout << i+x;});

42

Новые возможности C++11: лямбдыint x = 10;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) {cout << i+x;}};

for_each(Lambda());

43

Новые возможности C++14: лямбдыint x = 10;vector<int> v = { 1, 2, 3 };

for_each(v.begin(), v.end(), [x](auto i){cout << i+x;});

44

Новые возможности C++14: лямбдыint x = 10;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) {cout << i+x;}};

45

Новые возможности C++11: std::function

using namespace std;

void print(int i) { cout << i; }

struct Print { void operator()(int i) {cout << i+x;}}

46

Новые возможности C++11: std::function

using namespace std;

void print(int i) { cout << i; }

struct Print { void operator()(int i) {cout << i+x;}}

function<void(int)> p1 = print;function<void(int)> p1 = Print;function<void(int)> p1 = [](int i) {cout << i+x;};

47

Новые возможности C++11: std::function

using namespace std;

void print(int i) { cout << i; }

struct Print { void operator()(int i) {cout << i+x;}}

function<void(int)> p1 = print;function<void(int)> p1 = Print;function<void(int)> p1 = [](int i) {cout << i+x;};

48

Новые возможности C++11: std::function

Медленные (~10 раз медленнее шаблонной функции)

Обеспечивают поддержку концептовПозволяют сохранить исполняемые объекты

49

Новые возможности C++11: std::function

Медленные (~10 раз медленнее шаблонной функции)

Обеспечивают поддержку концептовПозволяют сохранить исполняемые объекты

Не замена шаблонам и виртуальным методам!

50

Пример из жизни

51

Исходные условияid=1234 \t time=2014.26.09 19:00

struct RecordBase { void Load(const string& str); string GetValue(const string& key) const; void SetValue(const string& key, const string& value);};

52

Версия 1.0struct EventRecord : RecordBase { int GetId() const { string str = GetValue("id"); // Parse id from str return id; } void SetId(int id) {

// Serialize id to str SetValue("id", str); } time_t GetTs() const { string str = GetValue("time"); // Parse ts from str return ts; } void SetTs(time_t ts) { // Serialize ts to str SetValue("date", str); } };

53

Версия 1.1struct 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)); } };

54

Уходим из ::template<typename T>struct Serializer { static T FromString(const string& str) { return ::FromString(str); } static string FromString(const T& value) { return ::ToString(value); }};

55

Инкапсулируем логику RecordBase

struct RecordBase { string GetValue(const string& key) const; void SetValue(const string& key, const string& value); template<typename Szr = Serializer, typename T> T Get(const string& key) const { return Szr::FromString(GetValue(key)); } template<typename Szr = Serializer, typename T> void Set(const string& key, const T& value) { SetValue(key, Szr::ToString(ts)); }};

56

Упрощаем EventRecordstruct EventRecord : RecordBase { int GetId() const { return Get<>("id"); } void SetId(int id) { Set<>("id", id); }

int GetTs() const { return Get<DateSerializer>("time"); } void SetTs(time_t ts) { Set<DateSerializer>("time", ts); } };

57

Еще больше инкапсуляцииtemplate<typename T, typename Szr = Serializer>class Property { RecordBase* record; string key; public: Property(RecordBase* record, const string& key) : record(record), key(key) {}

T Get() const { return Szr::FromString(record->GetValue(key)); }

void SetTs(const T& value) { record->SetValue(key, Szr::ToString(value)); }};

58

Итоговая версия

struct EventRecord : RecordBase { Property<int> Id; Property<time_t, DateSerializer> Ts; EventRecord() : Id("id"), Ts("time") {}};

EventRecord record;record.Id.Set(123);time_t ts = record.Ts.Get();

59

Итоги

60

Топ фич (субъективный)

1. Обычный метод/функция2. Шаблонный метод/функция (+лямбды)3. Шаблонный класс (+CRTP)4. Виртуальный метод5. Внешний полиморфизм6. std::function

61

Топ фич (субъективный)

1. Обычный метод/функция2. Шаблонный метод/функция (+лямбды)3. Шаблонный класс (+CRTP)4. Виртуальный метод5. Внешний полиморфизм6. std::function

62

Спасибо за внимание!

Дмитрий ЛевановВедущий разработчик Крипта

[email protected]