2017-01-15 14 views
3

У меня есть структура Message, которую я использую с шиной сообщений, и я бы хотел отправить данные с сообщениями. Проблема в том, что данные будут различаться по типу; возможно, для одного сообщения я просто хочу отправить один int, но для другого я хочу отправить несколько ints, строку, возможно, даже указатель на объект, например. Я мог сделать что-то вроде этого:Контейнер STL для хранения нескольких типов значений?

struct Message { 
    std::map<int, int> intPayload; 
    std::map<int, std::string> strPayload; 
    short id; 
}; 

Но это не только некрасиво и нечистым, и, вероятно, тратит впустую пространство, но это не объясняет, если я хочу передать относительно экзотический тип данных как указатель например, к экземпляру класса. Что я должен использовать для этого?

+1

Использование наследования и указатели на базовый класс и виртуальные функции? –

+1

Мое первое предположение было бы полиморфизмом, если это возможно в вашей ситуации. – DeiDei

+0

@DeiDei Я немного смущен, из моего понимания полиморфизм связан с наследованием класса/структуры, как здесь будет помогать полиморфизм? – Accumulator

ответ

1

Существует много способов сделать это. Вот пример с C++ 17-х std::variant:

std::vector<std::variant<int, std::string>> vec1; 

vec1.emplace_back(1); 
vec1.emplace_back("hello"s); 

doSomethingWithInt(std::get<int>(vec1[0])); 
doSomethingWithString(std::get<std::string>(vec1[1])); 

vec1 список элементов, которые являются либо int или std::string.

Вы также можете использовать статический посетитель:

std::vector<std::variant<int, std::string>> vec2; 

// ... 

for(auto&& variant : vec1) { 
    variant.visit([](auto value){ 
     using t = decltype(value); 

     if constexpr (std::is_same_v<t, int>) { 
      std::cout << "value is a int!" << std::endl; 
     } else if constexpr (std::is_same_v<t, std::string>) { 
      std::cout << "value is a string!" << std::endl; 
     } 
    }); 
} 
+0

Я подозреваю, что в его случае он не может сказать, какой тип выбирается из варианта внутри класса, который обрабатывает сообщение. – skypjack

+0

Это не работает в Visual Studio 2015 (я не могу включить ''), я делаю что-то неправильно? Это просто не поддерживает C++ 17? – Accumulator

+0

Существует много способов узнать, какой именно тип. Лучший метод - это метод посещения метода get_if и получения. Документация достаточно полная. перейдите к ['std :: variant'] (http: // ru.cppreference.com/w/cpp/utility/variant) page на cppreference –

2

Простой пример использования наследования и полиморфизма:

struct MessageBase 
{ 
    // The function to send *this* message to the receiver 
    virtual void send(ReceiverClass*) = 0; 
}; 

struct MessageInt : MessageBase 
{ 
    int payload; 

    void send(ReceiverClass* receiver) 
    { 
     // Code to send this message type to the receiver... 
    } 
}; 

struct MessageString : MessageBase 
{ 
    std::string payload; 

    void send(ReceiverClass* receiver) 
    { 
     // Code to send this message type to the receiver... 
    } 
}; 

// ... 

// Vector to store the messages 
std::vector<MessageBase*> messages; 

// Add a couple of messages 
messages.push_back(new MessageInt{123}); 
messages.push_back(new MessageString{"Foobar"}); 

// Send the message to some receiver 
for (auto const* message : messages) 
    message->send(some_reciver_object); 

Любой good book должен быть в состоянии дать вам больше информации.

2

Вы можете разместить свое решение на шаблоне посетителя.
Как минимальный, рабочий пример:

struct Listener; 

struct Message { 
    virtual void accept(Listener &) = 0; 
}; 

struct SimpleMessage: Message { 
    void accept(Listener &) override; 
    int i; 
}; 

struct ComplexMessage: Message { 
    void accept(Listener &) override; 
    int i; 
    char c; 
    double d; 
}; 

struct Listener { 
    void visit(SimpleMessage &) {} 
    void visit(ComplexMessage &) {} 
    void listen(Message &m) { m.accept(*this); } 
}; 

void SimpleMessage::accept(Listener &l) { l.visit(*this); } 
void ComplexMessage::accept(Listener &l) { l.visit(*this); } 

struct Bus { 
    Bus(Listener *l): l{l} {} 
    void publish(Message &m) { l->listen(m); } 
private: 
    Listener *l; 
}; 

int main() { 
    Listener l; 
    Bus b{&l}; 

    SimpleMessage sm; 
    ComplexMessage cm; 

    b.publish(sm); 
    b.publish(cm); 
} 

Отложите тот факт, что реализация для Bus тривиальна, обратите внимание, что visit функция-членов в Listener может быть виртуальной.
Таким образом, весь ваш слушатель может быть получен из этого класса и переопределить нужные методы.
Bus будет принимать набор Listener s, независимо от того, что является фактическим производным типом, и общий Message. С другой стороны, сообщение будет продвигаться к правильному производному типу и передавать ссылку на данного слушателя.

Методика, лежащая за шаблоном посетителя, также известна как с двойной отправкой, если вы хотите изучить ее дальше.