2016-09-29 3 views
3

Я работаю над улучшением собственной библиотеки сообщений, предназначенной для отправки сообщений внутри наших приложений и для внешних потребителей. Сообщение состоит из 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. Несоответствие будет обнаружено во время выполнения, но я хочу сделать это ошибкой времени компиляции.

Я не уверен, как достичь этого. Я рассмотрел:

  1. Декларирование все SendMessageX() методы, и использовать их для вызова Send<MessageX>(). Это уменьшает дублирование, но мне все равно приходится создавать новый метод каждый раз, когда будет определено сообщение.
  2. Попытка использования static_assert, чтобы поймать несоответствие. Я не уверен, как нарисовать MessageType s по желанию struct.
  3. Я ложному дерево
+0

Знаете ли вы, что сообщения перечислены во время компиляции? – Barry

+0

Да, ассоциация известна во время компиляции – endorph

+0

Не ассоциация, значения, которые вы вызываете 'Send()' with. – Barry

ответ

2

Если вы можете поднять перечисление на время компиляции постоянная, то это возможно:

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&) { 
    ... 
} 
+0

Простите невежество, но что вы подразумеваете под «поднимите enum до постоянной времени компиляции»? Это означает, что перечисление может быть определено во время выполнения. Как это будет работать? –

+0

@Michael Значение enum может быть определено во время выполнения. Нам это нужно во время компиляции. – Barry

+0

Вы должны повернуть эллипсис на конечном сегменте кода в комментарий, чтобы сохранить согласованность с предыдущим сегментом кода. – user2296177

-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 */ } 
Смежные вопросы