-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhandlers.h
190 lines (152 loc) · 5.15 KB
/
handlers.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/**
* COPYRIGHT 2014 (C) Jason Volk
* COPYRIGHT 2014 (C) Svetlana Tkachenko
*
* DISTRIBUTED UNDER THE GNU GENERAL PUBLIC LICENSE (GPL) (see: LICENSE)
*/
enum Special
{
ALL, // Called on anything no matter what
MISS, // Called if no mapped handlers found (one-times discarded even if not called)
_NUM_SPECIAL
};
template<class Handler>
class Handlers
{
template<class T> static std::string name_cast(const T &num);
std::multimap<std::string, Handler> handlers;
std::vector<std::list<Handler>> specials { _NUM_SPECIAL };
public:
template<class... Args> auto &add(const Special &special, Args&&... args);
template<class... Args> auto &add(const std::string &event, Args&&... args);
template<class... Args> auto &add(const char *const &event, Args&&... args);
template<class T, class... Args> auto &add(const T &num, Args&&... args);
template<class... Args> void operator()(const std::string &name, Args&&... args);
template<class... Args> void operator()(const Msg &msg, Args&&... args);
template<class T, class... Args> void operator()(const T &num, Args&&... args);
void clear(const std::string &event) { handlers.erase(event); }
void clear(const Special &special) { specials[special].clear(); }
void clear(const Prio &prio); // clears handlers by priority num
void clear_handlers() { handlers.clear(); }
void clear_specials(); // clears all Special handlers
void clear(); // clears everything
};
template<class Handler>
void Handlers<Handler>::clear()
{
clear_specials();
clear_handlers();
}
template<class Handler>
void Handlers<Handler>::clear_specials()
{
for(auto &s : specials)
s.clear();
}
template<class Handler>
void Handlers<Handler>::clear(const Prio &prio)
{
for(auto it(handlers.begin()); it != handlers.end(); )
if(it->second.get_prio() == prio)
handlers.erase(it++);
else
++it;
for(auto &spec : specials)
spec.remove_if([&prio](const Handler &handler)
{
return handler.get_prio() == prio;
});
}
template<class Handler>
template<class T,
class... Args>
void Handlers<Handler>::operator()(const T &num,
Args&&... args)
{
const auto name = name_cast(num);
operator()(name,std::forward<Args>(args)...);
}
template<class Handler>
template<class... Args>
void Handlers<Handler>::operator()(const Msg &msg,
Args&&... args)
{
const auto &name = msg.get_name();
operator()(name,msg,std::forward<Args>(args)...);
}
template<class Handler>
template<class... Args>
void Handlers<Handler>::operator()(const std::string &name,
Args&&... args)
{
// Find out how many handlers will be called
const auto itp = handlers.equal_range(name);
const size_t itp_sz = std::distance(itp.first,itp.second);
const size_t vec_sz = specials[ALL].size() + (itp_sz? itp_sz : specials[MISS].size());
// Gather pointers to all relevant handlers
std::vector<const Handler *> vec(vec_sz);
auto vit = pointers(specials[ALL].begin(),specials[ALL].end(),vec.begin());
if(itp_sz)
std::transform(itp.first,itp.second,vit,[](const auto &vt) { return &vt.second; });
else
pointers(specials[MISS].begin(),specials[MISS].end(),vit);
// Sort calling order based on handler priority
std::sort(vec.begin(),vec.end(),[]
(const auto &a, const auto &b)
{
return a->get_prio() < b->get_prio();
});
// Call handlers
for(const Handler *const &handler : vec)
(*handler)(std::forward<Args>(args)...);
// Erase one-time mapped handlers
for(auto it = itp.first; it != itp.second; )
if(!it->second.is(RECURRING))
handlers.erase(it++);
else
++it;
// Erase one-time Special handlers (note: one-time MISS handlers erased even if never called)
for(auto &s : specials)
s.remove_if([](const auto &handler) { return !handler.is(RECURRING); });
}
template<class Handler>
template<class T,
class... Args>
auto &Handlers<Handler>::add(const T &num,
Args&&... args)
{
const auto event(name_cast(num));
return add(event,std::forward<Args>(args)...);
}
template<class Handler>
template<class... Args>
auto &Handlers<Handler>::add(const std::string &event,
Args&&... args)
{
auto iit(handlers.emplace(event,Handler{std::forward<Args>(args)...}));
return iit->second;
}
template<class Handler>
template<class... Args>
auto &Handlers<Handler>::add(const char *const &event,
Args&&... args)
{
auto iit(handlers.emplace(event,Handler{std::forward<Args>(args)...}));
return iit->second;
}
template<class Handler>
template<class... Args>
auto &Handlers<Handler>::add(const Special &spec,
Args&&... args)
{
specials.at(spec).emplace_back(Handler{std::forward<Args>(args)...});
return specials.at(spec).back();
}
template<class Handler>
template<class T>
std::string Handlers<Handler>::name_cast(const T &num)
{
std::stringstream name;
name << std::setw(3) << std::setfill('0') << ssize_t(num);
return name.str();
}