Игорь Кудрин, «Используем неизменяемые данные и...

96
Константность как инструмент повышения качества кода Кудрин Игорь

Upload: platonov-sergey

Post on 12-Aug-2015

815 views

Category:

Technology


0 download

TRANSCRIPT

Константность как

инструмент повышения

качества кода Кудрин Игорь

Обо мне

Data Analysis designed by Brennan Novak from the thenounproject.com

Mesh Network designed by Lance Weisser from the thenounproject.com

Overlap designed by Nadir Balcikli from the thenounproject.com

Fibonacci Circles designed by Jae Aquino from the thenounproject.com

Проблема

• Язык C++ предоставляет слишком много возможностей

template <typename T> T f(T&& t);

#define M(...) { __VA_ARGS__ }

class virtual

public decltype

const

auto a = [](){}; throw

• Язык C++ предоставляет слишком много возможностей

• Нет времени научиться использовать их правильно

• Язык C++ предоставляет слишком много возможностей

• Нет времени научиться использовать их правильно

• Следствие: сложный и ненадёжный код

Что делать?

• Не использовать лишнего

• Не использовать лишнего • Множественное наследование

• Не использовать лишнего • Множественное наследование • Макросы

• Не использовать лишнего • Множественное наследование • Макросы • Шаблоны

• Не использовать лишнего • Множественное наследование • Макросы • Шаблоны • Исключения

• Не использовать лишнего • Множественное наследование • Макросы • Шаблоны • Исключения • Наследование реализации

• Не использовать лишнего • Упрощать интерфейс

• Не использовать лишнего • Упрощать интерфейс

• Минимум публичных методов

• Не использовать лишнего • Упрощать интерфейс

• Минимум публичных методов • Избегать парных операций

• Не использовать лишнего • Упрощать интерфейс

• Минимум публичных методов • Избегать парных операций • Стабильное состояние

• Не использовать лишнего • Упрощать интерфейс • Использовать const

Неизменяемая переменная

std::vector<int> v = gen_v(); int s; for (int i : v) { s += i; } ... f(s); h(s);

std::vector<int> v = gen_v(); int s; for (int i : v) { s += i; } ... f(s); h(s);

> gcc –Wall –std=c++11 x.cpp No warnings

std::vector<int> v = gen_v(); int s; for (int i : v) { s += i; } ... f(s); h(s);

> gcc –Wall –std=c++11 x.cpp No warnings > gcc –Wall –O –std=c++11 x.cpp warning: ‘s’ may be used uninitialized

std::vector<int> v = gen_v(); int s; for (int i : v) { s += i; } ... f(s); h(s);

std::vector<int> v = gen_v(); int s = 0; for (int i : v) { s += i; } ... f(s); h(s);

std::vector<int> v = gen_v(); int s = 0; for (int i : v) { s += i; } ... f(s); h(s);

А вдруг? void f(int&);

std::vector<int> v = gen_v(); int s = std::accumulate(begin(v), end(v), 0); ... f(s); h(s);

const std::vector<int> v = gen_v(); const int s = std::accumulate(begin(v), end(v), 0); ... f(s); h(s);

const std::vector<int> v = gen_v(); const int s = std::accumulate(begin(v), end(v), 0); ... f(s); h(s);

• Код становится проще

const std::vector<int> v = gen_v(); const int s = std::accumulate(begin(v), end(v), 0); ... f(s); h(s);

• Код становится проще • Защищает от части ошибок

const std::vector<int> v = gen_v(); const int s = std::accumulate(begin(v), end(v), 0); ... f(s); h(s);

• Код становится проще • Защищает от части ошибок • Способствует рефакторингу

Неизменяемый класс

class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { return x_; } int Y() const { return y_; } void Move(int ox, int oy) { x_ += ox; y_ += oy; } private: int x_, y_; };

Point Thread1 Thread2

Move()

X()

Y()

