antmd
4/18/2015 - 2:17 PM

boost_signals2_plus_fusion.cc

#include <iostream>
#include <utility>

#include <boost/fusion/container/map.hpp>
#include <boost/fusion/sequence/intrinsic/at_key.hpp>
#include <boost/fusion/sequence/intrinsic/value_at_key.hpp>
#include <boost/optional.hpp>
#include <boost/signals2.hpp>
#include <boost/utility/in_place_factory.hpp>


namespace util {

  //-----------------------------------------------------------------------------
  // EventSource - "Observable" mixin.
  //-----------------------------------------------------------------------------

  // Encapsulates event handler function signature.
  template<typename Signature> struct EventHandler {
  public:
    EventHandler(const EventHandler&) = delete;
    EventHandler& operator=(const EventHandler&) = delete;
    EventHandler() = default;

  private:
    template<typename Events> friend class EventSource;

    using Signal = boost::optional<
      typename boost::signals2::signal_type<
      Signature,
      boost::signals2::keywords::mutex_type<boost::signals2::dummy_mutex>
      >::type
    >;
    using SignalResult = typename Signal::value_type::result_type;

    Signal signal_;
  };


  // Generic event source mixin - users must derive from it.
  template<typename Events> class EventSource {
  private:
    using EventTable = typename Events::EventTable;

    // Helper type generator.
    template<typename Event> struct GetType {
      // EventHandler type.
      using EventHandler = typename boost::fusion::result_of::
        value_at_key<EventTable, Event>::type;
      // boost::optional type.
      using Signal = typename EventHandler::Signal;
      // Slot return type.
      using SignalResult = typename EventHandler::SignalResult;
    };

  public:
    // Connects event handler.
    template<typename Event, typename Handler>
    boost::signals2::connection Connect(Handler&& handler) {
      return GetSignal<Event>()->connect(std::forward<Handler>(handler));
    }

  protected:
    EventSource() = default;

    // Generates n-ary signal.
    template<typename Event, typename... Args>
    typename GetType<Event>::SignalResult FireEvent(Args&&... args) const {
      return GetSignal<Event>()->operator()(std::forward<Args>(args)...);
    }

  private:
    // Creates signal if not exists, returns reference to it.
    template<typename Event>
    typename GetType<Event>::Signal& GetSignal() const {
      auto& signal = boost::fusion::at_key<Event>(signals_).signal_;
      if (!signal)
        signal = boost::in_place();
      return signal;
    }

    mutable EventTable signals_;
  };
}  // namespace util


//-----------------------------------------------------------------------------
// Example usage.
//-----------------------------------------------------------------------------

struct MyEvents {
  struct EventA; struct EventB;
  using EventTable = boost::fusion::map<
    boost::fusion::pair<EventA, util::EventHandler<void()>>,
    boost::fusion::pair<EventB, util::EventHandler<int(int)>>
  >;
};

class MySource: public util::EventSource<MyEvents> {
public:
  void EmitEventA() {
    std::cout << "EventA signal generated." << std::endl;
    FireEvent<MyEvents::EventA>();
  }

  int EmitEventB() {
    int foo = 3;
    std::cout << "EventB signal generated. foo = " << foo << std::endl;
    int bar = *FireEvent<MyEvents::EventB>(foo);
    std::cout << "EventB signal processed. bar = " << bar << std::endl;
    return bar;
  }
};

class MyListener {
public:
  void OnEventA() {
    std::cout << "OnEventA handler called." << std::endl;
  }

  int OnEventB(int foo) {
    std::cout << "OnEventB handler called. foo = " << foo << std::endl;
    return foo * 2;
  }
};

int main() {
  MySource event_source;
  MyListener event_listener;

  // Connect event handlers.
  event_source.Connect<MyEvents::EventA>([&]() {
    event_listener.OnEventA();
  });
  event_source.Connect<MyEvents::EventB>([&](int foo) {
    return event_listener.OnEventB(foo);
  });

  // Fire events.
  event_source.EmitEventA();
  event_source.EmitEventB();
}
//-----------------------------------------------------------------------------
// Observable mixin.
//-----------------------------------------------------------------------------
 
#include <tuple>
#include <utility>
 
#include <boost/signals2.hpp>
 
 
// Convinience wrapper for boost::signals2::signal.
template<typename Signature> class Observer {
public:
  Observer(const Observer&) = delete;
  Observer& operator=(const Observer&) = delete;
  Observer() = default;
 
private:
  template<typename Observers> friend class Observable;
 
  using Signal = boost::signals2::signal<Signature>;
  using SignalResult = typename Signal::result_type;
 
  Signal signal_;
};
 
 
// Generic observable mixin - users must derive from it.
template<typename Observers> class Observable {
private:
  using ObserverTable = typename Observers::ObserverTable;
 
public:
  // Registers an observer.
  template<size_t ObserverId, typename F>
  boost::signals2::connection
  Register(F&& f) {
    return std::get<ObserverId>(signals_).signal_.connect(std::forward<F>(f));
  }
 
protected:
  Observable() = default;
 
  // Notifies observers.
  template<size_t ObserverId, typename... Args>
  typename std::tuple_element<ObserverId, ObserverTable>::type::SignalResult
  Notify(Args&&... args) const {
    return std::get<ObserverId>(signals_).signal_(std::forward<Args>(args)...);
  }
 
private:
  ObserverTable signals_;
};
 
 
//-----------------------------------------------------------------------------
// Example usage.
//-----------------------------------------------------------------------------
 
