Anklang 0.3.0-460-gc4ef46ba
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
object.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "object.hh"
3#include "internal.hh"
4#include "utils.hh"
5#include "unicode.hh"
6#include "strings.hh"
7#include "main.hh"
8
9namespace Ase {
10
11// == EventConnection ==
12class EventConnection final {
14public:
15 const String selector_;
16 EventHandler handler_;
17 EventDispatcher *dispatcher_ = nullptr;
19 EventConnection (EventDispatcher &edispatcher, const String &eventselector, EventHandler handler) :
20 selector_ (eventselector), handler_ (handler), dispatcher_ (&edispatcher)
21 {}
22 void disconnect ();
23 bool connected () const { return nullptr != handler_; }
24 void
25 emit (const Event &event, const String &event_type, const String &detailedevent)
26 {
27 if (connected() && (selector_ == event_type || selector_ == detailedevent))
28 handler_ (event);
29 }
30};
31
32// == EventDispatcher ==
36 uint in_emission = 0;
37 bool needs_purging = false;
38 void
39 purge_connections ()
40 {
41 if (in_emission)
42 {
43 needs_purging = true;
44 return;
45 }
46 needs_purging = false;
47 for (size_t i = connections.size() - 1; i < connections.size(); i--)
48 {
49 EventConnectionP conp = connections[i].lock();
50 if (!conp || !conp->connected())
51 connections.erase (connections.begin() + i);
52 }
53 }
54 void
55 emit (const Event &event)
56 {
57 const String event_type = event.type();
58 assert_return (event_type.empty() == false);
59 const String event_detail = event.detail();
60 const String detailedevent = event_detail.empty() ? event_type : event_type + ":" + event_detail;
61 in_emission++;
62 for (size_t i = 0; i < connections.size(); i++)
63 {
64 EventConnectionP conp = connections[i].lock();
65 if (conp)
66 conp->emit (event, event_type, detailedevent);
67 }
68 in_emission--;
69 if (in_emission == 0 && needs_purging)
70 purge_connections();
71 }
73 {
74 assert_return (in_emission == 0);
75 in_emission++; // block purge_connections() calls
76 std::vector<EventConnectionW> old_connections;
77 std::swap (old_connections, connections);
78 for (auto &conw : old_connections)
79 {
80 EventConnectionP conp = conw.lock();
81 if (conp)
82 conp->disconnect();
83 }
84 in_emission--;
85 }
86};
87
88void
89EventConnection::disconnect ()
90{
91 const bool was_connected = connected();
92 if (was_connected)
93 {
94 handler_ = nullptr;
95 dispatcher_->purge_connections();
96 dispatcher_ = nullptr;
97 }
98}
99
100EventConnection::~EventConnection()
101{
102 handler_ = nullptr;
103 dispatcher_ = nullptr;
104}
105
106bool
107Emittable::Connection::connected () const
108{
109 const EventConnectionP &econp = *this;
110 return econp && econp->connected();
111}
112
113void
114Emittable::Connection::disconnect () const
115{
116 const EventConnectionP &econp = *this;
117 if (econp)
118 econp->disconnect();
119}
120
121// == CoalesceNotifies ==
122static CoalesceNotifies *coalesce_notifies_head = nullptr;
123
124CoalesceNotifies::CoalesceNotifies ()
125{
126 next_ = coalesce_notifies_head;
127 coalesce_notifies_head = this;
128}
129
130void
131CoalesceNotifies::flush_notifications ()
132{
133 while (!notifications_.empty())
134 {
135 NotificationSet notifications;
136 notifications.swap (notifications_);
137 for (const auto &ns : notifications)
138 if (ns.emittable)
139 ns.emittable->emit_event ("notify", ns.detail);
140 }
141}
142
143CoalesceNotifies::~CoalesceNotifies ()
144{
145 CoalesceNotifies **ptrp = &coalesce_notifies_head;
146 while (*ptrp != this && *ptrp)
147 ptrp = &(*ptrp)->next_;
148 assert_return (*ptrp);
149 *ptrp = this->next_;
150 flush_notifications();
151}
152
153size_t
154CoalesceNotifies::NotificationHash::operator() (const Notification &notification) const
155{
156 size_t h = std::hash<void*>() (&*notification.emittable);
157 h ^= std::hash<String>() (notification.detail);
158 return h;
159}
160
161// == EmittableImpl ==
163void
165{
166 EmittableP emittablep = this->weak_from_this().expired() ? nullptr : shared_ptr_cast<Emittable> (this);
167 if (emittablep && coalesce_notifies_head)
168 coalesce_notifies_head->notifications_.insert ({ emittablep, detail });
169 else
170 this->emit_event ("notify", detail);
171}
172
173EmittableImpl::~EmittableImpl()
174{
175 EventDispatcher *old = ed_;
176 ed_ = nullptr;
177 delete old;
178}
179
180Emittable::Connection
181EmittableImpl::on_event (const String &eventselector, const EventHandler &eventhandler)
182{
183 return_unless (!eventselector.empty() && eventhandler, {});
184 if (!ed_)
185 ed_ = new EventDispatcher();
186 EventConnectionP cptr = std::make_shared<EventConnection> (*ed_, eventselector, eventhandler);
187 ed_->connections.push_back (cptr);
188 return *static_cast<Connection*> (&cptr);
189}
190
191void
192EmittableImpl::emit_event (const String &type, const String &detail, const ValueR fields)
193{
194#ifndef NDEBUG
195 const char eventtype_chars[] = ASE_STRING_SET_ASCII_ALNUM "_";
196 for (size_t i = 0; type[i]; i++)
197 if (!strchr (eventtype_chars, type[i])) {
198 warning ("invalid characters in Event type: %s", type);
199 break;
200 }
201 for (size_t i = 0; detail[i]; i++)
202 if (!strchr (eventtype_chars, detail[i])) {
203 if (!string_is_ncname (detail))
204 warning ("invalid characters in Event detail: %s:%s", type, detail);
205 break;
206 }
207#endif // NDEBUG
208 return_unless (ed_!= nullptr);
209 Event ev { type, detail };
210 for (auto &&e : fields)
211 if (e.name != "type" && e.name != "detail")
212 ev.push_back (e);
213 ed_->emit (ev); // emit detailedevent="notify:detail" as type="notify" detail="detail"
214}
215
216// == Emittable ==
217void
218Emittable::js_trigger (const String &eventselector, JsTrigger trigger)
219{
220 return_unless (trigger);
221 Emittable::Connection econ = on_event (eventselector, trigger);
222 trigger.ondestroy ([econ] () { econ.disconnect(); }); // must avoid strong references
223}
224
225// == ObjectImpl ==
226ObjectImpl::~ObjectImpl()
227{}
228
229// == Object ==
230Object::~Object()
231{}
232
233} // Ase
void emit_notify(const String &detail) override
Emit notify:detail, multiple notifications maybe coalesced if a CoalesceNotifies instance exists.
Definition object.cc:164
T empty(T... args)
T insert(T... args)
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
Definition internal.hh:71
The Anklang C++ API namespace.
Definition api.hh:9
bool string_is_ncname(const String &input)
Definition unicode.cc:325
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
uint32_t uint
Provide 'uint' as convenience type.
Definition cxxaux.hh:18
Structure for callback based notifications.
Definition value.hh:113
T swap(T... args)