Это связано с 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
структуры. Я не знаю, как это сделать. - Поддержание какой-либо карты указателей функций и вызов нужного. Проблема в том, что я не знаю, как инициализировать карту, не повторяя данные от
expected_type
, что я не хочу делать. - Определение
expected_type
с использованием макроса, а затем воспроизведение препроцессорных игр для массажа этих данных в оператор switch. Это может быть жизнеспособным, но я стараюсь избегать макросов, если это возможно.
Таким образом, я хотел бы иметь возможность называть другую специализацию шаблона на основе значения времени выполнения. Это кажется мне противоречием, но я надеюсь, что кто-то может указать мне в полезном направлении. Даже если это сообщает мне, что это не очень хорошая идея.
Я могу изменить expected_type
при необходимости, если он не сломает мой метод Send
(см. Мой other question).
Это может сработать, но MessageType не последовательный. Имеются пробелы, позволяющие определять будущие типы сообщений без нарушения существующих индексов. – endorph
Отредактированный пример, чтобы справиться с этим. –