2017-04-21 6 views
1

J'essaie d'écrire un système de messagerie avec des modèles, le modèle de visiteur et avec l'aide de CRTP. Je comprends ces concepts mais je suis dans une situation où je dois retrouver un type "perdu". J'ai une classe Base et je veux trouver un Derived<T>. C'est "deux" types à déduire [Derived pourrait être n'importe quoi, T pourrait être quelque chose] (même si c'est considéré comme un type).Récupérer le type d'un modèle de classe dans un modèle de visiteur

J'ai essayé de faire usage d'un deuxième modèle de visiteur, qui semble lourd et fou mais je n'ai trouvé aucune solution de travail. Même si c'est lié au jeu, c'est juste un exemple, il pourrait être appliqué à d'autres programmes, je suppose, je ne peux pas l'exposer dans un autre contexte.

Voici la dénomination je (avec des exemples inutiles):

  • SubscriberBase est une classe qui envoie et reçoit des messages (comme un client réseau)
  • Broadcaster est le pont entre les abonnés (comme un réseau commutateur/serveur) et contient un vecteur de SubscriberBase.

je suis venu avec ce code minimal:

class SubscriberBase {}; 

class Broadcaster { 
    std::vector<SubscriberBase*> subscribers; 
public: 
    template<typename TMessage> 
    void broadcast(TMessage& message) { 
     for(auto subscriber : subscribers) { 

      // <<< Here is my problem <<< 
      message.accept<THE_ACTUAL_SUBSCRIBER_TYPE>(subscriber); 

     } 
    } 

    void attach(SubscriberBase* subscriber) { 
     subscribers.push_back(subscriber); 
    } 
}; 

//Base class for handling messages of any type 

template<typename TMessage> 
class MessageHandler { 
public: 
    virtual void handleMessage(TMessage& message) {} 
}; 

//Base class for messages 

template<typename TMessage> 
class Message { 
    friend class Broadcaster; 
private: 

    //Visitor pattern with CRTP 
    template<typename TSubscriber> 
    void accept(TSubscriber* subscriber) { 
     subscriber->handleMessage(*static_cast<TMessage*>(this)); 
    } 

}; 

//Messages 

struct EntityCreated : public Message<EntityCreated> {}; 
struct EntityDestroyed : public Message<EntityDestroyed> {}; 
struct BurnAllGummyBears : public Message<BurnAllGummyBears> {}; 

//Subscribers 

class EntityCache : public SubscriberBase, 
    public MessageHandler<EntityCreated>, 
    public MessageHandler<EntityDestroyed>, 
    public MessageHandler<BurnAllGummyBears> 
{ 
public: 
    void handleMessage(EntityCreated& message) override { /* add to cache */ } 
    void handleMessage(EntityDestroyed& message) override { /* remove from cache */ } 
    //does not override BurnAllGummyBears because it's not relevant for EntityCache 
}; 

Le problème est le type THE_ACTUAL_SUBSCRIBER_TYPE. Cela pourrait être n'importe quel abonné "concret"; dans ce cas, il serait par exemple EntityCache ou quelque chose d'autre comme Logger, CommandRecorder ...

J'ai essayé d'utiliser un autre CRTP couplé avec une autre classe:

class SubscriberBase {}; 

template<typename TSubscriber> 
class Subscriber : public SubscriberBase { /* some other visitor pattern ? */ }; 

class EntityCache : public Subscriber<EntityCache>, /* ... */ 

sans succès.

Toutes les idées sont appréciées, merci :)

Répondre

0

J'ai renoncé à cette idée et je pensais: « Eh bien, Kiss my .. » alors j'ai décidé d'aller pour une conception plus simple, l'approche normale et propre pour un code normal et propre.

//Messages 
struct EntityCreated {}; 
struct EntityDestroyed {}; 
struct ChopAllTrees {}; 
struct MakeGummyBearsEvil {}; 

//Subscriber is now a base class containing all of the message handlers 
class Subscriber { 
public: 
    virtual void handleMessage(EntityCreated& msg) {} 
    virtual void handleMessage(EntityDestroyed& msg) {} 
    virtual void handleMessage(ChopAllTrees& msg) {} 
    virtual void handleMessage(MakeGummyBearsEvil& msg) {} 
}; 

class Broadcaster { 
    std::vector<Subscriber*> subscribers; 
public:  
    template<typename M> 
    void broadcast(M& msg) { 
     for(auto subscriber : subscribers) { 
      subscriber->handleMessage(msg); 
     } 
    } 

    template<typename M> 
    void broadcast(M&& msg) { 
     M owner(msg); 
     broadcast(owner); 
    } 

    void attach(Subscriber* subscriber) { 
     auto it = std::find(subscribers.begin(), subscribers.end(), subscriber); 
     if(it == subscribers.end()) { 
      subscribers.push_back(subscriber); 
     } 
    } 

    void detach(Subscriber* subscriber) { 
     auto it = std::find(subscribers.begin(), subscribers.end(), subscriber); 
     if(it != subscribers.end()) { 
      subscribers.erase(it); 
     } 
    } 
}; 

//Subscribers simply inherits from Subscriber and overrides interesting handlers 
class EntityCache : public Subscriber { 
    void handleMessage(EntityCreated& msg) override { 
     std::cout << "Handling creation\n"; 
    } 
    void handleMessage(EntityDestroyed& msg) override { 
     std::cout << "Handling destruction\n"; 
    } 
}; 

class Eviler : public Subscriber { 
    void handleMessage(MakeGummyBearsEvil& msg) override { 
     std::cout << "Gummy bears are now evil!\n"; 
    } 
}; 

int main() { 
    EntityCache entityCache; 
    Eviler eviler; 

    Broadcaster broadcaster; 
    broadcaster.attach(&entityCache); 
    broadcaster.attach(&eviler); 

    EntityCreated entityCreated; 

    broadcaster.broadcast(entityCreated); //lvalue test 
    broadcaster.broadcast(MakeGummyBearsEvil()); //rvalue test 

    broadcaster.detach(&eviler); 

    broadcaster.broadcast(EntityDestroyed()); 
    broadcaster.broadcast(MakeGummyBearsEvil()); //no effect 
} 

Je suis d'accepter cette réponse car il résout le problème, mais depuis que je suis toujours intéressé sur la façon de le faire, si quelqu'un a rencontré ce problème dans le passé (probablement pas) et manipulaient, ne hésitez pas pour répondre et je vais l'accepter.