2009-08-16 10 views
2

Le code suivant ne fonctionne PAS, mais il exprime bien ce que je souhaite faire. Il y a un problème avec le conteneur struct struct, qui, je pense, DEVRAIT fonctionner car sa taille est connue pour tout argument template.Rappel en C++, membre du modèle?

class callback { 

    public: 

    // constructs a callback to a method in the context of a given object 
    template<class C> 
    callback(C& object, void (C::*method)()) 
    : ptr.o(object), ptr.m(method) {} 

    // calls the method 
    void operator()() { 
    (&ptr.o ->* ptr.m)(); 
    } 

    private: 

    // container for the pointer to method 
    template<class C> 
    struct { 
    C& o; 
    void (C::*m)(); 
    } ptr; 

}; 

Y at-il un moyen de faire une telle chose? Je veux dire avoir un callback de classe non-template qui encapsule un pointeur sur la méthode?

Merci les gourous C++!

Edit:

S'il vous plaît voir ceci:

Callback in C++, template member? (2)

+2

Vous voulez utiliser 'boost :: function' ou 'boost :: signal': http://www.boost.org/doc/libs/1_39_0/doc/html/function.html. – avakar

Répondre

0

Vous ne dites pas ce que vous avez trouvé des erreurs, mais je trouve que cela a fonctionné:

template<typename C> 
class callback { 

    public: 

    // constructs a callback to a method in the context of a given object 
    callback(C& object, void (C::*method)()) 
    : ptr(object,method) {} 

    // calls the method 
    void operator()() { 
    (&ptr.o ->* ptr.m)(); 
    } 

    private: 

    // container for the pointer to method 
    // template<class C> 
    struct Ptr{ 
    Ptr(C& object, void (C::*method)()): o(object), m(method) {} 
    C& o; 
    void (C::*m)(); 
    } ptr; 

}; 

Remarque que Ptr a besoin d'un constructeur car il a un membre de référence.

Vous pourriez faire sans struct Ptr et avoir les membres bruts.

Testé avec VS2008 express.

+0

Ce type ne sera pas polymorphe, ce que je suppose est largement le point de l'exercice. Maintenant, si vous avez ajouté une classe de base avec un opérateur '()' virtuel, alors vous iriez bien. –

+0

Oui, mais maintenant le rappel est un modèle ... est-il possible de le faire sans que le rappel ne soit un modèle? –

+0

Il faut également considérer le découpage des objets, etc. Il est préférable de stocker les données via un pointeur. –

2

Vous devez utiliser le polymorphisme. Utilisez une classe de base abstraite avec une méthode d'appel virtuelle (operator() si vous le souhaitez), avec un descendant basé sur un modèle qui implémente la méthode virtuelle en utilisant la signature de type correcte.

De la manière dont vous l'avez maintenant, les données contenant le type sont modélisées, mais le code destiné à invoquer la méthode et à passer l'objet ne l'est pas. Cela ne marchera pas; les paramètres du type de gabarit doivent passer par la construction et l'invocation.

+0

bonne idée, regardez mon essai ci-dessous. –

1

@Barry Kelly

#include <iostream> 

class callback { 
    public: 
    virtual void operator()() {}; 
}; 

template<class C> 
class callback_specialization : public callback { 
    public: 
    callback_specialization(C& object, void (C::*method)()) 
    : o(object), m(method) {} 

    void operator()() { 
    (&o ->* m)(); 
    } 

    private: 
    C& o; 
    void (C::*m)(); 

}; 

class X { 
    public: 
    void y() { std::cout << "ok\n"; } 
}; 

int main() { 
    X x; 
    callback c(callback_specialization<X>(x, &X::y)); 
    c(); 
    return 0; 
} 

J'ai essayé, mais ça ne marche pas (impression "ok") ... pourquoi?

Edit: Comme Neil Butterworth mentionné, le polymorphisme fonctionne par des pointeurs et des références,

X x; 
    callback& c = callback_specialization<X>(x, &X::y); 
    c(); 

Edit: Avec ce code, je reçois une erreur:

invalid initialization of non-const reference of type ‘callback&’ 
from a temporary of type ‘callback_specialization<X>’ 

