Observer.hpp 4.83 KB
Newer Older
1
2
3
4
#pragma once

#include <algorithm>
#include <memory>
Praetorius, Simon's avatar
Praetorius, Simon committed
5
#include <set>
6
7
#include <type_traits>

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

11
#include <amdis/common/ConceptsBase.hpp>
Praetorius, Simon's avatar
Praetorius, Simon committed
12
#include <amdis/common/Index.hpp>
13
14
15

namespace AMDiS
{
Praetorius, Simon's avatar
Praetorius, Simon committed
16
  namespace event
17
  {
Praetorius, Simon's avatar
Praetorius, Simon committed
18
19
20
21
22
    /** 
     * An event that is signaled before the actual adaption happens. Example: grid.preAdapt(). 
     * The \ref value might indicate whether any pre-processing is necessary.
     **/
    struct preAdapt { bool value = true; };
23

Praetorius, Simon's avatar
Praetorius, Simon committed
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    /** 
     * An event that is called directly of the adaption. Example: grid.adapt().
     * The \ref value indicates whether something is changed during adaption.
     **/
    struct adapt { bool value = true; };

    /** 
     * An event that is called after adaption to indicate the start of a clean-up phase.
     **/
    struct postAdapt {};
  }


  template <class Event>
  class ObserverInterface
  {
  public:
    virtual ~ObserverInterface() = default;
    virtual void update(Event e) = 0;
  };
Praetorius, Simon's avatar
Praetorius, Simon committed
44
45
46
47


  /// \brief Mixin for signaling of certain events.
  /**
48
49
50
51
52
53
54
55
   *  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>
Praetorius, Simon's avatar
Praetorius, Simon committed
56
57
58
  class Notifier
      : public Notifier<Event>
      , public Notifier<Events...>
59
60
  {
  public:
Praetorius, Simon's avatar
Praetorius, Simon committed
61
62
    using Notifier<Event>::notify;
    using Notifier<Events...>::notify;
63
64
65
  };

  template <class Event>
Praetorius, Simon's avatar
Praetorius, Simon committed
66
  class Notifier<Event>
67
68
  {
  public:
Praetorius, Simon's avatar
Praetorius, Simon committed
69
70
71
72
73
74
75
76
77
78
    /// Call the \ref update method on all attached observers.
    void notify(Event const& e)
    {
      for (ObserverInterface<Event>* o : observers_)
        o->update(e);
    }

    /// Attach a new observer that gets called on \ref notify
    void attach(ObserverInterface<Event>* o)
    {
Praetorius, Simon's avatar
Praetorius, Simon committed
79
      observers_.insert(o);
Praetorius, Simon's avatar
Praetorius, Simon committed
80
81
82
83
84
    }

    /// Detaches the passed observer from the list, if stored.
    void detach(ObserverInterface<Event>* o)
    {
Praetorius, Simon's avatar
Praetorius, Simon committed
85
      observers_.erase(o);
Praetorius, Simon's avatar
Praetorius, Simon committed
86
87
88
    }

  private:
Praetorius, Simon's avatar
Praetorius, Simon committed
89
    std::set<ObserverInterface<Event>*> observers_;
90
91
92
  };


Praetorius, Simon's avatar
Praetorius, Simon committed
93
94
  /// Implementation of the \ref ObserverInterface 
  template <class Event, class... Tags>
95
  class Observer
Praetorius, Simon's avatar
Praetorius, Simon committed
96
      : public ObserverInterface<Event>
97
98
  {
  public:
Praetorius, Simon's avatar
Praetorius, Simon committed
99
    template <class Notifier>
Praetorius, Simon's avatar
Praetorius, Simon committed
100
101
    Observer(std::shared_ptr<Notifier> notifier)
      : notifier_(std::const_pointer_cast<std::remove_const_t<Notifier>>(std::move(notifier)))
Praetorius, Simon's avatar
Praetorius, Simon committed
102
103
104
105
    {
      notifier_->attach(this);
    }

Praetorius, Simon's avatar
Praetorius, Simon committed
106
107
108
109
110
111
    template <class Notifier,
      class = void_t<decltype(std::declval<std::remove_const_t<Notifier>>().notify(std::declval<Event>()))> >
    Observer(Notifier& notifier)
      : Observer(Dune::stackobject_to_shared_ptr(notifier))
    {}

Praetorius, Simon's avatar
Praetorius, Simon committed
112
113
114
    /// Destructor, detaches from the notifier
    virtual ~Observer()
    {
Praetorius, Simon's avatar
Praetorius, Simon committed
115
116
      if (notifier_)
        notifier_->detach(this);
Praetorius, Simon's avatar
Praetorius, Simon committed
117
118
119
120
121
122
123
124
125
126
127
128
    }

    /// Copy constructor. Attaches this to the copied notifier
    Observer(Observer const& other)
      : notifier_(other.notifier_)
    {
      notifier_->attach(this);
    }

    /// Copy-assignment operator, copies the notifier and attaches this.
    Observer& operator=(Observer const& other)
    {
Praetorius, Simon's avatar
Praetorius, Simon committed
129
130
131
132
133
      if (&other != this) {
        notifier_->detach(this);
        notifier_ = other.notifier_;
        notifier_->attach(this);
      }
Praetorius, Simon's avatar
Praetorius, Simon committed
134
135
136
137
138
139
140
141
142
      return *this;
    }

    /// Implementation of the interface method \ref ObserverInterface::update.
    /// Redirects to the \ref updateImpl method with additional \ref Tags parameters
    void update(Event e) final
    {
      updateImpl(e, Tags{}...);
    }
143

Praetorius, Simon's avatar
Praetorius, Simon committed
144
145
146
147
148
  protected:
    /// \brief Implementation of the update method in derived class
    // NOTE: The additional `Tags...` arguments can be used to distinguish
    //       between multiple observers of the same event.
    virtual void updateImpl(Event e, Tags...) = 0;
149

Praetorius, Simon's avatar
Praetorius, Simon committed
150
  private:
Praetorius, Simon's avatar
Praetorius, Simon committed
151
    std::shared_ptr<Notifier<Event>> notifier_ = nullptr;
152
153
  };

Praetorius, Simon's avatar
Praetorius, Simon committed
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177

  namespace Impl 
  {
    template <class Event, class Tags>
    class ObserverSequenceImpl;

    /// Combination of multiple observers of the same event but with different tags
    template <class Event, std::size_t... Is>
    class ObserverSequenceImpl<Event, std::index_sequence<Is...>>
        : private Observer<Event,index_t<Is>>...
    {
    public:
      template <class... Notifiers,
        REQUIRES(sizeof...(Notifiers) == sizeof...(Is))>
      ObserverSequenceImpl(Notifiers&&... notifiers)
        : Observer<Event,index_t<Is>>(FWD(notifiers))...
      {}
    };

  } // end namespace Impl

  template <class Event, std::size_t N>
  using ObserverSequence = Impl::ObserverSequenceImpl<Event, std::make_index_sequence<N>>;

178
} // end namespace AMDiS