Boost logo

Boost Users :

Subject: [Boost-users] Generic “Observable” mixin based on Boost
From: Павел Фролов (ark.fps_at_[hidden])
Date: 2013-09-17 13:23:53


I find Observer pattern a very useful tool to decrease coupling between
modules of a system. Boost provides Signals2 which can be used for that
purpose. But adding Signal2 objects to arbitrary classes is not very
convenient, because your must manually implement RegisterEvent/FireEvent
functions.

What I was looking for is a reusable mixin which could add “Observable”
behavior to arbitrary class with minimal hassle. Ideally, the user of
mixing must only provide event table which contains event tags and handler
signatures, e.g. (in pseudocode):
EventTable {
  EventA: void(),
  EventB: int(bool),
  ...
}

I came up with working proof of concept which uses CRTP-based mixin and
traits class for event table declaration. I'm new to template
meta-programming, so I'm looking forward to comments about possible
improvements.

In particular, I'm not very satisfied with syntax boost::fusion::pair<Show,
boost::optional<boost::signals2::signal<void()>>> (see attachement). I
think, I can do better by allowing user to specify just
boost::fusion::pair<Show,
void()> and then generate proper type with mpl::transform and/or
fusion::transform. This way is shorter and implementation details
(optional, signals2) are hidden from the user.

The mixin code (full code with example usage in attachment):

#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>

//*****************************************************************************
// EventSource mixin
//*****************************************************************************

// event source traits base (concrete event source must create
specialization)
template<typename ConcreteEventSource> struct Events;

// event source mixin (using CRTP)
template<typename ConcreteEventSource> class EventSource {
public:
  typedef ::Events<ConcreteEventSource> Events;

private:
  typedef typename Events::EventTable EventTable;

  // helper type generator
  template<typename Event> struct GetType {
    // boost::optional type
    typedef typename boost::fusion::result_of::
    value_at_key<EventTable, Event>::type SignalWrapper;
    // boost::signal type for particular boost::optional type
    typedef typename SignalWrapper::value_type Signal;
    // slot return type for particular boost::signal type
    typedef typename Signal::result_type SignalReturnType;
  };

public:
  // connect event handler
  template<typename Event, typename Handler>
  void Connect(Handler&& handler) {
    GetSignal<Event>()->connect(std::move(handler));
  }

protected:
  // generate nullary signal
  template<typename Event>
  typename GetType<Event>::SignalReturnType FireEvent() {
    return GetSignal<Event>()->operator()();
  }

  // generate unary signal
  template<typename Event, typename Arg1>
  typename GetType<Event>::SignalReturnType FireEvent(Arg1 arg1) {
    return GetSignal<Event>()->operator()(arg1);
  }

  // ... more overloads (MSVC does not support variadic templates)

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

  EventTable signals_;
};





Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net