STL Контейнер для хранения нескольких типов значений?

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

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

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

3

Решение

Есть много способов сделать это. Вот пример с 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;
}
});
}
1

Другие решения

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

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);

любой хорошая книга должен быть в состоянии дать вам больше информации.

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 примет набор Listeners, независимо от того, что является фактическим производным типом, и общим Message, С другой стороны, сообщение будет продвигаться к правильному производному типу и передавать ссылку на данного слушателя.

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

2
По вопросам рекламы [email protected]