Question

typedef void (FunctionSet::* Function)();

class MyFunctionSet : public FunctionSet
{
protected:
    void addFunctions()
    {
        addFunction(Function(&MyFunctionSet::function1));
    }

    void function1()
    {
        // Do something.
    }
};

La méthode addFunction ajoute la fonction à une liste de la classe de base,
qui peut ensuite être énuméré pour appeler toutes les fonctions.

Y a-t-il un moyen de simplifier (moins de travail de frappe) l'ajout de fonctions?

Était-ce utile?

La solution

On dirait que vous affectez un pointeur de fonction de membre à une fonction de la classe dérivée à un pointeur de fonction de membre à une fonction de la classe de base. C'est interdit, car cela ouvre un trou dans le système de types. C'est une surprise (du moins pour moi, la première fois que j'ai entendu ça). Lisez la cette réponse pour Pourquoi.

Pour répondre à votre question, je ferais de addFunction un modèle:

void addFunctions() {
    addFunction(&MyFunctionSet::function1);
}

Remplacez addFunction dans la classe de base par ceci:

template<typename Derived>
void addFunction(void(Derived::*f)()) {
    myFunctions.push_back(static_cast<Function>(f));
}

Mieux vaut utiliser static_cast , car il vous dira si Dérivé n'est pas réellement dérivé de FunctionSet .

Autres conseils

Pouvez-vous expliquer ce que vous essayez d'accomplir en faisant cela?

Cela semble être une conception plutôt mauvaise, ne pouvez-vous pas avoir une classe abstraite (" interface c ++ ") telle que " Computable " qui a une fonction virtuelle pure1, une sous-classe Computable pour chaque implémentation, puis MyFunctionSet doit-il maintenir un ensemble de Computable?

Y a-t-il une raison spécifique pour laquelle vous utilisez des pointeurs de fonction?

J'ai donc tendance à être en désaccord avec Uri, un peu.

Si vous implémentez le modèle Observer, vous voulez pouvoir dire:

"Quand l'objet X fait Y, je veux exécuter le code Z".

Adopter une approche purement basée sur une classe abstraite n’est pas la meilleure façon de le faire. Exiger une classe distincte (en particulier en C ++) pour chaque gestionnaire d'événements est excessif. Si vous venez de Java, c'est comme ça qu'ils font tout. Cependant, Java possède deux caractéristiques qui ne rendent que légèrement ennuyeux: la classe anonyme et "instance". classes de membres. Cela vous permet de définir plusieurs gestionnaires pour plusieurs événements dans la même classe. Vous devez préfixer les méthodes avec "class {", mais vous pouvez le faire.

C ++ n’a ni classes anonymes ni "instance". classes de membres. Cela rend l’utilisation d’une classe abstraite pour les événements beaucoup plus lourde si vous avez plusieurs gestionnaires qui doivent partager l’état. Vous devez faire l'équivalent de la génération de fermeture à la main, ce qui peut devenir assez rapidement irritant.

.NET utilise des délégués pour la gestion des événements, qui sont essentiellement des pointeurs de fonction sûrs. Ils rendent le code pour gérer les événements très simple. Cependant, contrairement aux pointeurs de fonction en C ++, un délégué unifie les pointeurs de fonction membres et les pointeurs de fonction statiques. Fondamentalement, il peut curry le "this" paramètre de n'importe quelle fonction membre, en laissant un pointeur de fonction qui ressemble à un pointeur de fonction statique. Cela signifie que le type d'objet qui gère l'événement ne fait pas partie de l'événement "interface". Cela le rend très flexible.

Vous ne pouvez pas le faire directement à l'aide de pointeurs de fonction de membre C ++, car le "this" type finit par faire partie du type pointeur de fonction, limitant ainsi vos gestionnaires à n'apparaître que dans la classe en cours.

La meilleure solution pour C ++ est un hybride entre les deux. Ce que vous voulez, c'est une interface générique comme celle-ci:

class EventHandler
{
public:
    virtual void Handle() = 0;
};

Et ensuite une implémentation pour les fonctions membres comme ceci

template <class T>
class MemberFuncEventHandler : public EventHandler
{
public:


    MemberFuncEventHandler(T * pThis, void (T::*pFunc)()) : m_pThis(pThis), m_pFunc(pFunc)
    {
    }

    void Handle()
    {
        (m_pThis->*m_pFunc)();
    } 
private:
    T* m_pThis;
    void (T::*m_pFunc)();
};

template <class T>
EventHandler * Handler(T * pThis, void (T::*pFunc)())
{
    return new MemberFuncEventHandler<T>(pThis, pFunc);
}

Vous pouvez également définir de la même manière une classe de gestionnaires pour les méthodes statiques. Ensuite, vous pouvez simplement faire les choses comme ceci:

Handlers += Handler(obj, &(c1::foo));
Handlers += Handler(obj, &(c2::bar));
Handlers += Handler(blaa); // for static methods...

Vous pourriez surcharger l'opérateur d'addition, je suppose.

Si vous essayez de créer un système de rappel ou quelque chose de similaire, je recommanderais la bibliothèque Boost.Signals. Fondamentalement, il regroupe les fonctions et appelle le groupe de fonctions sur commande (comme toute autre bibliothèque de signaux), mais il est également conçu pour fonctionner avec Boost.Bind et Boost.Function qui sont géniaux.

Exemple:

using boost::function
using boost::bind
using boost::signal

void some_other_func();    

Foo a;
Bar b;

signal<void()> sig;

sig.connect(bind(&Foo::foo,&a));
sig.connect(bind(&Bar::bar,&b));
sig.connect(some_other_func);

sig(); // calls -> a.foo(), b.bar(), some_other_func()

Prend également en charge le blocage, la gestion sophistiquée des connexions et d’autres fonctionnalités intéressantes.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top