class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { Lock lock(mtx_); return x_; } int Y() const { Lock lock(mtx_); return y_; } void Move(int ox, int oy) { Lock lock(mtx_); x_ += ox; y_ += oy; } private: int x_, y_; mutable std::mutex mtx_; typedef std::lock_guard<std::mutex> Lock; };

Point Thread1 Thread2

Move() X()

Y()

// External synchronization is required! class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { return x_; } int Y() const { return y_; } void Move(int ox, int oy) { x_ += ox; y_ += oy; } private: int x_, y_; };

Point Thread1 Thread2 Mutex

Lock()

X()

Y()

Lock()

Move()

Unlock()

Unlock()

class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { return x_; } int Y() const { return y_; } Point Move(int ox, int oy) const { return Point(x_ + ox, y_ + oy); } private: int x_, y_; };

class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { return x_; } int Y() const { return y_; } Point Move(int ox, int oy) const { return Point(x_ + ox, y_ + oy); } private: int x_, y_; };

Point p(10, 20); p = Point(20, 10);

class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { return x_; } int Y() const { return y_; } Point Move(int ox, int oy) const { return Point(x_ + ox, y_ + oy); } private: const int x_, y_; };

class Point { public: Point(int x, int y) : x_(x), y_(y) {} Point& operator=(const Point&) = delete; int X() const { return x_; } int Y() const { return y_; } Point Move(int ox, int oy) const { return Point(x_ + ox, y_ + oy); } private: const int x_, y_; };

Point2

Thread1 Thread2 Point1

X()

Y()

Move()

Point2

Thread1 Thread2 Point1

X()

Y()

Move()

X()

Y()

Point2

Thread1 Thread2 Point1

X()

Y()

Move()

X()

Y() X()

Y()

• Стабильное состояние

• Стабильное состояние • Невозможно сломать

• Стабильное состояние • Невозможно сломать

например, неправильной внешней синхронизацией

• Стабильное состояние • Невозможно сломать • Экономит время на синхронизации

• Стабильное состояние • Невозможно сломать • Экономит время на синхронизации • Удобно для многопоточной среды

Персистентные структуры

K

D

Q L

N

F

G B

K

D

Q L

N

F

G B

P

K

D

Q L

N

F

G B

P

?

K

D

Q L

N

F

G B

P

K

D

Q L

N

F

G B

P

K’

N’

Q’

D’

L’ B’ G’

F’

K

D

Q L

N

F

G B

P

K’

N’

Q’

K

D

Q L

N

F

G B

P

K’

N’

Q’

std::shared_ptr<T>

Небольшой пример

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Поиск

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Поиск

Справочник

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Map designed by Nicholas Menghini from the thenounproject.com

Поиск

Справочник

Карта

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Map designed by Nicholas Menghini from the thenounproject.com

Folder Tree designed by Juan Pablo Bravo from the thenounproject.com

Поиск

Справочник

Карта

VFS

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Map designed by Nicholas Menghini from the thenounproject.com

Folder Tree designed by Juan Pablo Bravo from the thenounproject.com

Поиск

Справочник

Карта

VFS

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Map designed by Nicholas Menghini from the thenounproject.com

Folder Tree designed by Juan Pablo Bravo from the thenounproject.com

Поиск

Справочник

Карта

Обновления

VFS

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Map designed by Nicholas Menghini from the thenounproject.com

Folder Tree designed by Juan Pablo Bravo from the thenounproject.com

Поиск

Справочник

Карта

Обновления

VFS

Виртуальная файловая система

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

1

typedef std::shared_ptr<const IFile> IFilePtr; typedef std::shared_ptr<const IDir> IDirPtr;

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

1 Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

typedef std::shared_ptr<const IFile> IFilePtr; typedef std::shared_ptr<const IDir> IDirPtr;

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

1 Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

1 Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

typedef std::shared_ptr<const VfsNode> VfsNodePtr;

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

