Observer.hpp 6.66 KB
Newer Older
1
2
3
4
5
6
7
#pragma once

#include <algorithm>
#include <list>
#include <memory>
#include <type_traits>

Praetorius, Simon's avatar
Praetorius, Simon committed
8
9
#include <dune/common/typeutilities.hh>

10
11
12
13
14
15
16
#include <amdis/common/ConceptsBase.hpp>
#include <amdis/Output.hpp>

namespace AMDiS
{
  namespace Impl
  {
Praetorius, Simon's avatar
Praetorius, Simon committed
17
    // forward declaration
18
    template <class Event>
Praetorius, Simon's avatar
Praetorius, Simon committed
19
    class ObserverInterface;
20
21

    template <class Event>
Praetorius, Simon's avatar
Praetorius, Simon committed
22
    class SignalBase
23
24
    {
    public:
Praetorius, Simon's avatar
Praetorius, Simon committed
25
26
27
      /// Attaches an observer to this class. This method will be called by all observers with
      /// with themselves as argument.
      void attachObserver(ObserverInterface<Event>* o) const
28
      {
Praetorius, Simon's avatar
Praetorius, Simon committed
29
        observers_.push_back(o);
30
31
      }

Praetorius, Simon's avatar
Praetorius, Simon committed
32
33
34
      /// Detaches an observer to this class. This method will be called by all observers with
      /// with themselves as argument.
      void detachObserver(ObserverInterface<Event>* o) const
35
      {
Praetorius, Simon's avatar
Praetorius, Simon committed
36
37
38
        auto it = std::find(observers_.begin(), observers_.end(), o);
        if (it != observers_.end())
          observers_.erase(it);
39
40
      }

Praetorius, Simon's avatar
Praetorius, Simon committed
41
42
      /// Notify all observers that have called attachObserver but have not called detachObserver
      void notify(Event const& e) const
43
      {
Praetorius, Simon's avatar
Praetorius, Simon committed
44
45
        for (ObserverInterface<Event>* o : observers_)
          o->update(e);
46
47
      }

Praetorius, Simon's avatar
Praetorius, Simon committed
48
49
50
51
52
    private:
      /// List of observers that need to be notified in case of an event
      // NOTE: this list is mutable, since the notification list itself is not part
      //       of the internal state of the object signaling the event.
      mutable std::list<ObserverInterface<Event>*> observers_;
53
54
    };

Praetorius, Simon's avatar
Praetorius, Simon committed
55

56
    template <class Event>
Praetorius, Simon's avatar
Praetorius, Simon committed
57
    class ObserverInterface
58
59
    {
    public:
Praetorius, Simon's avatar
Praetorius, Simon committed
60
      virtual ~ObserverInterface() = default;
61

Praetorius, Simon's avatar
Praetorius, Simon committed
62
63
64
      /// Attach the observer to a subject. It will then receive notifications from the subject when
      /// an event of type Event it triggered.
      void attach(std::shared_ptr<SignalBase<Event> const> const& subject)
65
      {
Praetorius, Simon's avatar
Praetorius, Simon committed
66
67
        if (bool(subject))
          subject->attachObserver(this);
68
69
      }

Praetorius, Simon's avatar
Praetorius, Simon committed
70
71
72
      /// Detach the observer from the subject. It will then no longer receive notifications. This
      /// must be called before the observer is deleted.
      void detach(std::shared_ptr<SignalBase<Event> const> const& subject)
73
      {
Praetorius, Simon's avatar
Praetorius, Simon committed
74
75
        if (bool(subject))
          subject->detachObserver(this);
76
77
      }

Praetorius, Simon's avatar
Praetorius, Simon committed
78
79
      /// This method will be called by the subject when triggering the event.
      virtual void update(Event const&)
80
      {
Praetorius, Simon's avatar
Praetorius, Simon committed
81
        error_exit("Method must be overridden by derived class");
82
83
84
85
86
      }
    };


