在模板函数参数之间强制实现所需的关系

时间:2016-09-29 03:01:49

标签: c++ templates c++11

我正致力于改进内部消息库,旨在在我们的应用程序内部发送消息,以及外部消费者。邮件由SELECT id_customer FROM customer WHERE id_customer NOT IN (1,2,3,4,5,6,7,8,9) GROUP BY id_customer 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) 始终在编译时已知。

问题是这种新方法不会强制MessageTypeMessageType之间的关系。例如,struct将编译,即使Send<Foo>(MessageType::TypeB, data)应包含MessageType::TypeB。将在运行时检测到不匹配,但我想使其成为编译时错误。

我不知道如何实现这一目标。我考虑过了:

  1. 声明所有Bar方法,并使用它们来调用SendMessageX()。这确实减少了重复,但每次定义消息时我仍然需要创建一个新方法。
  2. 尝试使用Send<MessageX>()来捕捉不匹配。我不确定如何将static_assert映射到所需的MessageType
  3. 我正在咆哮错误的树

2 个答案:

答案 0 :(得分:2)

如果你可以将枚举提升到编译时常数,那么就可以:

file_out = open('text_mining.xml', 'a') # before
file_out = open('text_mining.xml', 'w') # after

我们可以创建一个类模板,专门针对枚举所期望的每个枚举:

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

答案 1 :(得分:-1)

这是另一个版本,我认为它更清晰,可以在消息和数据之间匹配多种类型。

template<typename MT, typename DT>
struct CompitableCheck;
// only declare type pairs you want. A simple macro wrapping it is better
template<>
struct CompitableCheck<MsgTypeA, Bar> { static const bool value = true; };
template<>
struct CompitableCheck<MsgTypeA, Foo> { static const bool value = true; };
template<>   // allow one-to-many map, TypeA -> Foo && TypeA -> Bar.
struct CompitableCheck<MsgTypeA, Bar> { static const bool value = true; };

// here goes the final function
template<typename MT, typename DT, 
         typename = typename std::enable_if<CompitableCheck<MT, DT>::value>>::type
void Send(MT msgType, const DT & messageData) { /* send it */ }