Есть ли стандартный способ избавиться от блока switch/case в цикле чтения?
т.е.
enum msg_type
{
message_type_1,
//msg types
}
struct header
{
msg_type _msg_type;
uint64_t _length;
}
struct message1
{
header _header;
//fields
}
struct message2
{
header _header;
//fields
}
//socket read loop
void read(//blah)
{
//suppose we have full message here
char* buffer; //the buffer that holds data
header* h = (header*)buffer;
msg_type type = h->_msg_type;
switch(type)
{
case msg_type_1:
message1* msg1 = (message1*)buffer;
//Call handler function for this type
//rest
}
}
это означает, что я должен наследовать из базового класса контейнера обработчика, который имеет вид:
class handler_container_base
{
public:
virtual void handle(message1* msg){}
virtual void handle(message2* msg){}
//etc
}
и передать объект этого типа туда, где может появиться цикл сообщений, и попросить его называть их обратно.
Одна из проблем заключается в том, что даже когда я хочу реализовать и зарегистрировать только один обработчик для одного типа, я должен наследовать этот класс. Другое, это просто выглядит уродливо.
Мне было интересно, существуют ли существующие библиотеки, которые справляются с этой проблемой (должны быть бесплатными). Или нет лучшего способа сделать это, а не как это?
Другими подходами, которые избегают наследования, являются:
Для замкнутого набора типов:
Используйте вариант:
variant<message1_t, message2_t> my_message;
С посетителем вы можете сделать все остальное. Я рекомендую boost.variant.
Вы также можете использовать boost :: any, для открытого набора типов и копировать сообщения во время выполнения. Однако в какой-то момент вам придется отбрасывать исходный тип.
Другое решение идет по строкам Poco.DynamicAny, которое будет пытаться преобразовать в тип слева в задании, подобно динамическому языку. Но вам нужно регистрировать конвертеры самостоятельно для ваших типов.