Если sender только ссылается на очередь сообщений, то receiver ей владеет. Мы можем получить объект sender, ссылающийся на очередь, воспользовавшись неявным преобразованием. Процедура диспетчеризации сообщения начинается с обращения к функции wait(). При этом создается объект dispatcher, ссылающийся на очередь, которой владеет receiver. Класс dispatcher показан в следующем листинге; как видите, содержательная работа производится в его деструкторе. В данном случае работа состоит в ожидании сообщения и его диспетчеризации.

Листинг С.4. Класс dispatcher

namespace messaging {

class close_queue {}; ←Сообщение о закрытии очереди

class dispatcher {

 queue* q;                             │Экземпляры

 bool chained;                         │диспетчера нельзя

                                       │копировать

 dispatcher(dispatcher const&)=delete;←┘

 dispatcher& operator=(dispatcher const&)=delete;

 template<

  typename Dispatcher,│Разрешить экземплярам

  typename Msg,       │TemplateDispatcher доступ

  typename Func>     ←┘к закрытым частям класса

 friend class TemplateDispatcher;

 void wait_and_dispatch()

 {          (1) В цикле ждем и диспетчеризуем

  for (;;) {←┘сообщения

   auto msg = q->wait_and_pop();

   dispatch(msg);

  }

 }              (2) dispatch() смотрит, не пришло ли

                 │сообщение close_queue, и, если

 bool dispatch (←┘да, возбуждает исключение

  std::shared_ptr const& msg) {

  if (dynamic_cast*>(msg.get())) {

   throw close_queue();

  }

  return false;

 }

public:                          │Экземпляры диспетчера

 dispatcher(dispatcher&& other):←┘можно перемещать

  q(other.q), chained(other.chained) {│Объект-источник не должен

  other.chained = true;              ←┘ждать сообщений

 }

 explicit dispatcher(queue* q_): q(q_), chained(false) {}

 template

 TemplateDispatcher

 handle(Func&& f)←┐Сообщения конкретного типа

 {               (3) обрабатывает TemplateDispatcher

  return TemplateDispatcher(

   q, this, std::forward(f));

 }

 ~dispatcher() noexcept(false)←┐Деструктор может

 {                            (4) возбудить исключение

  if (!chained) {

   wait_and_dispatch();

  }

 }

};

}

Экземпляр dispatcher, возвращенный функцией wait(), немедленно уничтожается, так как является временным объектом, и, как уже было сказало, вся работа выполняется в его деструкторе. Деструктор вызывает функцию wait_and_dispatch(), которая в цикле (1) ожидает сообщения и передает его функции dispatch(). Сама функция dispatch() (2) проста, как правда: она проверяет, не получено ли сообщение типа close_queue, и, если так, то возбуждает исключение; в противном случае возвращает false, извещая, что сообщение не обработало. Именно из-за исключения close_queue деструктор и помечен как noexcept(false) (4); без этой аннотации действовала бы подразумеваемая спецификация исключений для деструктора — noexcept(true), означающая, что исключения не допускаются, и тогда исключение close_queue привело бы к завершению программы.

Перейти на страницу:

Похожие книги