Я хотел бы определить интерфейс для насоса сообщений, который может отправлять и получать сообщения с типом, указанным пользователем, для связи, например, между производителем и потребителем.
В настоящее время я сделал это вот так:
template <typename Message>
struct message_pump
{
virtual void send(Message &&) = 0;
//! Blocks if no message is available.
virtual Message receive() = 0;
};
Тогда я хотел бы использовать этот message_pump
интерфейс как член класса active
(шаблон из Herb Sutters '- «Предпочитать использование активных объектов вместо голых потоков» ):
template <typename Message>
class active
{
private:
struct quit_message{};
using MessageProxy = typename std::variant<Message, quit_message>;
std::unique_ptr<message_pump<MessageProxy>> message_pump_impl;
std::function<void(Message&&)> message_handler;
std::thread worker_thread;
void thread_code() {
while(true)
{
auto m{message_pump_impl->receive()};
if(std::holds_alternative<quit_message>(m))
break;
message_handler(std::move(std::get<Message>(m)));
}
}
public:
active(std::unique_ptr<message_pump<MessageProxy>> message_pump_impl,
std::function<void(Message&&)> message_handler) :
message_pump_impl{std::move(message_pump_impl)},
message_handler{message_handler},
worker_thread{[this](){ this->thread_code(); }} {}
};
Проблема здесь в том, что полиморфизм stati c и Dynami c плохо сочетается, и невозможно внедрить реализацию message_pump
, не зная тип базовый Message
.
Причина, по которой я делаю этот класс active
, заключается в том, что я хотел бы повторно использовать его в различных ОСРВ, которые предоставляют разные реализации классов queue
и thread
, и при этом иметь возможность тестировать его. на локальном компьютере. (Я поместил в этот листинг std::thread
только для упрощения, потому что как сделать реализацию thread
инъектируемой - это другой topi c).
Вопрос в том, какой способ предпочтительнее - самый OOP и способ «как это должно быть сделано» - определить интерфейс message_pump
, чтобы иметь возможность легко внедрить реализацию в класс active
?
У меня есть несколько решений:
Определите struct message {}
внутри message_pump
и сделайте MessageProxy
структурой, наследуемой от message_pump::message
. Затем верните std::unique_ptr<message>
из функции интерфейса receive()
.
Используйте std::any
вместо Message
внутри MessagePump
.
Использовать статический c полиморфизм и внедрять message_pump
реализацию через параметр шаблона. Тогда интерфейс message_pump
не нужно определять явно, и мы получим ошибки компилятора, если разработчик не имеет специальной функции c.
Используйте C ++ 20 концепции? (Я также хотел бы знать, как решить эту проблему с помощью C ++ 17).
Смешайте Ad.4 и Ad.5: используйте параметр шаблона, но явно укажите, какой интерфейс он должен реализовать .
Другое?