В листинге С.1 показан код очереди сообщений. Сообщения хранятся в списке и представлены указателями на базовый класс. Сообщения конкретного типа обрабатываются шаблонным классом, производным от этого базового класса. В момент помещения сообщения в очередь конструируется подходящий экземпляр обертывающего класса и сохраняется указатель на него; операция извлечения возвращает именно этот указатель. Поскольку в классе message_base
wrapped_message
, прежде чем сможет получить хранящееся сообщение.Листинг С.1.
Простая очередь сообщений#include
#include
#include
#include
namespace messaging
{ │
struct message_base {←┘
virtual ~message_base() {}
};
template
struct wrapped_message:←┘
message_base {
Msg contents;
explicit wrapped_message(Msg const& contents_):
contents(contents_) {}
};
│
class queue←┘
{ │
std::mutex m; │
std::condition_variable с; │
std::queue
public:
template
void push(T const& msg) │
{ │
std::lock_guard
q.push( ←┘
std::make_shared
с.notify_all();
}
std::shared_ptr
{ │
std::unique_lock
c.wait(lk, [&]{ return !q.empty(); }); ←┘
auto res = q.front();
q.pop();
return res;
}
};
}
Отправкой сообщений занимается объект класса sender
sender
копируется только указатель на очередь, а не сама очередь.Листинг С.2.
Классsender
namespace messaging {
class sender {│
queue* q; ←┘
public: │
sender() :←┘
q(nullptr) {}
│
explicit sender(queue* q_):←┘
q(q_) {}
template
void send(Message const& msg) {
if (q){ │
q->push(msg);←┘
}
}
};
}
Получение сообщений несколько сложнее. Мы не только должны дождаться появления сообщения в очереди, но еще и проверить, совпадает ли его тип с одним из известных нам типов, и вызвать соответствующий обработчик. Эта процедура начинается в классе receiver
Листинг С.3.
Классreceiver
namespace messaging {
class receiver {
queue q; ←
public: │
operator sender() {←┘
return sender(&q);
}
│
dispatcher wait() {←┘
return dispatcher(&q);
}
};
}