Если 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
if (dynamic_cast
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
}
~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 привело бы к завершению программы.