gphoto2pp
A C++ Wrapper for libgphoto2
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
observer.hpp
Go to the documentation of this file.
1 
26 #ifndef OBSERVER_HPP
27 #define OBSERVER_HPP
28 
29 #include <vector>
30 #include <map>
31 #include <memory>
32 #include <functional>
33 #include <algorithm>
34 #include <stdexcept>
35 
36 namespace gphoto2pp
37 {
38  namespace observer
39  {
40  // Registration is just a scoped type, once it goes out of scope,
41  // your registration has expired. The idea is that you store this
42  // registration as a member of the actual observer, tying it to
43  // the same lifetime.
44  using Registration = std::shared_ptr<void>;
45 
46 
47  // Nothing to see here, the detail namespace is used for just that:
48  // implementation details
49  namespace detail
50  {
51  // We want to create a base class for handling all the observer
52  // boilerplate, because of that we need to store 'untyped' pointers.
53  using UniversalPtr = std::shared_ptr<void>;
54 
55  // In this implementation we often need to know if an object is alive or not.
56  // The way we do that is by using shared_ptr and weak_ptr. Often we do not
57  // even care to store anything at all, we're just interested in using the
58  // deterministic destructor (RAII). This function creates a 'dangerous'
59  // shared_ptr because it points to 'DEADC0DE', this avoids and extra 'new',
60  // we could have used make_shared but that does not allow a customer deleter.
61  // Note that the deleter should not free any memory since we do not actually
62  // allocate anything.
63  template <typename Deleter>
64  UniversalPtr createEmptyPtr(Deleter deleter)
65  {
66  return UniversalPtr((void*) 0xDEADC0DE, deleter);
67  }
68 
69  // Slap the baby's bottom. This functions creates a complete no-op shared_ptr
70  // which we use as the 'heartbeat' for the Subject
71  inline UniversalPtr createHeartBeat()
72  {
73  return createEmptyPtr([] (void*) {});
74  }
75 
76  // A base class for handling common code in all Subjects. Normally using
77  // inheritance for code reuse is bad but in this case it is a common idiom
78  // to avoid 'code bloat' due to the templating. I.e., if we would leave
79  // this code in the actualy template class it would needlessly get replicated
80  // for each instantiation with a different type.
82  {
83  protected:
84  SubjectBase(): heartBeat_(createHeartBeat()) {}
85 
86  Registration registerObserver(UniversalPtr fptr)
87  {
88  observers_.push_back(fptr);
89  std::weak_ptr<UniversalPtr::element_type> weakHeartBeat(heartBeat_);
90 
91  // we pass the function pointer and a weak_ptr into this lambda by
92  // value. This is important because it lets the observer know if the
93  // subject is still alive at the moment of unregistering.
94  // This is basically the Registration, it's a placeholder that removes
95  // the observer from the list when the registration goes out of scope.
96  return createEmptyPtr([fptr, weakHeartBeat, this] (void*)
97  {
98  if (auto isBeating = weakHeartBeat.lock())
99  {
100  observers_.erase(std::remove(begin(observers_), end(observers_), fptr), end(observers_));
101  }
102  });
103  }
104 
105  std::vector<UniversalPtr> observers_;
106 
107  private:
108  UniversalPtr heartBeat_;
109  };
110 
111  template<typename EventTypeBase>
113  {
114  protected:
115  SubjectBaseEvent(): heartBeat_(createHeartBeat()) {}
116 
117  Registration registerObserver(const EventTypeBase& e, UniversalPtr fptr)
118  {
119  observers_[e].push_back(fptr);
120  std::weak_ptr<UniversalPtr::element_type> weakHeartBeat(heartBeat_);
121 
122  // we pass the function pointer and a weak_ptr into this lambda by
123  // value. This is important because it lets the observer know if the
124  // subject is still alive at the moment of unregistering.
125  // This is basically the Registration, it's a placeholder that removes
126  // the observer from the list when the registration goes out of scope.
127  return createEmptyPtr([e, fptr, weakHeartBeat, this] (void*)
128  {
129  if (auto isBeating = weakHeartBeat.lock())
130  {
131  observers_[e].erase(std::remove(begin(observers_[e]), end(observers_[e]), fptr), end(observers_[e]));
132  }
133  });
134  }
135 
136  std::map<EventTypeBase, std::vector<UniversalPtr>> observers_;
137 
138  private:
139  UniversalPtr heartBeat_;
140  };
141 
142  } // eons detail
143 
144  // We need this otherwise we cannot explicitly specialise for the funtion
145  // signature later
146  template <typename SignatureFunc>
147  struct Subject;
148 
149  // The Subject class is specialised on the type of 'notification' you
150  // want to send to the observers. Like Registration the Subject objects
151  // should be tied to the lifetime of what is the real subject, preferably as
152  // a member variable.
153  template <typename Return, typename... Params>
154  struct Subject<Return (Params...)> : detail::SubjectBase
155  {
156  using F = std::function<Return (Params...)>;
157  using FPtr = std::shared_ptr<F>;
158 
159  void operator()(Params... params)
160  {
161  for (auto const & observer : observers_)
162  {
163  FPtr const fptr = std::static_pointer_cast<F>(observer);
164  (*fptr)(params...);
165  }
166  }
167 
168  Registration registerObserver(F f)
169  {
170  FPtr fptr(new F(std::move(f)));
171  return SubjectBase::registerObserver(fptr);
172  }
173 
174  };
175 
176  template <typename SignatureEventType, typename SignatureFunc>
177  struct SubjectEvent;
178 
179  template <typename EventType, typename Return, typename... Params>
180  struct SubjectEvent<EventType, Return (Params...)> : detail::SubjectBaseEvent<EventType>
181  {
182  public:
183  using F = std::function<Return (Params...)>;
184  using FPtr = std::shared_ptr<F>;
185 
186  void operator()(EventType const & e, Params... params)
187  {
188  try
189  {
190  for (auto const & observer : this->observers_.at(e))
191  {
192  FPtr const fptr = std::static_pointer_cast<F>(observer);
193  (*fptr)(params...);
194  }
195  }
196  catch(std::out_of_range const & e)
197  {
198  // Supress this, means our std::map .at() method doesn't have an event of this type
199  }
200  }
201 
202  Registration registerObserver(EventType const & e, F f)
203  {
204  FPtr fptr(new F(std::move(f)));
206  }
207  };
208  } // eons obs
209 } // eons gphoto2pp
210 
211 #endif // OBSERVER_HPP