2017-01-15 2 views
3

J'ai une structure Message que j'utilise avec un bus de message, et je voudrais envoyer des données avec des messages. Le problème est que les données varient en type; peut-être pour un message je veux juste envoyer un int, mais pour un autre je vais vouloir envoyer plusieurs ints, une chaîne, peut-être même un pointeur vers un objet par exemple. Je pouvais faire quelque chose comme ceci:STL Container pour stocker plusieurs types de valeurs?

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

Mais non seulement est-ce laid et sale, et les déchets probablement espace, mais cela ne compte pas si je veux passer un type de données relativement exotiques comme un pointeur à une instance d'une classe par exemple. Que devrais-je utiliser pour cela?

+1

Utilisation de l'héritage et des pointeurs vers la classe de base et des fonctions virtuelles? –

+1

Ma première estimation serait le polymorphisme si possible dans votre situation. – DeiDei

+0

@DeiDei Je suis un peu confus, d'après ma compréhension du polymorphisme a à voir avec l'héritage de classe/structure, comment le polymorphisme aiderait-il ici? – Accumulator

Répondre

1

Il y a plusieurs façons de le faire. Voici un exemple avec 17 de std::variant C++:

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 est une liste d'éléments qui sont soit int ou std::string.

Vous pouvez également utiliser un visiteur statique:

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

Je soupçonne dans son cas qu'il ne peut pas dire quel est le type à sortir de la variante dans la classe qui gère le message. – skypjack

+0

Cela ne fonctionne pas dans Visual Studio 2015 (je ne peux pas inclure ''), est-ce que je fais quelque chose de mal? Est-ce que ça ne supporte pas C++ 17? – Accumulator

+0

Il existe plusieurs façons de savoir de quel type il s'agit. La meilleure méthode est la méthode visit, get_if et get. La documentation est assez complète. Allez voir le ['std :: variant'] (http: // en.cppreference.com/w/cpp/utility/variant) page sur cppreference –

2

Un exemple simple en utilisant l'héritage et le polymorphisme:

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

Tout good book devrait être en mesure de vous donner plus d'informations.

2

Vous pouvez baser votre solution sur le modèle de visiteur.
En tant que minime, exemple de travail:

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

Mettez de côté le fait que la mise en œuvre du Bus est trivial, notez que visit fonctions membres dans Listener peuvent être virtuels. De cette façon, tout votre écouteur peut être dérivé de cette classe et remplacer les méthodes souhaitées.
Le Bus acceptera un ensemble de Listener s, quel que soit le type dérivé réel, et un Message générique. De l'autre côté, le message va se promouvoir sur le type dérivé droit et transmettre une référence à l'écouteur donné.

La technique derrière le modèle de visiteur est également connue comme double expédition, si vous voulez l'explorer plus loin.