2013-04-04 6 views
2

J'ai une classe avec un modèle de méthode:Mettre en œuvre un « modèle d'observation statique »

struct Subject 
{ 
    template <typename T> 
    void doSomething() 
    { 
    .. 
    } 
}; 

Maintenant, chaque fois que doSomething est appelé (avec un T) Je voudrais avoir un « observateur » soit informé:

template <typename T> 
    void onDoSomething() 
    { 
    .. 
    } 

Il est important que l'observateur est un modèle de méthode (modèle de classe avec méthode reprise fonctionnerait aussi bien). Si ce n'était pas le cas, je pourrais simplement mettre en œuvre le modèle d'observateur commun.

Il serait autorisé à modifier Subject::doSomething() afin qu'il appelle une méthode. Cependant, la classe Subject ne devrait pas "connaître" la méthode Observer/Observer concrète.

L'idée sous-jacente est: J'utilise Subject dans deux projets. J'ai besoin (et j'ai) de l'observateur seulement dans l'un d'entre eux.

Y a-t-il un moyen d'y parvenir?

+0

J'ai oublié de mentionner que je ne peux pas utiliser les fonctionnalités C++ 11. Mais même si je pouvais, je ne vois pas comment cela résout ma question spécifique? – Philipp

+0

@Philipp vous avez raison, ça n'aide pas. J'ai mal compris votre question. – juanchopanza

Répondre

2

difficile. Il semble faire bouillir jusqu'à

La notification doit être résoluble au moment de la compilation (c'est un modèle)

et, en même temps,

La notification ne doit pas être résoluble à compiler le temps (Subject ne devrait pas connaître l'observateur)

Je peux penser à deux façons de le faire:


1. Ajouter une surcharge de doSomething:

template <typename T, typename Observer> 
void doSomething() 
{ 
    doSomething<T>(); 
    Observer::onDoSomething<T>(); 
} 

appellent alors la version d'un paramètre dans le projet A, et la version à deux paramètres dans le projet B.


2. avoir le fichier définissant Subject comprennent un en-tête qui sera différent/différemment configuré dans chaque projet:

Subject.h

#include "observer_def.hpp" 

struct Subject 
{ 
    template <typename T> 
    void doSomething() 
    { 
    .. 
    notifyDoSomething<T>(); 
    } 
}; 

observer_def.hpp dans le projet A:

template <typename> 
inline void notifyDoSomething() {} 

observer_def.HPP dans le projet B:

template <typename T> 
inline void notifyDoSomething() 
{ 
    MyObserver::onDoSomething<T>(); 
} 
+0

merci pour vos idées. La méthode 1 n'est pas souhaitable parce que 'doSomething' est appelé profondément dans d'autres méthodes que je ne veux pas changer. La méthode 2 ne m'a pas l'air d'un style C++ moderne. Je suppose que la solution que j'ai trouvée (voir autre réponse) est plus ce dont j'avais besoin, mais de toute façon. – Philipp

+0

@Philipp: en fait, votre solution nécessite exactement la même machinerie de préprocesseur que 'Angew' utilisée ... bien que je recommanderais d'utiliser un bloc' #ifdef ... #else ... # endif' plutôt que deux fichiers différents (c'est moins déroutant). –

+0

@MatthieuM. C'est pourquoi j'ai dit "différent/différemment configuré". J'essayais aussi de couvrir l'option '# ifdef'. – Angew

1

j'ai finalement trouvé une solution satisfaisante au moyen de spécialisation de modèle:

///signaling struct. Could be replaced with any other type. 
struct SpecializedObserver{}; 

///unspecialized: 
template <typename> 
struct Observer 
{ 
    template <typename T> 
    static void onDoSomething() 
    { 
     //default: do nothing. 
    } 
}; 

///optional. Specialize in project A or leave aside in project B: 
template<> 
struct Observer<SpecializedObserver> 
{ 
    template <typename T> 
    static void onDoSomething() 
    { 
     std::cout << "doing something with " << typeid(T).name() << std::endl; 
    } 
}; 

struct Subject 
{ 
    template <typename T> 
    void doSomething() 
    { 
     Observer<SpecializedObserver>::onDoSomething<T>(); 
     .. 
    } 
}; 

Cette solution ne nécessite aucune action si je ne veux pas d'observateur. Dans le cas où je veux en avoir un, je spécialise le modèle Observer comme indiqué ci-dessus.

EDIT: Cela a bien fonctionné dans mon test, mais la question est de savoir comment l'utiliser dans un scénario avec différentes unités de compilation - où devrais-je définir la spécialisation?

+1

La spécialisation doit être visible au point d'utilisation. Il doit donc être inclus avant que la propriété 'Subject :: doSomething' ne soit définie ... ce qui nécessite un hackery préprocesseur (' #ifdef ... ') et par conséquent on se demande pourquoi utiliser une spécialisation de template et pas seulement une définition différente d'un non-template Classe 'Observer'. –

Questions connexes