c++ - 根据运行时值调用不同的模板函数特化

标签 c++ templates c++11

这与 previous question 有关因为它是同一个系统的一部分,但这是一个不同的问题。

我正在开发一个内部消息传递系统,该系统旨在向消费者发送消息 ( struct s)。

当项目要使用消息系统时,它会定义一组消息(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默认方法,以及 ProcessMessage 中的 switch 语句必须更新功能。

我想避免手动修改基类。具体来说,我想使用存储在 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);
}

这当然不行,因为模板不是那样工作的。

我想到的事情:

  1. expected_type 生成 switch 语句结构。我不知道该怎么做。
  2. 维护某种函数指针映射,并调用所需的指针。问题是我不知道如何在不重复 expected_type 中的数据的情况下初始化 map 。 ,我不想这样做。
  3. 定义 expected_type使用宏,然后玩预处理器游戏将该数据也按摩到 switch 语句中。这可能是可行的,但我尽量避免使用宏。

因此,总而言之,我希望能够根据运行时值调用不同的模板特化。这对我来说似乎是矛盾的,但我希望有人能给我指出一个有用的方向。即使这告诉我这不是一个好主意。

我可以改变expected_type如果需要,只要它不破坏我的 Send方法(参见我的 other question )。

最佳答案

您对 expected_typeReceive 模板的想法是正确的;只需一步即可完成所有工作。

首先,我们需要提供一些方法来枚举 MessageType:

enum class MessageType {
    _FIRST = 0,
    TypeA = _FIRST,
    TypeB,
    TypeM = 100,
    _LAST
};

然后我们可以在编译时枚举 MessageType 并生成调度函数(使用 SFINAE 跳过 expected_types 中未定义的值):

// this overload works when expected_types has a specialization for this value of E
template<MessageType E> void processMessageHelper(MessageType msgType, void * data, typename expected_type<E>::type*) {
    if (msgType == E) Receive<E>(*(expected_type<E>::type*)data);
    else processMessageHelper<(MessageType)((int)E + 1)>(msgType, data, nullptr);
}
template<MessageType E> void processMessageHelper(MessageType msgType, void * data, bool) {
    processMessageHelper<(MessageType)((int)E + 1)>(msgType, data, nullptr);
}
template<> void processMessageHelper<MessageType::_LAST>(MessageType msgType, void * data, bool) {
    std::cout << "Unexpected message type\n";
}

void ProcessMessage(MessageType msgType, void * data) {
    processMessageHelper<MessageType::_FIRST>(msgType, data, nullptr);
}

关于c++ - 根据运行时值调用不同的模板函数特化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39842853/

相关文章:

c++ - std::map with key as templated structs with enum member

c++ - 根据模板参数选择成员类型?

c++ - 整数值的缩放(来自 float )

c++ - tensorflow C++ 批量推理

c++ - 静态链接库

c++ - (Qt) 如何在 View 中实现 editorOpened(index) 和 editorClosed(index) 信号

c++ - 为什么模板不能在 extern "C" block 内?

c++ - 父类使用默认构造函数;子类的析构函数被意外调用

C++11 GCC 4.6.2 std::move

c++ - 比较和交换弱原子变量