我正在努力改进内部消息传递库,该库旨在在我们的应用程序内部向外部消费者发送消息。一条消息由MessageType
(enum class
)和一些数据(struct
)组成。每个MessageType::
对应于特定的数据类型(例如MessageType::TypeA
将始终包含Foo
)。但是,多种消息类型可以使用相同的结构(例如MessageType::TypeM
也可以使用Foo
)。
我们有一个可以发送消息的类。我们先前对消息发送者类的实现为每种类型定义了一个方法:
SendMessageTypeA(Foo data)
SendMessageTypeB(Bar data)
SendMessageTypeM(Foo data)
当有很多消息时,这可能导致很多代码重复(方法主体本质上是相同的,不同的参数类型除外)。
我实现了一种新方法:
template<typename structType>
void Send(MessageType msgType, const structType & messageData)
根据提供的适当模板参数,此单一方法可以发送任何消息。注意,
MessageType
在编译时始终是已知的。问题在于,此新方法不会强制
MessageType
和struct
之间的关系。例如,即使Send<Foo>(MessageType::TypeB, data)
应该包含MessageType::TypeB
,Bar
也会编译。不匹配将在运行时检测到,但是我想使其成为编译时错误。我不确定如何实现这一目标。我考虑过:
SendMessageX()
方法,并使用它们调用Send<MessageX>()
。这确实减少了重复,但是每次定义一条消息时,我仍然必须创建一个新方法。 static_assert
捕获不匹配。我不确定如何将MessageType
映射到所需的struct
。 最佳答案
如果可以将枚举值提升为编译时间常数,则可能:
template <MessageType E, class Data>
void Send(Data const& ) { ... }
我们可以创建一个类模板,专门针对每个枚举,并提供枚举期望的内容:
template <MessageType E> struct expected_type;
template <> struct expected_type<MessageType::TypeA> { using type = Foo; };
template <> struct expected_type<MessageType::TypeB> { using type = Bar; };
template <> struct expected_type<MessageType::TypeM> { using type = Foo; };
template <MessageType E>
using expected_type_t = typename expected_type<E>::type;
然后,我们可以使用它来编写静态断言:
template <MessageType E, class Data>
void Send(Data const& ) {
static_assert(std::is_same<Data, expected_type_t<E>>{}, "!");
// ...
}
或者,可以使用该类模板直接设置
Data
类型:template <MessageType E>
void Send(expected_type_t<E> const& ) {
...
}