1 #pragma once
2 
3 #include <algorithm> // for std::remove_if
4 #include <cinttypes>
5 #include <memory>
6 #include <utility>
7 #include <vector>
8 
9 namespace scx {
10 
11 namespace signal {
12 
13 template<class>
14 class MuteSignal;
15 
16 template<class R, class... P>
17 class MuteSignal<R(P...)>
18 {
19     using RawFunction = R (*)(P...);
20 
21   private:
22     struct Slot;
23 
24   public:
25     MuteSignal() = default;
26     ~MuteSignal() = default;
27 
28     MuteSignal(const MuteSignal&) = delete;
29     MuteSignal(MuteSignal&&) = delete;
30     MuteSignal& operator=(const MuteSignal&) = delete;
31     MuteSignal& operator=(MuteSignal&&) = delete;
32 
33     template<class... Args>
Connect(Args &&...args)34     uintptr_t Connect(Args&&... args)
35     {
36         auto slot = MakeSlot(std::forward<Args>(args)...);
37         auto id = reinterpret_cast<uintptr_t>(slot.get());
38         _slots.push_back(std::move(slot));
39         return id;
40     }
41 
Disconnect(uintptr_t id)42     bool Disconnect(uintptr_t id)
43     {
44         for (size_t i = 0; i < _slots.size(); ++i) {
45             auto thatId = reinterpret_cast<uintptr_t>(_slots[i].get());
46             if (thatId == id) {
47                 _slots.erase(_slots.begin() + i);
48                 return true;
49             }
50         }
51         return false;
52     }
53 
Disconnect(RawFunction fn)54     bool Disconnect(RawFunction fn)
55     {
56         bool hit = false;
57         EraseIf([&hit, fn](const auto& slot) {
58             if (slot->type == SlotType::RawFunction) {
59                 auto that = static_cast<RawFunctionSlot*>(slot.get());
60                 if (that->fn == fn) {
61                     hit = true;
62                     return true;
63                 }
64             }
65             return false;
66         });
67         return hit;
68     }
69 
70     template<class O>
Disconnect(O * obj)71     bool Disconnect(O* obj)
72     {
73         bool hit = false;
74         EraseIf([&hit, obj](const auto& slot) {
75             if (slot->type == SlotType::MemberFunction) {
76                 auto that = dynamic_cast<MemberFunctionSlot<O>*>(slot.get());
77                 if (that && that->obj == obj) {
78                     hit = true;
79                     return true;
80                 }
81             }
82             return false;
83         });
84         return hit;
85     }
86 
87     template<class F, class O>
Disconnect(F fn,O * obj)88     bool Disconnect(F fn, O* obj)
89     {
90         bool hit = false;
91         EraseIf([&hit, fn, obj](const auto& slot) {
92             if (slot->type == SlotType::MemberFunction) {
93                 auto that = dynamic_cast<SignedMemberFunctionSlot<F, O>*>(slot.get());
94                 if (that && that->obj == obj && that->fn == fn) {
95                     hit = true;
96                     return true;
97                 }
98             }
99             return false;
100         });
101         return hit;
102     }
103 
Clear()104     void Clear() { _slots.clear(); }
105 
Empty()106     auto Empty() const { return _slots.empty(); }
107 
Size()108     auto Size() const { return _slots.size(); }
109 
110   protected:
111     std::vector<std::unique_ptr<Slot>> _slots;
112 
113   private:
114     template<typename Pred>
EraseIf(Pred && pred)115     void EraseIf(Pred&& pred)
116     {
117         _slots.erase(std::remove_if(_slots.begin(), _slots.end(), std::forward<Pred>(pred)), _slots.end());
118     }
119 
120     enum class SlotType : uint8_t
121     {
122         Function,
123         RawFunction,
124         MemberFunction
125     };
126 
127     struct Slot
128     {
SlotSlot129         explicit Slot(SlotType type)
130           : type(type)
131         {
132         }
133 
~SlotSlot134         virtual ~Slot() {}
135         virtual R Invoke(P...) const = 0;
136 
137         const SlotType type;
138     };
139 
140     template<class F>
141     struct FunctionSlot : public Slot
142     {
FunctionSlotFunctionSlot143         FunctionSlot(const F& fn)
144           : Slot(SlotType::Function)
145           , fn(fn)
146         {
147         }
148 
FunctionSlotFunctionSlot149         FunctionSlot(F&& fn)
150           : Slot(SlotType::Function)
151           , fn(std::move(fn))
152         {
153         }
154 
InvokeFunctionSlot155         R Invoke(P... p) const final { return fn(p...); }
156 
157         F const fn;
158     };
159 
160     struct RawFunctionSlot : public Slot
161     {
RawFunctionSlotRawFunctionSlot162         explicit RawFunctionSlot(RawFunction fn)
163           : Slot(SlotType::RawFunction)
164           , fn(fn)
165         {
166         }
167 
InvokeRawFunctionSlot168         R Invoke(P... p) const final { return fn(p...); }
169 
170         RawFunction const fn;
171     };
172 
173     template<class O>
174     struct MemberFunctionSlot : public Slot
175     {
MemberFunctionSlotMemberFunctionSlot176         explicit MemberFunctionSlot(O* obj)
177           : Slot(SlotType::MemberFunction)
178           , obj(obj)
179         {
180         }
181 
182         O* const obj;
183     };
184 
185     template<class F, class O>
186     struct SignedMemberFunctionSlot : public MemberFunctionSlot<O>
187     {
SignedMemberFunctionSlotSignedMemberFunctionSlot188         SignedMemberFunctionSlot(F fn, O* obj)
189           : MemberFunctionSlot<O>(obj)
190           , fn(fn)
191         {
192         }
193 
InvokeSignedMemberFunctionSlot194         R Invoke(P... p) const final { return (this->obj->*fn)(p...); }
195 
196         F const fn;
197     };
198 
MakeSlot(RawFunction f)199     static auto MakeSlot(RawFunction f) { return std::make_unique<RawFunctionSlot>(f); }
200 
201     template<class F, class O>
MakeSlot(F f,O * o)202     static auto MakeSlot(F f, O* o)
203     {
204         return std::make_unique<SignedMemberFunctionSlot<F, O>>(f, o);
205     }
206 
207     template<class F>
MakeSlot(F && f)208     static auto MakeSlot(F&& f)
209     {
210         return std::make_unique<FunctionSlot<std::decay_t<F>>>(std::forward<F>(f));
211     }
212 };
213 
214 template<class R>
215 class LastValueCollector
216 {
217   public:
218     void operator+=(const R& val) { this->val = val; }
219 
220     void operator+=(R&& val) { this->val = std::move(val); }
221 
operator()222     R& operator()() { return val; }
223 
224   private:
225     R val;
226 };
227 
228 } // namespace signal
229 
230 template<class, template<class> class Collector = signal::LastValueCollector>
231 class Signal;
232 
233 template<class R, class... P, template<class> class Collector>
234 class Signal<R(P...), Collector> : public signal::MuteSignal<R(P...)>
235 {
236   public:
operator()237     R operator()(P... p) const
238     {
239         Collector<R> collector;
240         for (size_t i = 0; i < this->_slots.size(); ++i) {
241             collector += this->_slots[i]->Invoke(p...);
242         }
243         return std::move(collector());
244     }
245 };
246 
247 template<class... P>
248 class Signal<void(P...)> : public signal::MuteSignal<void(P...)>
249 {
250   public:
operator()251     void operator()(P... p) const
252     {
253         for (size_t i = 0; i < this->_slots.size(); ++i) {
254             this->_slots[i]->Invoke(p...);
255         }
256     }
257 };
258 
259 } // namespace scx
260