1 Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

typedef std::shared_ptr<const VfsNode> VfsNodePtr;

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

1 Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

typedef std::shared_ptr<const VfsNode> VfsNodePtr;

class Vfs { public: IDirPtr Root() const; void AddDir(const Path& path, IDirPtr dir); void RemoveDir(const Path& path); };

class Vfs { public: IDirPtr Root() const; void AddDir(const Path& path, IDirPtr dir); void RemoveDir(const Path& path); private: std::mutex update_mtx_; std::atomic<VfsNodePtr> root_; };

void Vfs::AddDir(const Path& path, IDirPtr dir) { std::lock_guard<std::mutex> lock(update_mtx_); const VfsNodePtr root = root_; root_ = root ? root->AddDir(path, std::move(dir)) : VfsNode::Create(path, std::move(dir)); }

std::mutex update_mtx_; std::atomic<VfsNodePtr> root_;

void Vfs::RemoveDir(const Path& path) { std::lock_guard<std::mutex> lock(update_mtx_); if (const VfsNodePtr root = root_) { root_ = root->RemoveDir(path); } }

std::mutex update_mtx_; std::atomic<VfsNodePtr> root_;

IDirPtr Vfs::Root() const { return root_.load(); }

std::mutex update_mtx_; std::atomic<VfsNodePtr> root_;

Выводы

• Используем const

• Используем const • Делаем «неубиваемые» классы

• Используем const • Делаем «неубиваемые» классы • Используем персистентные структуры

Кудрин Игорь [email protected]

Mail designed by Andy Fuchs from the thenounproject.com

[email protected]

struct IFile; typedef std::shared_ptr<const IFile> IFilePtr; struct IDir; typedef std::shared_ptr<const IDir> IDirPtr; struct IDir { virtual IFilePtr File(const std::string& name) const = 0; virtual IDirPtr Dir(const std::string& name) const = 0; };

Бонус: IDir

class Path { public: const std::string& Head() const; Path Tail() const; };

Бонус: Path

class VfsNode : public IDir, public std::enable_shared_from_this<VfsNode> { typedef std::unordered_map<std::string, VfsNodePtr> ChildrenMap; const IDirPtr dir_; const ChildrenMap children_; public: explicit VfsNode(IDirPtr dir) : dir_(std::move(dir)), children_() {} explicit VfsNode(ChildrenMap children) : dir_(), children_(std::move(children)) {} };

Бонус: VfsNode

VfsNodePtr VfsNode::Create(const Path& path, IDirPtr dir) { if (path.Head().empty()) { return std::make_shared<VfsNode>(std::move(dir)); } else { ChildrenMap children = { { path.Head(), Create(path.Tail(), std::move(dir)) } }; return std::make_shared<VfsNode>(std::move(children)); } }

Бонус: VfsNode

VfsNodePtr VfsNode::AddDir(const Path& path, IDirPtr dir) const { if (path.Head().empty()) { return std::make_shared<VfsNode>(std::move(dir)); } ChildrenMap children = children_; VfsNodePtr& node = children[path.Head()]; node = (nullptr != node) ? node->AddDir(path.Tail(), std::move(dir)) : VfsNode::Create(path.Tail(), std::move(dir)); return std::make_shared<VfsNode>(std::move(children)); }

Бонус: VfsNode

VfsNodePtr VfsNode::RemoveDir(const Path& path) const { if (path.Head().empty()) return VfsNodePtr(); ChildrenMap children = children_; const auto it = children.find(path.Head()); if (it == children.end()) return shared_from_this(); VfsNodePtr new_child_node = it->second->RemoveDir(path.Tail()); if (new_child_node == it->second) return shared_from_this(); if (new_child_node) it->second.swap(new_child_node); else { children.erase(it); if (children.empty()) return VfsNodePtr(); } return std::make_shared<VfsNode>(std::move(children)); }

Бонус: VfsNode