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

« « « Anklang Documentation
Loading...
Searching...
No Matches
gadget.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 "gadget.hh"
3#include "jsonipc/jsonipc.hh"
4#include "utils.hh"
5#include "project.hh"
6#include "serialize.hh"
7#include "internal.hh"
8#include "randomhash.hh"
9
10namespace Ase {
11
12// == Gadget ==
13Gadget::Gadget() :
14 name (this, "name")
15{}
16
17// == GadgetImpl ==
18GadgetImpl::~GadgetImpl()
19{}
20
22GadgetImpl::gadget_flags (uint64_t setbits, uint64_t mask)
23{
24 gadget_flags_ &= mask;
25 gadget_flags_ |= setbits;
26 return gadget_flags_;
27}
28
29void
30GadgetImpl::_set_parent (GadgetImpl *parent)
31{
32 if (parent)
33 assert_return (parent_ == nullptr);
34 else // !parent
35 assert_return (parent_ != nullptr);
36 parent_ = parent;
37}
38
40GadgetImpl::fallback_name () const
41{
42 return type_nick();
43}
44
45String
46GadgetImpl::canonify_key (const String &input)
47{
48 String key = string_canonify (input, string_set_a2z() + string_set_A2Z() + "_0123456789.", "_");
49 if (key.size() && key[0] == '.')
50 key = "_" + key;
51 return key;
52}
53
54Value
55GadgetImpl::get_data (const String &key) const
56{
57 const String ckey = canonify_key (key);
58 return session_data_[ckey];
59}
60
61bool
62GadgetImpl::set_data (const String &key, const Value &v)
63{
64 const String ckey = canonify_key (key);
65 return_unless (ckey.size(), false);
66 session_data_[ckey] = v;
67 emit_event ("data", ckey);
68 return true;
69}
70
71void
72GadgetImpl::serialize (WritNode &xs)
73{
74 // name
75 String current_name = name();
76 if (xs.in_save() && current_name != fallback_name())
77 xs["name"] & current_name;
78 if (xs.in_load() && xs.has ("name"))
79 {
80 String new_name;
81 xs["name"] & new_name;
82 if (current_name != new_name) // avoid fixating a fallback
83 name (new_name);
84 }
85 // Serializable
86 Serializable::serialize (xs);
87 // properties
88 for (PropertyP p : access_properties())
89 {
90 const String hints = p->hints();
91 if (!string_option_check (hints, "S"))
92 continue;
93 if (xs.in_save() && string_option_check (hints, "r"))
94 {
95 Value v = p->get_value();
96 xs[p->ident()] & v;
97 }
98 if (xs.in_load() && string_option_check (hints, "w") && xs.has (p->ident()))
99 {
100 Value v;
101 xs[p->ident()] & v;
102 p->set_value (v);
103 }
104 }
105 // data
106 if (xs.in_save())
107 {
108 ValueR cdata;
109 for (const ValueField &f : session_data_)
110 if (f.name[0] != '_' && f.value)
111 cdata[f.name] = *f.value;
112 if (cdata.size())
113 xs["custom_data"] & cdata;
114 }
115 if (xs.in_load())
116 {
117 ValueR cdata;
118 xs["custom_data"] & cdata;
119 for (const ValueField &f : cdata)
120 if (f.value)
121 set_data (f.name, *f.value);
122 }
123}
124
125String
126GadgetImpl::type_nick () const
127{
128 String tname = Jsonipc::rtti_typename (*this);
129 ssize_t colon = tname.rfind (':');
130 if (colon != ssize_t (tname.npos))
131 tname = tname.substr (colon + 1);
132 if (string_endswith (tname, "Impl"))
133 tname = tname.substr (0, tname.size() - 4);
134 return tname;
135}
136
137static CustomDataKey<String> gadget_name_key;
138
139String
140GadgetImpl::get_name () const
141{
142 if (!has_custom_data (&gadget_name_key))
143 return fallback_name();
144 return get_custom_data (&gadget_name_key);
145}
146
147void
148GadgetImpl::set_name (const std::string &n)
149{
150 String newname = string_strip (n);
151 if (newname.empty())
152 del_custom_data (&gadget_name_key);
153 else
154 set_custom_data (&gadget_name_key, newname);
155 name.notify();
156}
157
158PropertyS
159GadgetImpl::access_properties ()
160{
161 if (props_.empty())
162 create_properties();
163 return { begin (props_), end (props_) };
164}
165
166// == Gadget ==
168Gadget::_project() const
169{
170 Gadget *last = const_cast<Gadget*> (this);
171 for (Gadget *parent = last->_parent(); parent; parent = last->_parent())
172 last = parent;
173 return dynamic_cast<ProjectImpl*> (last);
174}
175
177Gadget::list_properties ()
178{
179 PropertyS props = access_properties();
180 StringS names;
181 names.reserve (props.size());
182 for (const PropertyP &prop : props)
183 names.push_back (prop->ident());
184 return names;
185}
186
187PropertyP
188Gadget::access_property (String ident)
189{
190 for (const auto &p : access_properties())
191 if (p->ident() == ident)
192 return p;
193 return {};
194}
195
196Value
197Gadget::get_value (String ident)
198{
199 PropertyP prop = access_property (ident);
200 return prop ? prop->get_value() : Value {};
201}
202
203bool
204Gadget::set_value (String ident, const Value &v)
205{
206 PropertyP prop = access_property (ident);
207 return prop && prop->set_value (v);
208}
209
211 const char *member_typeid_name = nullptr;
212 ptrdiff_t memb_offset = -1;
214 GadgetImpl::MemberInfosP infosp = nullptr;
216 uint64_t flags = 0;
217};
218
220 /*key*/
221 const char *class_typeid_name = nullptr;
222 /*payload*/
223 GadgetImpl::MemberClassT classtest = nullptr;
225};
226
227static auto&
228cml_set()
229{
230 static auto gcml_hash = [] (const GadgetClassMemberList &m) {
231 return fnv1a_consthash64 (m.class_typeid_name);
232 };
233 static auto gcml_equal = [] (const GadgetClassMemberList &a, const GadgetClassMemberList &b) {
234 return !strcmp (a.class_typeid_name, b.class_typeid_name);
235 };
236 using MemberAccessorSet = std::unordered_set<GadgetClassMemberList, decltype (gcml_hash), decltype (gcml_equal)>;
237 static MemberAccessorSet mas (0, gcml_hash, gcml_equal);
238 return mas;
239}
240
241bool
242GadgetImpl::requires_accessor (const char *ot, const char *mt, ptrdiff_t offset)
243{
244 auto &cml = cml_set();
245 const GadgetClassMemberList key { .class_typeid_name = ot };
246 auto it = cml.find (key);
247 if (it != cml.end())
248 for (const MemberAccessor *maf : it->members)
249 if (!strcmp (mt, maf->member_typeid_name)) {
250 assert_return (maf->memb_offset == offset, false);
251 return false;
252 }
253 return true;
254}
255
256void
257GadgetImpl::register_accessor (const char *ot, const char *mt, ptrdiff_t offset, MemberClassT classtest,
258 const Param::ExtraVals &ev, MemberAccessF &&accessfunc,
259 MemberInfosP infosp, uint64_t flags)
260{
261 auto &cml = cml_set();
262 auto [celement, inserted] = cml.emplace (ot, classtest);
263 assert_return (celement != cml.end());
264 GadgetClassMemberList *element = const_cast<GadgetClassMemberList*> (&*celement);
265 element->members.push_back (new MemberAccessor {mt, offset, std::move (accessfunc), infosp, ev, flags});
266 //printerr ("%s: %s+%s=%+zd\n", __func__, ot, mt, offset);
267}
268
269void
270GadgetImpl::create_properties ()
271{
272 /* When creating the properties for an instance, walk through the known
273 * GadgetClassMemberList entries, test each via dynamic_cast and thus
274 * identify all class member lists that match the instance.
275 */
276 auto &cml = cml_set();
277 for (const GadgetClassMemberList &ml : cml)
278 if (ml.classtest (*this))
279 for (const MemberAccessor *m : ml.members) {
280 PropertyGetter getter = [this,m] (Value &value) { m->func (this, nullptr, &value); };
281 PropertySetter setter = [this,m] (const Value &value) { return m->func (this, &value, nullptr); };
282 PropertyLister lister = nullptr;
283 StringS infos = m->infosp();
284 String hints = kvpairs_fetch (infos, "hints");
285 if (m->flags & MemberDetails::READABLE)
286 hints += ":r";
287 if (m->flags & MemberDetails::WRITABLE)
288 hints += ":w";
289 if (m->flags & MemberDetails::STORAGE)
290 hints += ":S";
291 if (m->flags & MemberDetails::GUI)
292 hints += ":G";
293 kvpairs_assign (infos, "hints=" + hints);
294 Param param { .extras = m->ev, .metadata = infos };
295 this->props_.push_back (PropertyImpl::make_shared (param, getter, setter, lister));
296 }
297}
298
299} // Ase
Base type for classes that have a Property.
Definition gadget.hh:12
Base type for classes that have a Property.
Definition api.hh:177
virtual GadgetImpl * _parent() const =0
Retrieve parent container.
One entry in a Writ serialization document.
Definition serialize.hh:24
bool in_load() const
Return true during deserialization.
Definition serialize.hh:175
bool in_save() const
Return true during serialization.
Definition serialize.hh:181
T find(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_option_check(const String &optionlist, const String &feature)
Check if an option is set/unset in an options list string.
Definition strings.cc:1412
std::vector< String > StringS
Convenience alias for a std::vector<std::string>.
Definition cxxaux.hh:36
std::function< ChoiceS(const ParameterProperty &)> PropertyLister
Function type to list Choice Property values.
Definition properties.hh:75
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
String string_strip(const String &input)
Strip whitespaces from the left and right of a string.
Definition strings.cc:1126
std::function< void(Value &)> PropertyGetter
Function type for Property value getters.
Definition properties.hh:69
std::function< bool(const Value &)> PropertySetter
Function type for Property value setters.
Definition properties.hh:72
T push_back(T... args)
T reserve(T... args)
T rfind(T... args)
T size(T... args)
typedef uint64_t
strcmp
Helper to specify parameter ranges.
Definition parameter.hh:21
Value type used to interface with various property types.
Definition value.hh:54
T substr(T... args)
typedef ssize_t