这与previous question相关,因为它是同一系统的一部分,但它是一个不同的问题。
我正在开发一个内部消息传递系统,该系统旨在向消费者发送消息(struct
)。
当项目想要使用消息传递系统时,它将定义一组消息(enum class
),数据类型(struct
)以及这些实体之间的关系:
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; };
请注意,不同类型的消息可能使用相同的数据类型。
我之前的问题讨论了发送这些消息的代码。有一个模板化方法可以发送任何消息,并使用上面的模板定义维护类型安全。它工作得非常好。
我的问题是关于消息接收器类。有一个基类,它实现了这样的方法:
ReceiveMessageTypeA(const Foo & data) { /* Some default action */ };
ReceiveMessageTypeB(const Bar & data) { /* Some default action */ };
ReceiveMessageTypeM(const Foo & data) { /* Some default action */ };
然后实现单个消息处理功能,如下所示:
bool ProcessMessage(MessageType msgType, void * data) {
switch (msgType) {
case TypeA:
ReceiveMessageTypeA(data);
break;
case TypeB:
ReceiveMessageTypeB(data);
break;
// Repeat for all supported message types
default:
// error handling
break;
}
}
当需要消息接收器时,将扩展此基类,并实现所需的ReceiveMessageTypeX
方法。如果该特定接收者不关心消息类型,则相应的函数未被实现,而是使用基类的默认值。
旁注:忽略我传递void *
而不是特定类型的事实。中间还有一些代码来处理所有这些,但它不是相关细节。
该方法的问题是添加了新的消息类型。除了必须定义enum
,struct
和expected_type<>
特化之外,还必须修改基类以添加新的ReceiveMessageTypeX
默认方法和switch语句必须更新ProcessMessage
函数。
我想避免手动修改基类。具体来说,我希望使用expected_type
中存储的信息来完成繁重的工作,并避免重复。
这是我尝试过的解决方案:
在基类中,定义一个方法:
template <MessageType msgType>
bool Receive(expected_type<msgType>::type data) {
// Default implementation. Print "Message not supported", or something
}
然后,子类可以实现他们关心的特化:
template<> Receive<MessageType::TypeA>(const Foo & data) { /* Some processing */ }
// Don't care about TypeB
template<> Receive<MessageType::TypeM>(const Foo & data) { /* Some processing */ }
我认为这解决了部分问题;我不需要在基类中定义新方法。
但我无法弄清楚如何摆脱switch语句。我希望能够做到这一点:
bool ProcessMessage(MessageType msgType, void * data) {
Receive<msgType>(data);
}
当然,这不会做,因为模板不会像那样工作。
我想到的事情:
expected_type
结构生成switch语句。我不知道该怎么做。expected_type
的数据,这是我不想做的。expected_type
,然后播放预处理器游戏以将该数据按到switch语句中。这可能是可行的,但我尽可能避免使用宏。因此,总而言之,我希望能够基于运行时值调用不同的模板专门化。这似乎与我相矛盾,但我希望有人可以指出我有用的方向。即使这告诉我这不是一个好主意。
如果需要,我可以更改expected_type
,只要它不会破坏我的Send
方法(请参阅我的other question)。
答案 0 :(得分:1)
您对collection := session.DB("your_db_name").C("main")
err = collection.Remove(bson.M{"id":1})
和expected_type
模板有正确的想法;只剩下一步才能完成所有工作。
首先,我们需要给我们一些枚举MessageType的方法:
Receive
然后我们可以在编译时枚举MessageType并生成调度函数(使用SFINAE跳过未在enum class MessageType {
_FIRST = 0,
TypeA = _FIRST,
TypeB,
TypeM = 100,
_LAST
};
中定义的值):
expected_types
答案 1 :(得分:0)
您的标题是:&#34;根据运行时值调用不同的模板函数特化&#34;
只能通过某种手动switch
声明或virtual
函数来完成。
一方面,它看起来就像你正在进行面向对象的编程,但你还没有任何virtual
方法。如果你发现你到处都在写伪对象,但你没有任何virtual
函数,那么这意味着你没有做OOP。这不是一件坏事。如果你过度使用OOP,那么你可能无法理解它有用的特殊情况,因此它只会引起更多的混淆。
简化您的代码,不要被OOP分心
您希望消息类型对象具有一些魔法&#39;与之关联,它MessageType
控制它的分派方式。这意味着您需要一个虚拟功能。
struct message {
virtual void Receive() = 0;
}
struct message_type_A : public message {
virtual void Receive() {
....
}
}
这允许您在适当的情况下将这些对象作为message&
传递,并致电msg.process_me()