Question

Classe de base MessageHandler a dérivé des classes. Ils aimeraient transmettre des messages les uns aux autres. Les messages peuvent être de différentes classes, mais peuvent être faits pour partager une classe de base. Comment chaque MessageHandler éviter un message reçu coulée en descente? Est-il possible de faire en quelque sorte quelque chose qui a pour effet de modèle-paramétrisant la fonction receiveMessage virtuelle sur MessageHandler?

Pour l'essentiel, je suis en train de remplacer le code suivant avec quelque chose qui ne fonctionne pas baissés, et je l'espère une chose compilation:

// ...
virtual void MessageHandler::receiveMessage(Message &msg) = 0;
// ...

// to receive a message
void DerivedMessageHandler::receiveMessage(Message& msg)
{
    switch (msg.MsgType()) // enum
    {
        case Message::MessageType::A:
            MessageA& = dynamic_cast<MessageA&>(msg);
            break;

        case Message::MessageType::B:
            MessageB& = dynamic_cast<MessageB&>(msg);
            break;
        default:
            // don't process unknown messages
            break;
    }
}

// to send a message
list<MessageHandler> mhList;
// populate list
for (MessageHandler& mh : mhList)
{
    mh.receiveMessage(msg);
}

Je sais que je ne peux pas faire cela, mais quelque chose comme

template <typename M>
void MessageHandler::receiveMessage(M& msg) {}

Et ont chacun de se spécialiser DerivedMessageHandler sur M? Quel serait un modèle de conception qui permet proprement chaque travail de gestionnaire sur leurs objets de message pris en charge?

Était-ce utile?

La solution

Ceci est assez facile à faire. Il y a généralement deux alternatives:

Boost.Variant

Au lieu de passer une classe dérivée, simplement énumérer les types possibles qu'un message pourrait être. Ces types ne doivent pas être dérivés d'un autre. Enveloppez ces types dans un boost :: variante :

typedef boost::variant<MessageData1, MessageData2, MessageData3, ...> MessageData;

Notez que cela signifie que les types de données de messages possibles doivent être dénombrable. Les méthodes de visite de Boost.Variant rendent facile de travailler avec des objets de ces types sans savoir exactement quel type il stocke.

Boost.Any

Il suffit de passer quoi que ce soit avec un boost::any:

void MessageHandler::receiveMessage(const boost::any &msg)
{
  const MessageType1 *pMsg = boost::any_cast<MessageType1>(&msg);
  if(!pMsg)
    //Cannot process
    return;

  //Process message.
}

boost::any est comme un void* de type sécurisé. Il se souvient du type exact qui a été mis en elle, et toute tentative de le jeter à quelque chose autre que ce qui est stocké dans elle échouera. boost::any peut stocker quoi que ce soit, d'où le nom.

Il a également la sémantique de valeur, il peut donc être copié comme son contenu.

Autres conseils

Si je comprends bien votre question, vous avez juste besoin d'héritage droit avec une fonction virtuelle. Quelque chose comme:

class BaseMessage 
{
    public:
    virtual ~BaseMessage() {}

    virtual void processMsg() = 0;
};

class MessageA : public BaseMessage
{
    public:
    MessageA() {}
    virtual ~MessageA() {}    
    virtual void processMsg()
    {
        // ... do something for MessageA ...
    }
};

class MessageB : public BaseMessage
{
    public:
    MessageB() {}
    virtual ~MessageB() {}    
    virtual void processMsg()
    {
        // ... do something for MessageB ...
    }
};

Si vous gérez le message, il suffit d'appeler la fonction processMsg () sur le message que vous recevez pour traiter chaque message comme spécifié dans chaque classe.

std::auto_ptr<BaseMessage> m(mailbox.getMessage()); // Returns whatever msg is sent to your handler
m->processMsg();

Vous pouvez utiliser un modèle de visiteur.

mais un visiteur doit connaître chacun des sous-types et définir une action, donc aucune action par défaut, AFAIK

class Visitor;
class BaseMsg {
//..
public:
virtual void acceptVisitor(Visitor * v) = 0;
};

class Msg1;
class Msg2;
class Visitor {     
// You can put here pure virtuals for sure every visitor will implement them
public:
virtual void action (Msg1 * msg) = 0;
virtual void action (Msg2 * msg) = 0;
};

class Msg1: public BaseMsg {
//..
public:
void acceptVisitor(Visitor * v){v->action(this);}
};

class Msg2: public BaseMsg  {
//..
public:
void acceptVisitor(Visitor * v){v->action(this);}
};



class Visitor1 : public Visitor {
// ...
public:
void action (Msg1 * msg) {/*...*/ cout << "I like the message!\n";}
void action (Msg2 * msg) {/*...*/ cout << "I hate the message!\n";}
// more messages and actions for them
};

class Visitor2 : public Visitor{
// ...
public:
void action (Msg1 * msg) {/*...*/ cout << "Just fine\n";}
void action (Msg2 * msg) {/*...*/ cout << "Sorry, I'm busy\n";}
// more messages and actions for them
};

int main() {

BaseMsg * a = new Msg1;
BaseMsg * b = new Msg2;

Visitor * act = new Visitor1;
Visitor * lazy = new Visitor2;
// ............
// somewhere in a deep deep forest of your code

a->acceptVisitor(act);
b->acceptVisitor(act);

// somewhere else

a->acceptVisitor(lazy);
b->acceptVisitor(lazy);

delete act;
delete lazy;
delete a;
delete b;
return 0;
}  

Sortie:

  • Je aime le message!
  • Je déteste le message!
  • Juste fin
  • Désolé, je suis occupé
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top