C++ 2: boost::bind & boost::function

21
Лекция 6. Исключения Valery Lesin. C++ In-Depth, 2014 1

Transcript of C++ 2: boost::bind & boost::function

Page 1: C++ 2: boost::bind & boost::function

Лекция 6. Исключения

Valery Lesin. C++ In-Depth, 2014 1

Page 2: C++ 2: boost::bind & boost::function

Способы обработки ошибок

• Ошибка повод для std::terminate

• Вернуть признак ошибки и выставить

глобальный код ошибки

• Вернуть код ошибки

• Бросить исключение:

– его невозможно проигнорировать;

– распространяется автоматически;

– выносит обработку из основного потока

выполнения

– незаменимо в конструкторах или операторах.

Valery Lesin. C++ In-Depth, 2014 2

Page 3: C++ 2: boost::bind & boost::function

try...catch

Valery Lesin. C++ In-Depth, 2014 3

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

27.

try

{

// throws explicit or implicit

throw exception_type(/*...*/);

//...

}

catch(std::exception const& err)

{/*...*/}

catch(...)

{

/*...*/

throw;

}

//...

Type::Type(/*...*/)

try

: field1_(/*...*/)

, field2_(/*...*/)

{

/*...*/

}

catch(std::exception const&)

{

/*some logging*/

throw;

}

Page 4: C++ 2: boost::bind & boost::function

Типы исключений

• Исключение может быть любым типом (хоть int или const char*)

• Разумно делать класс исключения, наследуя его от std::exception (и реализовать what)

• Есть стандартные исключения, например, std::runtime_error, logic_error (<stdexcept>)

• Перерехват по

– точному соответствию типа;

– ссылке на базовый класс;

– по приводимому указателю.

Valery Lesin. C++ In-Depth, 2014 4

Page 5: C++ 2: boost::bind & boost::function

Использование RAII

• Есть ли проблемы в этом коде?

Valery Lesin. C++ In-Depth, 2014 5

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

double sqrt(double value)

{

if (value < 0) throw runtime_error("nagative");

/*...*/

}

double* make_roots(double* values, size_t size)

{

double* roots = new double[size];

transform(values, values + size, roots, sqrt);

return roots;

}

Page 6: C++ 2: boost::bind & boost::function

Использование RAII (2)

• Есть ли проблемы в этом коде?

Valery Lesin. C++ In-Depth, 2014 6

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

double* make_roots(double* values, size_t size)

{

double* roots = new double[size];

try

{

transform(values, values + size, roots, sqrt);

}

catch(std::exception const& err)

{

delete [] roots;

cerr << err.what() << endl;

throw;

}

return roots;

}

double* make_roots(double* values, size_t size)

{

unique_ptr<double> roots(new double[size]);

transform(values, values + size, roots.get(), sqrt);

return roots.release();

}

Page 7: C++ 2: boost::bind & boost::function

Передача параметров

• Исправляет ли вызов в (10) ошибочный вызов в (3)?

Valery Lesin. C++ In-Depth, 2014 7

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

void foo(T* a, U* b);

/*...*/

void foo(new T(/*...*/), new U(/*...*/));

typedef shared_ptr<T> T_ptr;

typedef shared_ptr<U> U_ptr;

void foo(T_ptr a, U_ptr b);

/*...*/

void foo(T_ptr(new T(/*...*/)), U_ptr(new U(/*...*/)));

Page 8: C++ 2: boost::bind & boost::function

Передача параметров(2)

• Если для соответствующего RAII класса нет фабрики, используйте способ (1).

Valery Lesin. C++ In-Depth, 2014 8

1.

2.

3.

4.

5.

6.

7.

8.

9.

void foo(T_ptr a, U_ptr b);

//(1)

T_ptr a(new T(/*...*/));

U_ptr b(new U(/*...*/));

foo(a, b);

// (2)

foo(make_shared<T>(/*...*/), make_shared<U>(/*...*/));

Page 9: C++ 2: boost::bind & boost::function

Класс stack

• Попробуем разработать безопасную

реализацию класса stack

Valery Lesin. C++ In-Depth, 2014 9

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

template<class T> struct stack

{

stack(size_t def_size = 10);

~stack();

T pop();

void push(T const&);

/*...*/

private:

size_t size_; // buffer size

size_t used_; // number of objects

T* buf_ ; // main buffer

};

Page 10: C++ 2: boost::bind & boost::function

Класс stack, конструктор

• Такой код нейтрален – любое исключение

передается дальше.

• Не вызывает утечек памяти.

• Независимо от наличия исключений стек

остается согласованным (либо

несозданным).

Valery Lesin. C++ In-Depth, 2014 10

1.

2.

3.

4.

5.

6.

template<class T>

stack<T>::stack(size_t def_size)

: size_(def_size)

, used_(0)

, buf_ (new T[def_size])

{}

Page 11: C++ 2: boost::bind & boost::function

Гарантии безопасности исключений