maintenant , Je ne comprends pas cette erreur, mais si je remplace callback & c avec & const rappel c et opérateur void virtuel()() avec opérateur void virtuel()() const, il fonctionne.

+0

Vous instanciez un objet de rappel, puis appelez sa méthode operator(), qui est définie pour ne rien faire. Pourquoi devrait-il faire quelque chose? –

+0

C'est virtuel, ne devrait-il pas appeler la version de callback_specialization? Au moins c'est ce que je voulais faire ... –

+0

Son appelé slicing - vous initialisez un 'callback' avec un objet dérivé qui est plus grand, donc vous perdez les bits supplémentaires. Vous pourriez essayer d'initialiser un rappel et de votre spécialisation. – quamrana

0

Améliorer la réponse de l'OP:

int main() { 
    X x; 
    callback_specialization<X> c(x, &X::y); 
    callback& ref(c); 
    c(); 
    return 0; 
} 

Cette affiche "ok".

Testé sur VS2008 express.

4

J'ai récemment mis en œuvre ceci:

#define UNKOWN_ITEM 0xFFFFFFFF 

template <typename TArg> 
class DelegateI 
{ 
public: 
    virtual void operator()(TArg& a)=0; 
    virtual bool equals(DelegateI<TArg>* d)=0; 
}; 


template <class TArg> 
class Event 
{ 
public:  
    Event() 
    { 
    } 

    ~Event() 
    { 
    for (size_t x=0; x<m_vDelegates.size(); x++) 
     delete m_vDelegates[x]; 
    } 

    void operator()(TArg& a) 
    { 
     for (size_t x=0; x<m_vDelegates.size(); x++) 
     { 
      m_vDelegates[x]->operator()(a); 
     } 
    } 

    void operator+=(DelegateI<TArg>* d) 
    { 
     if (findInfo(d) != UNKOWN_ITEM) 
     { 
      delete d; 
      return; 
     } 

     m_vDelegates.push_back(d); 
    } 

    void operator-=(DelegateI<TArg>* d) 
    { 
     uint32 index = findInfo(d); 

     delete d; 

     if (index == UNKOWN_ITEM) 
      return; 

     m_vDelegates.erase(m_vDelegates.begin()+index); 
    } 

protected: 
    int findInfo(DelegateI<TArg>* d) 
    { 
     for (size_t x=0; x<m_vDelegates.size(); x++) 
     { 
      if (m_vDelegates[x]->equals(d)) 
       return (int)x; 
     } 

     return UNKOWN_ITEM; 
    } 

private: 
    std::vector<DelegateI<TArg>*> m_vDelegates; 
}; 

template <class TObj, typename TArg> 
class ObjDelegate : public DelegateI<TArg> 
{ 
public: 
    typedef void (TObj::*TFunct)(TArg&); 

    ObjDelegate(TObj* t, TFunct f) 
    { 
     m_pObj = t; 
     m_pFunct = f; 
    } 

    virtual bool equals(DelegateI<TArg>* di) 
    { 
     ObjDelegate<TObj,TArg> *d = dynamic_cast<ObjDelegate<TObj,TArg>*>(di); 

     if (!d) 
      return false; 

     return ((m_pObj == d->m_pObj) && (m_pFunct == d->m_pFunct)); 
    } 

    virtual void operator()(TArg& a) 
    { 
     if (m_pObj && m_pFunct) 
     { 
      (*m_pObj.*m_pFunct)(a); 
     } 
    } 

    TFunct m_pFunct; // pointer to member function 
    TObj* m_pObj;  // pointer to object 
}; 

template <typename TArg> 
class FunctDelegate : public DelegateI<TArg> 
{ 
public: 
    typedef void (*TFunct)(TArg&); 

    FunctDelegate(TFunct f) 
    { 
     m_pFunct = f; 
    } 

    virtual bool equals(DelegateI<TArg>* di) 
    { 
     FunctDelegate<TArg> *d = dynamic_cast<FunctDelegate<TArg>*>(di); 

     if (!d) 
      return false; 

     return (m_pFunct == d->m_pFunct); 
    } 

