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

« « « Anklang Documentation
Loading...
Searching...
No Matches
value.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 "value.hh"
3#include "api.hh"
4#include "jsonipc/jsonipc.hh"
5#include "strings.hh"
6#include <cmath>
7
8namespace Ase {
9
10// == Value ==
11const Value Value::empty_value;
12
14size_t
16{
17 switch (index())
18 {
19 case ARRAY: { const auto &a = std::get<ValueS> (*this); return a.size(); }
20 case RECORD: { const auto &r = std::get<ValueR> (*this); return r.size(); }
21 default: ;
22 }
23 return 0;
24}
25
27bool
28Value::has (const String &key) const
29{
30 if (index() == RECORD)
31 for (const auto &field : std::get<ValueR> (*this))
32 if (key == field.name)
33 return true;
34 return false;
35}
36
40{
41 StringS kk;
42 if (index() == RECORD)
43 for (const auto &field : std::get<ValueR> (*this))
44 if (field.value)
45 kk.push_back (field.name);
46 return kk;
47}
48
50bool
51Value::is_numeric (bool boolisnumeric) const
52{
53 Type t = index();
54 return t == INT64 || t == DOUBLE || (t == BOOL && boolisnumeric);
55}
56
60{
61 switch (index())
62 {
63 case BOOL: return std::get<bool> (*this);
64 case INT64: return std::get<int64> (*this);
65 case DOUBLE: return int64 (std::get<double> (*this));
66 case STRING: return string_to_int (std::get<String> (*this));
67 case ARRAY: return count();
68 case RECORD: return count();
69 case INSTANCE: return !!std::get<InstanceP> (*this);
70 case NONE: ; // std::monostate
71 }
72 return 0; // as bool: false
73}
74
76double
78{
79 switch (index())
80 {
81 case BOOL: return std::get<bool> (*this);
82 case INT64: return std::get<int64> (*this);
83 case DOUBLE: return std::get<double> (*this);
84 case STRING: return string_to_double (std::get<String> (*this));
85 case ARRAY: return count();
86 case RECORD: return count();
87 case INSTANCE: return !!std::get<InstanceP> (*this);
88 case NONE: ; // std::monostate
89 }
90 return 0; // as bool: false
91}
92
96{
97 switch (index())
98 {
99 case BOOL: return std::get<bool> (*this) ? "true" : "false";
100 case INT64: return string_from_int (std::get<int64> (*this));
101 case DOUBLE: return string_from_double (std::get<double> (*this));
102 case STRING: return std::get<String> (*this);
103 case ARRAY: return count() ? "[...]" : "[]";
104 case RECORD: return count() ? "{...}" : "{}";
105 case INSTANCE: {
106 InstanceP ip = std::get<InstanceP> (*this);
107 return ip ? Jsonipc::rtti_typename (*ip) + "{}" : "(Instance*) nullptr";
108 }
109 case NONE: ; // std::monostate
110 }
111 return "";
113 static_assert (std::is_same<decltype (std::get<BOOL> (std::declval<Value>())), bool&&>::value);
114 static_assert (std::is_same<decltype (std::get<bool> (std::declval<Value>())), bool&&>::value);
117 static_assert (std::is_same<decltype (std::get<DOUBLE> (std::declval<Value>())), double&&>::value);
118 static_assert (std::is_same<decltype (std::get<double> (std::declval<Value>())), double&&>::value);
131}
132
134const ValueS&
136{
137 if (index() == ARRAY)
138 return std::get<ValueS> (*this);
139 return ValueS::empty_array;
140}
141
142const ValueR&
143Value::as_record () const
144{
145 if (index() == RECORD)
146 return std::get<ValueR> (*this);
147 return ValueR::empty_record;
148}
149
150Value&
151Value::operator[] (size_t i)
152{
153 if (index() == ARRAY)
154 {
155 ValueS &a = std::get<ValueS> (*this);
156 if (i < a.size())
157 {
158 if (!a[i])
160 return *a[i];
161 }
162 }
163 if (index() == RECORD)
164 {
165 ValueR &r = std::get<ValueR> (*this);
166 if (i < r.size())
167 {
168 if (!r[i].value)
169 r[i].value = std::make_shared<Value>();
170 return *r[i].value;
171 }
172 }
173 throw std::runtime_error ("Invalid Ase::Value index");
174}
175
176const Value&
177Value::operator[] (size_t i) const
178{
179 if (index() == ARRAY)
180 {
181 const ValueS &a = std::get<ValueS> (*this);
182 if (i < a.size() && a[i])
183 return *a[i];
184 }
185 if (index() == RECORD)
186 {
187 const ValueR &r = std::get<ValueR> (*this);
188 if (i < r.size() && r[i].value)
189 return *r[i].value;
190 }
191 return empty_value;
192}
193
194Value&
195Value::operator[] (const String &name)
196{
197 if (index() == RECORD)
198 return std::get<ValueR> (*this)[name];
199 throw std::runtime_error ("Invalid Ase::Value index");
200}
201
202const Value&
203Value::operator[] (const String &name) const
204{
205 if (index() == RECORD)
206 std::get<ValueR> (*this)[name];
207 return empty_value;
208}
209
210static String
211value_array_to_string (const ValueS &vec)
212{
213 String s;
214 for (auto const &valuep : vec)
215 {
216 const Value &value = valuep ? *valuep : Value::empty_value;
217 if (!s.empty())
218 s += ",";
219 s += value.repr();
220 }
221 s = "[" + s + "]";
222 return s;
223}
224
225static String
226value_record_to_string (const ValueR &vec)
227{
228 String s;
229 for (auto const &field : vec)
230 {
231 const Value &value = field.value ? *field.value : Value::empty_value;
232 if (!s.empty())
233 s += ",";
234 s += string_to_cquote (field.name) + ":";
235 s += value.repr();
236 }
237 s = "{" + s + "}";
238 return s;
239}
240
242String
244{
245 String s;
246 switch (index())
247 {
248 case BOOL: s += std::get<bool> (*this) ? "true" : "false"; break;
249 case INT64: s += string_format ("%d", std::get<int64> (*this)); break;
250 case DOUBLE: s += string_format ("%.17g", std::get<double> (*this)); break;
251 case STRING: s += string_to_cquote (std::get<String> (*this)); break;
252 case ARRAY: s += value_array_to_string (std::get<ValueS>(*this)); break;
253 case RECORD: s += value_record_to_string (std::get<ValueR>(*this)); break;
254 case INSTANCE: s += as_string(); break;
255 case NONE: s += "null"; /* std::monostate */ break;
256 }
257 return s;
258}
259
261void
262Value::filter (const std::function<bool (const ValueField&)> &pred)
263{
264 if (index() == Value::ARRAY)
265 for (auto &vp : std::get<ValueS> (*this))
266 if (vp)
267 vp->filter (pred);
268 if (index() == Value::RECORD)
269 {
270 ValueR &rec = std::get<ValueR> (*this);
271 for (size_t i = rec.size(); i > 0; i--)
272 if (pred (rec[i - 1]))
273 rec.erase (rec.begin() + i - 1);
274 else if (rec[i - 1].value)
275 rec[i - 1].value->filter (pred);
276 }
277}
278
279// == ValueS ==
280const ValueS ValueS::empty_array;
281
282ValueS::ValueS()
283{}
284
285ValueS::ValueS (std::initializer_list<Value> il)
286{
287 reserve (il.size());
288 for (auto &&e : il)
289 push_back (std::move (e));
290}
291
292String
293ValueS::repr() const
294{
295 return value_array_to_string (*this);
296}
297
298// == Event ==
299Event::Event ()
300{}
301
302Event::Event (const String &type, const String &detail, std::initializer_list<ValueField> il)
303{
304 reserve (2 + il.size());
305 push_back ({ "type", type });
306 push_back ({ "detail", detail });
307 for (auto &&e : il)
308 push_back (std::move (e));
309}
310
311// == ValueR ==
312const ValueR ValueR::empty_record;
313
314ValueR::ValueR()
315{}
316
317ValueR::ValueR (std::initializer_list<ValueField> il)
318{
319 reserve (il.size());
320 for (auto &&e : il)
321 push_back (std::move (e));
322}
323
324String
325ValueR::repr() const
326{
327 return value_record_to_string (*this);
328}
329
330ValueP
331ValueR::peek (const String &name) const
332{
333 for (size_t i = 0; i < size(); i++)
334 if (name == (*this)[i].name)
335 return (*this)[i].value;
336 return nullptr;
337}
338
339ValueP
340ValueR::valuep (const String &name, bool front)
341{
342 for (size_t i = 0; i < size(); i++)
343 if (name == (*this)[i].name)
344 {
345 if (!(*this)[i].value)
346 (*this)[i].value = std::make_shared<Value>();
347 return (*this)[i].value;
348 }
349 if (front)
350 {
351 insert (begin(), { name, Value() });
352 return begin()->value;
353 }
354 else
355 {
356 push_back ({ name, Value() });
357 return back().value;
358 }
359}
360
361Value&
362ValueR::operator[] (const String &name)
363{
364 return *valuep (name, false);
365}
366
367const Value&
368ValueR::operator[] (const String &name) const
369{
370 for (size_t i = 0; i < size(); i++)
371 if ((*this)[i].value && name == (*this)[i].name)
372 return *(*this)[i].value;
373 return Value::empty_value;
374}
375
376// == ValueField ==
377ValueField::ValueField() :
378 value (std::make_shared<Value>())
379{}
380
381ValueField::ValueField (const String &nam, const Value &val) :
382 name (nam), value (std::make_shared<Value> (val))
383{}
384
385ValueField::ValueField (const String &nam, Value &&val) :
386 name (nam), value (std::make_shared<Value> (std::move (val)))
387{}
388
389ValueField::ValueField (const String &nam, ValueP val) :
390 name (nam), value (val ? val : std::make_shared<Value>())
391{}
392
393// == EnumInfo ==
394static std::mutex enuminfo_mutex;
395static std::vector<std::pair<const std::type_info*, std::function<EnumInfo(int64)>>> enuminfo_funcs;
396
398EnumInfo
399EnumInfo::value_info (const std::type_info &enumtype, int64 value)
400{
402 {
403 std::lock_guard<std::mutex> locker (enuminfo_mutex);
404 for (const auto &pair : enuminfo_funcs)
405 if (enumtype == *pair.first)
406 f = pair.second;
407 }
408 EnumInfo info;
409 if (f)
410 info = f (value);
411 return info;
412}
413
415void
416EnumInfo::impl (const std::type_info &enumtype, const std::function<EnumInfo(int64)> &fun)
417{
418 std::lock_guard<std::mutex> locker (enuminfo_mutex);
419 enuminfo_funcs.push_back ({ &enumtype, fun });
420}
421
422} // Ase
T begin(T... args)
T erase(T... args)
T make_shared(T... args)
T move(T... args)
The Anklang C++ API namespace.
Definition api.hh:9
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
String string_from_double(double value)
Convert a double into a string, using the POSIX/C locale.
Definition strings.cc:699
int64_t int64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:29
String string_from_int(int64 value)
Convert a 64bit signed integer into a string.
Definition strings.cc:604
int64 string_to_int(const String &string, size_t *consumed, uint base)
Parse a string into a 64bit integer, optionally specifying the expected number base.
Definition strings.cc:578
double string_to_double(const String &string)
Parse a double from a string, trying locale specific characters and POSIX/C formatting.
Definition strings.cc:674
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
String string_to_cquote(const String &str)
Returns a string as C string including double quotes.
Definition strings.cc:1036
T push_back(T... args)
T reserve(T... args)
T size(T... args)
Get auxiallary enum information.
Definition value.hh:101
double as_double() const
Convert Value to double or return 0.
Definition value.cc:77
String repr() const
Convert Value to a string representation, useful for debugging.
Definition value.cc:243
bool is_numeric(bool boolisnumeric=true) const
Checks if Value is a DOUBLE, INT64, or BOOL.
Definition value.cc:51
int64 as_int() const
Convert Value to int64 or return 0.
Definition value.cc:59
size_t count() const
Number of elements in a RECORD or ARRAY Value.
Definition value.cc:15
bool has(const String &key) const
Check for a named field in a RECORD.
Definition value.cc:28
String as_string() const
Convert Value to a string, not very useful for RECORD or ARRAY.
Definition value.cc:95
void filter(const std::function< bool(const ValueField &)> &pred)
Recursively purge/remove RECORD elements iff to pred (recordfield) == true.
Definition value.cc:262
StringS keys() const
List the field names of a RECORD Value.
Definition value.cc:39
const ValueS & as_array() const
Retrive a non-empty array if Value contains a non-empty array.
Definition value.cc:135