• Базовая гарантия: даже при наличии генерируемых объектом класса T (или иных) исключений утечки в классе stack отсутствуют (согласованность).

• Строгая гарантия: если операция прекращается из-за генерации исключения, состояние программы остается неизменным (rollback).

• Гарантия отсутствия исключений: функция не генерирует исключений ни при каких обстоятельствах.

Valery Lesin. C++ In-Depth, 2014 11

Page 12: C++ 2: boost::bind & boost::function

Гарантия отсутствия исключений

• Деструкторы:

Что будет, если 13-й объект при создании

бросит исключение, а затем 12-й при

удалении?

• Функция swap

Valery Lesin. C++ In-Depth, 2014 12

1. Type array [42];

Page 13: C++ 2: boost::bind & boost::function

Копирование стека• Воспользуемся вспомогательной функцией:

Valery Lesin. C++ In-Depth, 2014 13

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

18.

19.

template<class T>

T* new_copy(const T* src, size_t src_size, size_t

dst_size)

{

// could be made via unique_ptr

T* dst = new T[dst_size];

try

{

copy(src, src + src_size, dst);

}

catch(...)

{

delete[] dst;

throw;

}

return dst;

}

Page 14: C++ 2: boost::bind & boost::function

Копирование стека (2)

Valery Lesin. C++ In-Depth, 2014 14

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

18.

19.

20.

21.

22.

template<class T>

stack<T>::stack(stack const& o)

: buf_ (new_copy(o.buf_, o.src_size, o.dst_size))

, size_(o.size_)

, used_(o.used_)

{

}

template<class T>

stack<T>& stack<T>::operator=(stack<T> const& o)

{

if (this != other)

{

T* cp = new_copy(o.buf_, o.src_size, o.dst_size);

delete[] buf_; // no more exceptions

buf_ = cp;

size_ = o.size_;

used_ = o.used_;

}

return *this;

}

Page 15: C++ 2: boost::bind & boost::function

push / pop

Valery Lesin. C++ In-Depth, 2014 15

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

template<class T>

void stack<T>::push(T const& t)

{

if (used_ == size_)

{

size_t sz = 2 * size_ + 1;

T* cp = new_copy(buf_, size_, sz);

delete [] buf_; // now ok

buf_ = cp;

size_ = sz;

}

buf_[used_] = t;

++used_;

}

template<class T>

T stack<T>::pop()

{

if (empty()) throw /*...*/;

T res = buf[used_ - 1];

--used_;

return res; // what to do?

}

Page 16: C++ 2: boost::bind & boost::function

pop / top

Valery Lesin. C++ In-Depth, 2014 16

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

template<class T>

T& stack<T>::top()

{

if (empty()) throw /*...*/;

return buf[used_ - 1];

}

template<class T>

void stack<T>::pop()

{

if (empty()) throw /*...*/;

--used_;

}

• Разделим pop на pop и top:

Page 17: C++ 2: boost::bind & boost::function

Гарантии и требования stack

• Требования:

– наличие конструктора по умолчанию;

– наличие копирующего конструктора;

– деструктор без исключений;

– безопасный оператор присваивания.

• Гарантии:

– строгая гарантия при удовлетворении

требований.

Valery Lesin. C++ In-Depth, 2014 17

Page 18: C++ 2: boost::bind & boost::function

Уменьшение требований stack

• Для начала рассмотрим функции:

Valery Lesin. C++ In-Depth, 2014 18

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

template<class T1, class T2>

void construct(T1* p, T2 const& value)

{ new (p) T1(value); }

template<class T>

void destroy(T* p)

{ p->~T();}

template<class fwd_it>

void destroy(fwd_it beg, fwd_it end)

{

while (beg != end)

{

destroy(&*beg);

++beg;

}

}

Page 19: C++ 2: boost::bind & boost::function

stack_impl

Valery Lesin. C++ In-Depth, 2014 19

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

stack_impl::stack_impl(size_t size)

: buf (static_cast<T*>(size == 0 ?

0 : new char[size * sizeof(T)])

, size(size)

, used(0)

{}

stack_impl::~stack_impl()

{

destroy(buf, buf + used);

delete static_cast<void*>(buf);

}

• Сделаем вспомогательный класс:

Page 20: C++ 2: boost::bind & boost::function

Новый вариант stack

Valery Lesin. C++ In-Depth, 2014 20

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

struct stack

{

/**/

stack_impl impl_;

}

stack::stack(size_t def_size)

: impl_(def_size)

{}

stack::stack(stack const& o)

: impl_(o.impl_.used)

{

while (used < o.impl_.used)

{

construct(impl_.buf + impl_.used,

o.impl_.buf + impl_.used);

++impl_.used;

}

}

stack& stack::operator=(stack o)

{

swap(*this, o);

return *this;

}

Page 21: C++ 2: boost::bind & boost::function

To be continued…

Valery Lesin. C++ In-Depth, 2014 21