c++ - 与任何类型的函数的接口
问题描述
我想为消息泵定义一个接口,该接口能够发送和接收具有用户指定类型的消息,以便在生产者和消费者之间进行通信。
目前我已经这样做了:
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 的模式 - “Prefer Using Active Objects 而不是 Naked Threads”):
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_pump
这里的问题是静态和动态多态性不能很好地混合,并且在不知道底层的类型的情况下不可能注入实现Message
。
我做这active
门课的原因是我想在各种 RTOS 中重用它,它们提供不同queue
的thread
类实现,并且仍然能够在本地计算机上测试它。(我在这个清单中std::thread
只是为了简化,因为如何使thread
实现可注入是一个不同的主题)。
问题是定义接口的首选方式 - 最 OOP 和“应该做的”方式 - 以便message_pump
能够轻松地将实现注入active
类?
我有几个解决方案:
struct message {}
在内部定义message_pump
并创建MessageProxy
一个从message_pump::message
. 然后std::unique_ptr<message>
从receive()
接口函数返回。使用
std::any
而不是Message
里面MessagePump
。使用静态多态性并
message_pump
通过模板参数注入实现。然后message_pump
不需要显式定义接口,如果实现者没有特定的功能,我们将得到编译器错误。使用 C++20 概念?(我也想知道如何用 C++17 解决它)。
混合 Ad.4 和 Ad.5:使用模板参数,但明确定义它应实现的接口。
其他?
解决方案
使用动态多态性。这是可行的,但是,它需要
active
类型公开它在内部使用什么类型来保存消息,以便可以构造正确类型的队列。struct message {}
在内部定义message_pump
并创建MessageProxy
一个从message_pump::message
. 如果我们使用动态多态性,这不会给我们带来任何好处。但是,如果使用静态多态性,用户消息可以派生自active::message
哪个可以派生自message_pump::message
,这样就可以在没有“emplace”方法的情况下添加不可移动的消息。必须从中派生消息active::message
是一个不应该被忽视的缺点,特别是如果消息可以重用现有类型,例如int
其他类型。即使对于通常不需要这样做的队列,这也需要动态分配消息。使用
std::any
而不是Message
里面MessagePump
。如果使用动态多态性,它解决了active
必须公开它在内部使用的消息类型并允许active
不是模板的问题。但是会丢失静态类型检查并具有运行时开销。我不建议这样做,因为静态类型检查可以使重构更不容易出错。使用静态多态性并
message_pump
通过模板参数注入实现。如果message_pump
是模板模板参数,active
则不必公开它在内部使用的消息类型。这类似于标准库采用的方法。但是,错误消息可能难以理解。使用 C++20 概念?(我也想知道如何用 C++17 解决它)。概念可以帮助记录
message_pump
需要哪些方法,并可能给出更好的错误。我不会用 c++17 尝试这样的事情,因为 c++17 版本往往难以阅读并且在这种情况下几乎没有什么好处。使用模板参数,但明确定义它应该实现的接口。基本上是什么概念是要实现的。
其他?实现一个可在多个平台上工作的队列,可能使用
#ifdef
并已使用该队列或该队列的active
默认模板参数。active
message_pump
推荐阅读
- docker - 使用 Docker Toolbox for Windows 在 uses 文件夹外挂载驱动器
- oop - 在 Lua 中摆脱一个类的实例
- java - 在二维数组的行中创建一个最大值数组
- python - 当我运行测试时,django 如何重新定义 DJANGO_SETTINGS_MODULE?
- c# - 对象类型和泛型 T 类型的函数重载所需的优先级说明
- java - Spring Bean 验证给出 ConstraintDeclarationException
- macos - 当我运行我的 cpp 文件时,由于未捕获的异常而终止应用程序
- c - 我可以将 int 传递给 C 中的 void 函数吗?
- python - 如何使用一个顶级列对多索引熊猫数据框进行排序?
- java - 服务器端没有响应(基本的java客户端-服务器)