#include <iostream>
 
 
// Defines observers for Windows class.
struct WindowObservers {
  enum { ShowEvent, CloseEvent };
  using ObserverTable = std::tuple<
    Observer<void()>,                 // ShowEvent
    Observer<bool(bool force_close)>  // CloseEvent
  >;
};
 
 
// Window: our Observable.
class Window: public Observable<WindowObservers> {
public:
  void Show() {
    std::cout << "Window::Show called." << std::endl;
    Notify<WindowObservers::ShowEvent>();
    std::cout << "Window::Show handled." << std::endl << std::endl;
  }
 
  bool Close(bool force_close = false) {
    std::cout << "Window::Close called: force_close == "
              << std::boolalpha << force_close << "." << std::endl;
      
    const boost::optional<bool> can_close{
      Notify<WindowObservers::CloseEvent>(force_close) };
    std::cout << "Window::Close handled. can_close == "
              << std::boolalpha << (!can_close || *can_close) << "."
              << std::endl << std::endl;
      
    const bool closing{ force_close || !can_close || *can_close };
    if (closing) {
      // Actually close the window.
      // ...  
    }
    return closing;
  }
}; 
 
// Application: our Observer.
class Application {
public:
  explicit Application(Window& window) : window_(window) {
    // Register window observers.
    window_.Register<WindowObservers::ShowEvent>([this]() {
      OnWindowShow();
    });
    window.Register<WindowObservers::CloseEvent>([this](bool force_close) {
      return OnWindowClose(force_close);
    });
  }
 
private:    
  void OnWindowShow() {
    std::cout << "Application::OnWindowShow called." << std::endl;
  }
 
  bool OnWindowClose(bool force_close) {
    std::cout << "Application::WindowClose called: force_close == "
              << std::boolalpha << force_close << "." << std::endl;
    return force_close;
  }
 
  Window& window_;    
};
 
 
int main() {
  Window window;  
  Application application{ window };  
 
  // Notify observers.
  window.Show();
  //...
  window.Close(false);
  window.Close(true);
}
#include <iostream>
#include <functional>
#include <tuple>
#include <utility>


namespace util {

//-----------------------------------------------------------------------------
// EventSource - "Observable" mixin.
//-----------------------------------------------------------------------------

// Generic event source mixin - users must derive from it.
template<typename Events> class EventSource {
private:
  using EventTable = typename Events::EventTable;

public:
  // Connects event handler.
  template<size_t Event, typename Handler>
  void Connect(Handler&& handler) {
    std::get<Event>(signals_) = std::forward<Handler>(handler);
  }

protected:
  EventSource() = default;

  // Generates n-ary signal.
  template<size_t Event, typename... Args>
  typename std::tuple_element<Event, EventTable>::type::result_type
  FireEvent(Args&&... args) const {
    return std::get<Event>(signals_)(std::forward<Args>(args)...);
  }

private:
  mutable EventTable signals_;
};

}  // namespace util


//-----------------------------------------------------------------------------
// Example usage.
//-----------------------------------------------------------------------------

struct MyEvents {
  enum Event { EventA, EventB };
  using EventTable = std::tuple<
    std::function<void()>,
    std::function<int(int)>
  >;
};

class MySource: public util::EventSource<MyEvents> {
public:
  void EmitEventA() {
    std::cout << "EventA signal generated." << std::endl;
    FireEvent<MyEvents::EventA>();
  }

  int EmitEventB() {
    int foo = 3;
    std::cout << "EventB signal generated. foo = " << foo << std::endl;
    int bar = FireEvent<MyEvents::EventB>(foo);
    std::cout << "EventB signal processed. bar = " << bar << std::endl;
    return bar;
  }
};

class MyListener {
public:
  void OnEventA() {
    std::cout << "OnEventA handler called." << std::endl;
  }

  int OnEventB(int foo) {
    std::cout << "OnEventB handler called. foo = " << foo << std::endl;
    return foo * 2;
  }
};

int main() {
  MySource event_source;
  MyListener event_listener;

  // Connect event handlers.
  event_source.Connect<MyEvents::EventA>([&]() {
    event_listener.OnEventA();
  });
  event_source.Connect<MyEvents::EventB>([&](int foo) {
    return event_listener.OnEventB(foo);
  });

  // Fire events.
  event_source.EmitEventA();
  event_source.EmitEventB();
}