    template <class Event>
Praetorius, Simon's avatar
Praetorius, Simon committed
87
    class ObserverBase
88
89
        : virtual public ObserverInterface<Event>
    {
Praetorius, Simon's avatar
Praetorius, Simon committed
90
      using Self = ObserverBase;
91

Praetorius, Simon's avatar
Praetorius, Simon committed
92
93
    private:
      ObserverBase() = default;
94

Praetorius, Simon's avatar
Praetorius, Simon committed
95
96
97
98
      // use subject if Event is handled directly
      template <class S,
        REQUIRES(std::is_base_of<SignalBase<Event>, S>::value)>
      ObserverBase(std::shared_ptr<S const> subject, Dune::PriorityTag<2>)
99
100
101
102
103
        : subject_(std::move(subject))
      {
        this->attach(subject_);
      }

Praetorius, Simon's avatar
Praetorius, Simon committed
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
      // get next subject in hierarchy if Event is not handled
      template <class Observer,
        class = void_t<decltype(std::declval<Observer>().subject_)> >
      ObserverBase(std::shared_ptr<Observer const> const& o, Dune::PriorityTag<1>)
        : ObserverBase(o->subject_)
      {}

      // non-observable type
      template <class T>
      ObserverBase(std::shared_ptr<T const> const& other, Dune::PriorityTag<0>)
      {
        /* fallback implementation */
      }

    public:
      template <class T>
      ObserverBase(std::shared_ptr<T const> const& arg)
        : ObserverBase(arg, Dune::PriorityTag<42>{})
122
123
124
      {}

      /// Copy constructor
Praetorius, Simon's avatar
Praetorius, Simon committed
125
      ObserverBase(Self const& that)
126
127
128
129
130
131
        : subject_(that.subject_)
      {
        this->attach(subject_);
      }

      /// Move constructor
Praetorius, Simon's avatar
Praetorius, Simon committed
132
      ObserverBase(Self&& that)
133
134
135
136
137
      {
        swap(*this, that);
      }

      /// Destructor. Detaches observer from the subject
Praetorius, Simon's avatar
Praetorius, Simon committed
138
      ~ObserverBase() override
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
      {
        this->detach(subject_);
      }

      /// Copy/Move assignment
      Self& operator=(Self that)
      {
        swap(*this, that);
        return *this;
      }

      /// Swaps the contents of lhs and rhs
      friend void swap(Self& lhs, Self& rhs)
      {
        using std::swap;
        lhs.detach(lhs.subject_);
        rhs.detach(rhs.subject_);
        swap(lhs.subject_, rhs.subject_);
        lhs.attach(lhs.subject_);
        rhs.attach(rhs.subject_);
      }

    private:
      /// The observed subject
Praetorius, Simon's avatar
Praetorius, Simon committed
163
      std::shared_ptr<SignalBase<Event> const> subject_ = nullptr;
164
165
    };

Praetorius, Simon's avatar
Praetorius, Simon committed
166
167
168
169
170
  } // end namespace Impl


  /// \brief Mixin for signaling of certain events.
  /**
171
172
173
174
175
176
177
178
179
   *  Derived classes T can emit a signal e by calling notify(e). This will send the signal to all
   *  classes U... deriving from Observer<S, Events...> if
   *  - the type of the event is included in Events,
   *  - T = S or S is an Observer of T (directly or indirectly via other Observers),
   *  - U called the Observer constructor with an instance of S that has direct or indirect access
   *      to the instance of T (see Observer)
   */
  template <class Event, class... Events>
  class Signals
Praetorius, Simon's avatar
Praetorius, Simon committed
180
      : public Impl::SignalBase<Event>
181
182
183
      , public Signals<Events...>
  {
  public:
Praetorius, Simon's avatar
Praetorius, Simon committed
184
    using Impl::SignalBase<Event>::notify;
185
186
187
188
189
    using Signals<Events...>::notify;
  };

  template <class Event>
  class Signals<Event>
Praetorius, Simon's avatar
Praetorius, Simon committed
190
      : public Impl::SignalBase<Event>
191
192
  {
  public:
Praetorius, Simon's avatar
Praetorius, Simon committed
193
    using Impl::SignalBase<Event>::notify;
194
195
196
  };


Praetorius, Simon's avatar
Praetorius, Simon committed
197
198
199
  /// \brief Mixin for reacting to certain events.
  /**
   *  Derived classes can react to events by implementing the functions `update(Event)` for
200
   *  all Events specified in the template parameter list, whose origin is an instance of class
Praetorius, Simon's avatar
Praetorius, Simon committed
201
   *  `Subject` specified in the constructor to Observer and any indirectly observed class instances.
202
   *  Observed instances may include:
Praetorius, Simon's avatar
Praetorius, Simon committed
203
204
205
206
207
208
209
   *  - the instance of class `Subject` passed to the constructor
   *  - the instance passed to the constructor of `Subject::Observer<S>` if Subject inherits from
   *    \ref Observer<S>
   *  - all other instances indirectly accessible in the above way
   *
   *  For each event E the first object in the above hierarchy that implements \ref Signals<Es...> with E
   *  included in Es... will be observed by this class.
210
211
212
   */
  template <class Subject, class... Events>
  class Observer
Praetorius, Simon's avatar
Praetorius, Simon committed
213
      : public Impl::ObserverBase<Events>...
214
215
  {
    template <class E>
Praetorius, Simon's avatar
Praetorius, Simon committed
216
    friend class Impl::ObserverBase;
217
218

  public:
Praetorius, Simon's avatar
Praetorius, Simon committed
219
220
    Observer(std::shared_ptr<Subject const> const& s)
      : Impl::ObserverBase<Events>(s)...
221
222
223
      , subject_(std::move(s))
    {}

Praetorius, Simon's avatar
Praetorius, Simon committed
224
225
226
    Observer(std::shared_ptr<Subject> const& s)
      : Observer(std::const_pointer_cast<Subject const>(s))
    {}
227

Praetorius, Simon's avatar
Praetorius, Simon committed
228
229
  private:
    std::shared_ptr<Subject const> subject_ = nullptr;
230
231
232
  };

} // end namespace AMDiS