Параллельное программирование на С++ в действии — страница 42 из 53

>wait_and_pop();

   if (dispatch(msg))←┐
Если мы обработали

   break;             │
сообщение выходим

  }                  
(1) из цикла

 }


 bool dispatch(std::shared_ptr const& msg) {

  if (wrapped_message* wrapper =

   dynamic_cast*>(

   msg.get())) {       ←┐
Проверяем тип

   f(wrapper->contents);│
сообщения и

   return true;         │
вызываем

  }                    
(2) функцию

  else {

   return prev->dispatch(msg);←┐
Вызываем предыдущий

  }                           
(3) диспетчер в цепочке

 }


public:

 TemplateDispatcher(TemplateDispatcher&& other):

  q(other.q), prev(other.prev), f(std::move(other.f)),

  chained(other.chained) {

  other.chained = true;

 }


 TemplateDispatcher(

  queue* q_, PreviousDispatcher* prev_, Func&& f_):

  q(q_), prev(prev_), f(std::forward(f_)), chained(false)

 {

  prev_->chained = true;

 }


 template

 TemplateDispatcher

 handle(OtherFunc&& of)←┐
Дополнительные обработчики

 {                     
(4) можно связать в цепочку

  return TemplateDispatcher<

   TemplateDispatcher, OtherMsg, OtherFunc>(

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

 }


 ~TemplateDispatcher() noexcept(false)←┐
Деструктор снова

 {                                     │
помечен как

  if (!chained) {                     
(5) noexcept(false)

   wait_and_dispatch();

  }

 }

};

}

Шаблон класса

TemplateDispatcher<>
устроен по образцу класса
dispatcher
и почти ничем не отличается от него. В частности, деструктор тоже вызывает
wait_and_dispatch()
, чтобы дождаться сообщения.

Поскольку мы не возбуждаем исключения, если сообщение обработало, то теперь в цикле (1) нужно проверять, обработали мы сообщение или нет. Обработка прекращается, как только сообщение успешно обработало, чтобы в следующий раз можно было ждать очередного набора сообщений. Если найдено соответствие указанному типу сообщения, то вызывается предоставленная функция (2), а не возбуждается исключение (хотя функция-обработчик может и сама возбудить исключение). Если же соответствие не найдено, то мы передаем сообщение предыдущему диспетчеру в цепочке (3). В самом первом экземпляре это будет объект

dispatcher
, но если в функции
handle()
(4) вызовы сцеплялись, чтобы можно было обработать несколько типов сообщений, то предыдущим диспетчером может быть ранее созданный экземпляр
TemplateDispatcher<>
, который в свою очередь передаст сообщение предшествующему ему диспетчеру в цепочке, если не сможет обработать его сам. Поскольку любой обработчик может возбудить исключение (в том числе и обработчик самого первого объекта
dispatcher
, если встретит сообщение
close_queue
), то деструктор снова необходимо снабдить аннотацией
noexcept(false)
(5).

Этот простенький каркас позволяет помещать в очередь сообщения любого типа, а затем на принимающем конце отбирать те из них, которые мы можем обработать. Кроме того, он позволяет передавать ссылку на очередь, чтобы в нее можно было добавлять новые сообщения, оставляя при этом прижимающий конец недоступным извне.

И чтобы закончить пример из главы 4, в листинге С.6 приведён код сообщений, в листингах С.7, С.8 и С.9 — различные конечные автоматы, а в листинге С.10 — управляющая программа.


Листинг С.6. Сообщения банкомата

struct withdraw {

 std::string account;

 unsigned amount;

 mutable messaging::sender atm_queue;


 withdraw(std::string const& account_,

  unsigned amount_, messaging::sender atm_queue_):

   account(account_), amount(amount_), atm_queue(atm_queue_) {}

};


struct withdraw_ok {};


struct withdraw_denied {};


struct cancel_withdrawal {

 std::string account;

 unsigned amount;


 cancel_withdrawal(std::string const& account_,

  unsigned amount_):

   account(account_), amount(amount_) {}

};


struct withdrawal_processed {

 std::string account;

 unsigned amount;


 withdrawal_processed(std::string const& account_,

  unsigned amount_):

   account(account_), amount(amount_) {}

};


struct card_inserted {

 std::string account;


 explicit card_inserted(std::string const& account_):

  account(account_) {}

};


struct digit_pressed {

 char digit;


 explicit digit_pressed(char digit_):

  digit(digit_) {}

};


struct clear_last_pressed {};


struct eject_card {};


struct withdraw_pressed {

 unsigned amount;


 explicit withdraw_pressed(unsigned amount_):

  amount(amount_) {}


};


struct cancel_pressed {};


struct issue_money {

 unsigned amount;

 issue_money(unsigned amount_):

  amount(amount_) {}

};


struct verify_pin {

 std::string account;

 std::string pin;

 mutable messaging::sender atm_queue;


 verify_pin(std::string const& account_, std::string const& pin_,

  messaging::sender atm_queue_):

   account(account_), pin(pin_), atm_queue(atm_queue_) {}

};


struct pin_verified {};


struct pin_incorrect {};


struct display_enter_pin {};


struct display_enter_card {};


struct display_insufficient_funds {};


struct display_withdrawal_cancelled {};


struct display_pin_incorrect_message {};


struct display_withdrawal_options (};


struct get_balance {

 std::string account;