【发布时间】:2020-05-05 10:18:49
【问题描述】:
我想为消息泵定义一个接口,该接口能够发送和接收用户指定类型的消息,以进行通信,例如在生产者和消费者之间。
目前我是这样做的:
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(); }} {}
};
这里的问题是静态和动态多态性不能很好地混合,并且在不知道底层Message 的类型的情况下不可能注入message_pump 的实现。
我做这个 active 类的原因是我想在各种 RTOS 中重用它,它们提供不同的 queue 和 thread 类实现,并且仍然能够在本地测试它计算机。 (我在这个清单中添加了std::thread 只是为了简化,因为如何使thread 实现可注入是另一个主题)。
问题是定义接口message_pump,以便能够轻松地将实现注入active 类的首选方式是什么——最OOP 和“应该做的”方式?
我有几个解决方案:
在
message_pump内定义struct message {}并使MessageProxy成为继承自message_pump::message的结构。然后从receive()接口函数返回std::unique_ptr<message>。在
MessagePump中使用std::any而不是Message。使用静态多态,通过模板参数注入
message_pump实现。那么message_pump接口就不需要显式定义了,如果实现者没有特定的函数,我们会得到编译器错误。使用 C++20 概念? (我也想知道如何用 C++17 解决)。
混合 Ad.4 和 Ad.5:使用模板参数,但明确定义它应实现的接口。
其他?
【问题讨论】:
-
一个问题,当
std::variant持有的类型之一是private struct quite_message时,此类用户如何能够提供MessageProxy? -
没错,这就是我要的:)
-
我可以想象
message_pump<int>可能对某些 RTOS 有专长,所以我想知道 OOP 是否是最好的解决方案。但这对于quit_message来说已经很棘手了。也许是catch(quit_message)? -
好的,我想你可以在这里使用
Type Erasure。 -
好的,我更新了我的答案,展示了如何使用
type erasure和opaque pointers来隐藏消息和消息泵的实现细节。
标签: c++ interface polymorphism c++17 c++-concepts