Libmacro  0.2
Libmacro is an extensible macro and hotkey library.
dispatch_receiver_map.h
Go to the documentation of this file.
1 /* Libmacro - A multi-platform, extendable macro and hotkey C library
2  Copyright (C) 2013 Jonathan Pelletier, New Paradigm Software
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Lesser General Public
6  License as published by the Free Software Foundation; either
7  version 2.1 of the License, or (at your option) any later version.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public
15  License along with this library; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18 
22 #ifndef __cplusplus
23  #pragma message "C++ support is required for extras module"
24  #include "mcr/err.h"
25 #endif
26 
27 #ifndef MCR_EXTRAS_DISPATCH_RECEIVER_MAP_H_
28 #define MCR_EXTRAS_DISPATCH_RECEIVER_MAP_H_
29 
31 
32 #include <algorithm>
33 #include <functional>
34 #include <map>
35 #include <vector>
36 
37 namespace mcr
38 {
43 template<typename kT>
45 {
46 public:
48  std::binary_function<mcr_ReceiverMapElement, mcr_ReceiverMapElement, bool> {
49  bool operator()(const mcr_ReceiverMapElement &s1, const mcr_ReceiverMapElement &s2) const
50  {
51  return reinterpret_cast<const kT &>(s1) < reinterpret_cast<const kT &>(s2);
52  }
53  };
54 
55  DispatchReceiverMap(mcr_ReceiverMapElement **applyReceiversPt = nullptr,
56  size_t *applyCountPt = nullptr)
57  : _applyReceiversPt(applyReceiversPt), _applyCountPt(applyCountPt)
58  {
59  }
61  : _applyReceiversPt(nullptr), _applyCountPt(0),
62  _receiverMap(copytron._receiverMap),
63  _receivers(copytron._receivers)
64  {
65  updateVectors();
66  apply();
67  }
69  {
70  apply(nullptr, 0);
71  }
72  DispatchReceiverMap &operator =(const DispatchReceiverMap &copytron)
73  {
74  if (&copytron != this) {
75  apply(nullptr, 0);
76  _receiverMap = copytron._receiverMap;
77  _receivers = copytron._receivers;
78  updateVectors();
79  apply();
80  }
81  return *this;
82  }
83 
84  void add(const kT &key, void *receiver, mcr_dispatch_receive_fnc receiverFnc)
85  {
86  auto mapIt = _receiverMap.find(key);
87  // vector is in map (or insert)
88  if (mapIt == _receiverMap.end()) {
89  // return pair<iterator, bool>
90  auto success = _receiverMap.insert({key, {}});
91  mcr_throwif(!success.second, errno);
92  mapIt = success.first;
93  }
94  // insert receiver into map's sorted vector
95  DispatchReceiverSet &receiverSet = mapIt->second;
96  receiverSet.add(receiver, receiverFnc);
97  updateVector(key, receiverSet.array(), receiverSet.count());
98  apply();
99  }
100  void clear()
101  {
102  apply(nullptr, 0);
103  _receiverMap.clear();
104  _receivers.clear();
105  }
106  void remove(void *remReceiver)
107  {
108  apply(nullptr, 0);
109  // In map, for each sorted vector search and remove receiver
110  for (auto rit = _receiverMap.rbegin(); rit != _receiverMap.rend(); ++rit) {
111  DispatchReceiverSet &receiverSet = rit->second;
112  if (receiverSet.empty()) {
113  /* This set is empty, remove from map and vector. */
114  removeKey(rit->first);
115  _receiverMap.erase(rit.base());
116  } else if (receiverSet.find(remReceiver)) {
117  /* Has receiver to remove... */
118  receiverSet.remove(remReceiver);
119  /* Now empty remove, else update vector array */
120  if (receiverSet.empty()) {
121  removeKey(rit->first);
122  _receiverMap.erase(rit.base());
123  } else {
124  /* Note: Receiver is likely only registered once, so
125  * updating one vector is heuristically more efficient than
126  * everything every time. */
127  updateVector(rit->first, receiverSet.array(), receiverSet.count());
128  }
129  }
130  /* else not found in current set, move to next. */
131  }
132  apply();
133  }
134  void trim()
135  {
136  for (auto it = _receiverMap.begin(); it != _receiverMap.end(); it++) {
137  it->second.trim();
138  }
139  /* Vector references may have changed. */
140  updateVectors();
141  _receivers.shrink_to_fit();
142  apply();
143  }
144 
145  mcr_ReceiverMapElement *array()
146  {
147  return _receivers.empty() ? nullptr : &_receivers.front();
148  }
149  size_t count() const
150  {
151  return _receiverMap.size();
152  }
153  inline size_t size() const
154  {
155  return count();
156  }
159  void apply()
160  {
161  apply(array(), count());
162  }
165  void apply(mcr_ReceiverMapElement *receivers, size_t count)
166  {
167  if (applicable()) {
168  *_applyReceiversPt = receivers;
169  *_applyCountPt = count;
170  }
171  }
172 
173  mcr_ReceiverMapElement **applyReceiversPt() const
174  {
175  return _applyReceiversPt;
176  }
177  void setApplyReceiversPt(mcr_ReceiverMapElement **applyReceiversPt)
178  {
179  if (applyReceiversPt != _applyReceiversPt) {
180  apply(nullptr, 0);
181  _applyReceiversPt = applyReceiversPt;
182  apply();
183  }
184  }
185  size_t *applyCountPt() const
186  {
187  return _applyCountPt;
188  }
189  void setApplyCountPt(size_t *applyCountPt)
190  {
191  if (applyCountPt != _applyCountPt) {
192  apply(nullptr, 0);
193  _applyCountPt = applyCountPt;
194  apply();
195  }
196  }
197  inline bool applicable() const
198  {
199  return _applyReceiversPt && _applyCountPt;
200  }
201  void setApplyReceivers(mcr_ReceiverMapElement **applyReceiversPt,
202  size_t *applyCountPt)
203  {
204  if (applyReceiversPt != _applyReceiversPt ||
205  applyCountPt != _applyCountPt ) {
206  apply(nullptr, 0);
207  _applyReceiversPt = applyReceiversPt;
208  _applyCountPt = applyCountPt;
209  apply();
210  }
211  }
212 private:
213  mcr_ReceiverMapElement **_applyReceiversPt;
214  size_t *_applyCountPt;
215 
216  std::map<kT, DispatchReceiverSet> _receiverMap;
217  std::vector<mcr_ReceiverMapElement> _receivers;
218 
219  void removeKey(const kT &key)
220  {
221  mcr_ReceiverMapElement insert;
222  reinterpret_cast<kT &>(insert) = key;
223  auto found = std::lower_bound(_receivers.begin(), _receivers.end(), insert,
225  if (found != _receivers.end())
226  _receivers.erase(found);
227  }
228  void updateVector(const kT &key, mcr_DispatchReceiver *array, size_t count)
229  {
230  mcr_ReceiverMapElement insert;
231  reinterpret_cast<kT &>(insert) = key;
232  auto found = std::lower_bound(_receivers.begin(), _receivers.end(), insert,
234  /* Not found, create ref. */
235  if (found == _receivers.end() || compare(&*found, &key)) {
236  insert.receivers = array;
237  insert.receiver_count = count;
238  _receivers.insert(found, insert);
239  } else {
240  found->receivers = array;
241  found->receiver_count = count;
242  }
243  }
244  void updateVectors()
245  {
246  _receivers.resize(_receiverMap.size());
247  auto receiverArray = _receivers.begin();
248  for (auto it = _receiverMap.begin(); it != _receiverMap.end();
249  it++, receiverArray++) {
250  reinterpret_cast<kT &>(*receiverArray) = it->first;
251  receiverArray->receivers = it->second.array();
252  receiverArray->receiver_count = it->second.count();
253  }
254  }
255 
256  static int compare(const void *lhsPt, const void *rhsPt)
257  {
258  if (rhsPt) {
259  if (lhsPt)
260  return MCR_CMP_PTR(const kT, lhsPt, rhsPt);
261  return -1;
262  }
263  return !!lhsPt;
264  }
265 };
266 }
267 
268 #endif
#define mcr_throwif(condition, errorNumber)
Definition: defines.h:415
size_t receiver_count
Definition: types.h:91
bool(* mcr_dispatch_receive_fnc)(struct mcr_DispatchReceiver *dispatchReceiver, struct mcr_Signal *dispatchSignal, unsigned int mods)
Definition: types.h:50
#define MCR_CMP_PTR(T, lhsPtr, rhsPtr)
Definition: defines.h:218
Raise a compiler error. Usage: #include "mcr/err.h"
struct mcr_DispatchReceiver * receivers
Definition: types.h:85
Libmacro, by Jonathan Pelletier, New Paradigm Software. Alpha version.
Definition: classes.h:31
void apply(mcr_ReceiverMapElement *receivers, size_t count)