    virtual void operator()(TArg& a) 
    { 
     if (m_pFunct) 
     { 
      (*m_pFunct)(a); 
     } 
    } 

    TFunct m_pFunct; // pointer to member function 
}; 


template <typename TArg> 
class ProxieDelegate : public DelegateI<TArg> 
{ 
public: 
    ProxieDelegate(Event<TArg>* e) 
    { 
     m_pEvent = e; 
    } 

    virtual bool equals(DelegateI<TArg>* di) 
    { 
     ProxieDelegate<TArg> *d = dynamic_cast<ProxieDelegate<TArg>*>(di); 

     if (!d) 
      return false; 

     return (m_pEvent == d->m_pEvent); 
    } 

    virtual void operator()(TArg& a) 
    { 
     if (m_pEvent) 
     { 
      (*m_pEvent)(a); 
     } 
    } 

    Event<TArg>* m_pEvent; // pointer to member function 
}; 


template <class TObj, class TArg> 
DelegateI<TArg>* delegate(TObj* pObj, void (TObj::*NotifyMethod)(TArg&)) 
{ 
    return new ObjDelegate<TObj, TArg>(pObj, NotifyMethod); 
} 

template <class TArg> 
DelegateI<TArg>* delegate(void (*NotifyMethod)(TArg&)) 
{ 
    return new FunctDelegate<TArg>(NotifyMethod); 
} 

template <class TArg> 
DelegateI<TArg>* delegate(Event<TArg>* e) 
{ 
    return new ProxieDelegate<TArg>(e); 
} 

l'utiliser comme ceci:

définissent:

Event<SomeClass> someEvent; 

enrôlent callbacks:

someEvent += delegate(&someFunction); 
someEvent += delegate(classPtr, &class::classFunction); 
someEvent += delegate(&someOtherEvent); 

déclencheur:

someEvent(someClassObj); 

Vous pouvez également créer vos propres délégués et surpasser ce qu'ils font. J'ai fait un couple d'autres avec un être en mesure de s'assurer que l'événement déclenche la fonction dans le fil gui au lieu du fil, il a été appelé.

+0

cool :-) ... pourrait Événement ne pas être un modèle? –

+0

il doit être un modèle pour autoriser uniquement les délégués qui peuvent gérer ce type d'objet à enregistrer. – Lodle

+0

Ah, j'ai compris, merci beaucoup. Mais il y a une certaine allocation dynamique sous le capot, ** nouveau ** dans les fonctions de délégué. S'il vous plaît voir mon autre question ici: http://stackoverflow.com/questions/1284239/callback-in-c-template-member-2 –

6

Ceci est un exemple de travail complet qui fait ce que je pense que vous essayez de le faire:

#include <iostream> 
#include <memory> 

// INTERNAL CLASSES 

class CallbackSpecBase 
{ 
    public: 
    virtual ~CallbackSpecBase() {} 
    virtual void operator()() const = 0; 
}; 

template<class C> 
class CallbackSpec : public CallbackSpecBase 
{ 
    public: 
    CallbackSpec(C& o, void (C::*m)()) : obj(o), method(m) {} 
    void operator()() const { (&obj->*method)(); } 

    private: 
    C& obj; 
    void (C::*method)(); 
}; 

// PUBLIC API 

class Callback 
{ 
    public: 
    Callback() {} 

    void operator()() { (*spec)(); } 

    template<class C> 
     void set(C& o, void (C::*m)()) { spec.reset(new CallbackSpec<C>(o, m)); } 

    private: 
    std::auto_ptr<CallbackSpecBase> spec; 
}; 

// TEST CODE 

class Test 
{ 
    public: 
    void foo() { std::cout << "Working" << std::endl; } 
    void bar() { std::cout << "Like a charm" << std::endl; } 
}; 

int main() 
{ 
    Test t; 
    Callback c; 
    c.set(t, &Test::foo); 
    c(); 
    c.set(t, &Test::bar); 
    c(); 
} 
+0

Ouais réponse cool, dommage que je ne peux pas choisir deux d'entre eux pour être le meilleur ... mais vous avez une allocation dynamique. Voir http://stackoverflow.com/questions/1284239/callback-in-c-template-member-2 –

Questions connexes