Search code examples
c++boostc++11signalsobserver-pattern

C++11 observer pattern (signals, slots, events, change broadcaster/listener, or whatever you want to call it)


With the changes made in C++11 (such as the inclusion of std::bind), is there a recommended way to implement a simple single-threaded observer pattern without dependence on anything external to the core language or standard library (like boost::signal)?

EDIT

If someone could post some code showing how dependence on boost::signal could be reduced using new language features, that would still be very useful.


Solution

  • I think that bind makes it easier to create slots (cfr. the 'preferred' syntax vs. the 'portable' syntax - that's all going away). The observer management, however, is not becoming less complex.

    But as @R. Martinho Fernandes mentions: an std::vector<std::function< r(a1) > > is now easily created without the hassle for an (artificial) 'pure virtual' interface class.


    Upon request: an idea on connection management - probably full of bugs, but you'll get the idea:

    // note that the Func parameter is something
    // like std::function< void(int,int) > or whatever, greatly simplified
    // by the C++11 standard
    template<typename Func>
    struct signal {
      typedef int Key; // 
      Key nextKey;
      std::map<Key,Func> connections;
      
      // note that connection management is the same in C++03 or C++11
      // (until a better idea arises)
      template<typename FuncLike>
      Key connect( FuncLike f ) {
         Key k=nextKey++;
         connections[k]=f;
         return k;
      }
    
      void disconnect(Key k){
         connections.erase(k);
      }
    
      template<typename... Args>
      typename Func::result_type call(Args... args...){
         // supposing no subcription changes within call:
         for(auto &connection: connections){
            connection.second(args...);
         }
      }
    };
    

    Usage:

    signal<function<void(int,int)>> xychanged;
    
    void dump(int x, int y) { cout << x << ", " << y << endl; }
    
    struct XY { int x, y; } xy;
    
    auto dumpkey=xychanged.connect(dump);
    auto lambdakey=xychanged.connect([&xy](int x, int y){ xy.x=x; xy.y=y; });
    
    xychanged.call(1,2);