mercredi 29 novembre 2017

C++11: Store pointer to templated class member function

I am trying to implement a generic templated observer pattern in C++11. The target is to provide a mechanism for any class member function to be registered as a callback to the observer. The observer template is as following:

template <typename... Args>
class tsubject {
public:

   /// @brief Register a member function of any class as callback to the subject.
   template <typename AnyObserver>
   void register( AnyObserver* observer, void(AnyObserver::*func)(Args... ) ) {
       register( [=]( Args... args ) {
           ( observer->*func )( args... );
       });
   }

   /// @brief Register an std::function as callback to the subject.
   void register( std::function<void(Args...)> const & func ) {
       mCallbacks.push_back( func );
   }

   /// @brief Notify all registered observers
   void update( Args... p ) {
       for( auto it : mCallbacks ) {
           it( p... );
       }
   }

private:
    std::vector<std::function<void(Args...)> mCallbacks;
};

Usage:

class MyObserverClass {
public:
    void callback( std::string arg1, int arg2 ) {
       std::count << "callback:" << arg1 << ":" << arg2 << std::endl;
    }
}

int main( ) {
    tsubject<std::string, int> subject;

    MyObserverClass observer;
    subject.register( &observer, &MyObserverClass::callback );

    subject.update( "test", 1 );
}

Output:

callback: test:1

Issue:

int main( ) {
    tsubject<std::string, int> subject;

    MyObserverClass observer;
    subject.register( &observer, &MyObserverClass::callback );
    subject.register( &observer, &MyObserverClass::callback );

    subject.update( "test", 1 );
}

Output:

callback: test:1
callback: test:1

The issue with this implementation is that if the same observer method is registered multiple times, the callback is triggered multiple times. Since C++11 pointer to member functions are not normal pointers, they cannot be type-casted to void* for later comparison and since this is a variadic template functions, an std::map cannot be used for mCallback since the type cannot be specified. Is there any mechanism possible to avoid multiple registration elagantly?

Aucun commentaire:

Enregistrer un commentaire