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

« « « Anklang Documentation
Loading...
Searching...
No Matches
serialize.hh
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#ifndef __ASE_SERIALIZE_HH__
3#define __ASE_SERIALIZE_HH__
4
5#include <ase/value.hh>
6#include <ase/strings.hh>
7#include <ase/jsonapi.hh>
8
9namespace Ase {
10
11class Writ;
12using WritP = std::shared_ptr<Writ>;
13
14// == WritLink ==
15class WritLink {
16 Serializable **spp_;
17 friend class WritNode;
18public:
19 explicit WritLink (Serializable **spp);
20};
21
22// == WritNode ==
24class WritNode {
25 Writ &writ_;
26 ValueP valuep_;
27 Value &value_;
28 friend class Writ;
29 ValueP dummy ();
30 WritNode recfield (const String &fieldname, bool front);
31public:
32 /*ctor*/ WritNode (Writ &writ, ValueP vp = std::make_shared<Value> (Value::empty_value));
33 WritNode operator[] (const String &fieldname) { return recfield (fieldname, false); }
34 WritNode front (const String &fieldname) { return recfield (fieldname, true); }
35 bool in_load () const;
36 bool in_save () const;
37 bool skip_emptystring () const;
38 bool skip_zero () const;
39 Writ& writ ();
40 Value& value ();
41 void purge_value ();
42 Value::Type index () const { return value_.index(); }
43 size_t count () const { return value_.count(); }
44 int64 as_int () const { return value_.as_int(); }
45 double as_double () const { return value_.as_double(); }
46 String as_string () const { return value_.as_string(); }
48 WritNode push ();
49 String repr () const { return value_.repr(); }
50 StringS keys () const { return value_.keys(); }
51 bool has (const String &key) const { return value_.has (key); }
52 bool loadable (const String &key) const;
53 template<typename T> bool operator<< (const T &v);
54 template<typename T> bool operator>> (T &v);
55 template<typename T> bool operator& (T &v);
56 bool operator& (const WritLink &l);
57 template<class T, class E = void>
58 bool serialize (T&, const String& = "", const StringS& = StringS());
59 template<class T>
60 bool serialize (std::vector<T> &vec, const String& = "", const StringS& = StringS());
61 bool serialize (ValueS &vec, const String& = "", const StringS& = StringS());
62 bool serialize (ValueR &rec, const String& = "", const StringS& = StringS());
63 bool serialize (Value &val, const String& = "", const StringS& = StringS());
64 bool serialize (Serializable &sobj);
65 template<class ...T>
66 bool serialize (std::tuple<T...> &tup, const String& = "", const StringS& = StringS());
67};
68
69// == Writ ==
71class Writ {
72 WritNode root_;
73 bool in_load_ = false, in_save_ = false, skip_zero_ = false, skip_emptystring_ = false, relaxed_ = false;
74 ValueP dummy_;
75 struct InstanceMap : Jsonipc::InstanceMap {
76 Jsonipc::JsonValue wrapper_to_json (Wrapper*, size_t, Jsonipc::JsonAllocator&) override;
77 Wrapper* wrapper_from_json (const Jsonipc::JsonValue&) override;
78 } instance_map_;
79 struct LinkEntry { ValueP value; Serializable *sp = nullptr; int64 id = 0; };
80 struct LinkPtr { Serializable **spp = nullptr; int64 id = 0; };
82 int64 link_counter_ = 0;
83 std::vector<LinkPtr> linkptrs_;
84 ValueP dummy ();
85 void reset (int mode);
86 void prepare_link (Serializable &serializable, ValueP valuep);
87 int64 use_link (Serializable &serializable);
88 void insert_links ();
89 void collect_link (int64 id, Serializable &serializable);
90 Serializable* resolve_link (int64 id);
91 void assign_links ();
92 friend class WritNode;
93public:
94 enum Flags { NONE = 0, RELAXED = 1, SKIP_ZERO = 2, SKIP_EMPTYSTRING = 4, SKIP_DEFAULTS = SKIP_ZERO | SKIP_EMPTYSTRING };
95 friend Flags operator| (Flags a, Flags b) { return Flags (uint64_t (a) | b); }
96 explicit Writ (Flags flags = Flags (0));
97 template<class T> void save (T &source);
98 template<class T> bool load (T &target);
99 String to_json ();
100 bool from_json (const String &jsonstring);
101 bool in_load () const { return in_load_; }
102 bool in_save () const { return in_save_; }
103 static void blank_enum (const String &enumname);
104 static void not_serializable (const String &classname);
105 static bool typedata_is_loadable (const StringS &typedata, const String &fieldname);
106 static bool typedata_is_storable (const StringS &typedata, const String &fieldname);
107 static bool typedata_find_minimum (const StringS &typedata, const std::string &fieldname, long double *limit);
108 static bool typedata_find_maximum (const StringS &typedata, const std::string &fieldname, long double *limit);
109};
110
111// == JSON API ==
113template<class T> String json_stringify (const T &source, Writ::Flags flags = Writ::Flags (0));
114
116template<class T> bool json_parse (const String &jsonstring, T &target);
117
119template<class T> T json_parse (const String &jsonstring);
120
121// == WritConverter ==
123template<typename T, typename = void>
125 static_assert (!sizeof (T), "ENOIMPL: type serialization unimplemented");
126 // bool save_value (WritNode node, const T&, const StringS&, const String&);
127 // bool load_value (WritNode node, T&, const StringS&, const String&);
128};
129
130// == Implementations ==
132template<class, class = void> struct
136
137template<class T> inline void
138Writ::save (T &source)
139{
140 reset (2);
141 Jsonipc::Scope scope (instance_map_);
143 {
144 Serializable &serializable = source;
145 root_ & serializable;
146 }
147 else
148 root_ & source;
149 insert_links();
150}
151
152template<class T> inline bool
153Writ::load (T &target)
154{
155 ASE_ASSERT_RETURN (in_load(), false);
156 Jsonipc::Scope scope (instance_map_);
158 {
159 Serializable &serializable = target;
160 root_ & serializable;
161 }
162 else
163 root_ & target;
164 assign_links();
165 return true;
166}
167
168inline ValueP
169WritNode::dummy ()
170{
171 return writ_.dummy();
172}
173
174inline bool
176{
177 return writ_.in_load();
178}
179
180inline bool
182{
183 return writ_.in_save();
184}
185
186inline bool
188{
189 return writ_.skip_emptystring_;
190}
191
192inline bool
194{
195 return writ_.skip_zero_;
196}
197
198inline Value&
200{
201 return value_;
202}
203
204inline Writ&
206{
207 return writ_;
208}
209
210inline bool
211WritNode::loadable (const String &key) const
212{
213 return in_load() && has (key);
214}
215
217template<typename T> inline bool
219{
220 static_assert (!std::is_const<T>::value, "serializable type <T> may not be const");
221 return serialize (v, "");
222}
223
224template<typename T> inline bool
225WritNode::operator<< (const T &v)
226{
227 ASE_ASSERT_RETURN (in_save(), false);
228 return serialize (const_cast<T&> (v), "");
229}
230
231template<typename T> inline bool
232WritNode::operator>> (T &v)
233{
234 ASE_ASSERT_RETURN (in_load(), false);
235 static_assert (!std::is_const<T>::value, "serializable type <T> may not be const");
236 return serialize (v, "");
237}
238
239template<typename T, typename> inline bool
240WritNode::serialize (T &typval, const String &fieldname, const StringS &typedata)
241{
242 if (in_save())
243 return WritConverter<T>::save_value (*this, typval, typedata, fieldname);
244 if (in_load())
245 return WritConverter<T>::load_value (*this, typval, typedata, fieldname);
246 return false;
247}
248
249template<> inline bool
250WritNode::serialize (String &string, const String &fieldname, const StringS &typedata)
251{
252 if (in_save() && Writ::typedata_is_storable (typedata, fieldname))
253 {
254 value_ = string;
255 return true;
256 }
257 if (in_load() && Writ::typedata_is_loadable (typedata, fieldname))
258 {
259 string = value_.as_string();
260 return true;
261 }
262 return false;
263}
264
265template<class T> inline bool
266WritNode::serialize (std::vector<T> &vec, const String &fieldname, const StringS &typedata)
267{
268 if (in_save() && Writ::typedata_is_storable (typedata, fieldname))
269 {
270 value_ = ValueS();
271 ValueS &array = std::get<ValueS> (value_);
272 array.reserve (vec.size());
273 for (auto &el : vec)
274 {
275 array.push_back (Value());
276 WritNode nth_node (writ_, array.back());
277 nth_node & el;
278 }
279 return true;
280 }
281 if (in_load() && Writ::typedata_is_loadable (typedata, fieldname) &&
282 value_.index() == Value::ARRAY)
283 {
284 ValueS &array = std::get<ValueS> (value_);
285 vec.reserve (vec.size() + array.size());
286 for (size_t i = 0; i < array.size(); i++)
287 {
288 vec.resize (vec.size() + 1);
289 WritNode nth_node (writ_, array[i]);
290 nth_node & vec.back();
291 }
292 return true;
293 }
294 return false;
295}
296
297template<class ...T> inline bool
298WritNode::serialize (std::tuple<T...> &tuple, const String &fieldname, const StringS &typedata)
299{
300 using Tuple = std::tuple<T...>;
301 if (in_save() && Writ::typedata_is_storable (typedata, fieldname))
302 {
303 value_ = ValueS();
304 ValueS &array = std::get<ValueS> (value_);
305 array.reserve (std::tuple_size_v<Tuple>);
306 auto save_arg = [&] (auto arg) {
307 array.push_back (Value());
308 WritNode nth_node (writ_, array.back());
309 nth_node & arg;
310 };
311 std::apply ([&] (auto &&...parampack) {
312 (save_arg (parampack), ...); // fold expression to unpack parampack
313 }, tuple);
314 return true;
315 }
316 if (in_load() && Writ::typedata_is_loadable (typedata, fieldname) &&
317 value_.index() == Value::ARRAY)
318 {
319 ValueS &array = std::get<ValueS> (value_);
320 size_t idx = 0;
321 auto load_arg = [&] (auto &arg) {
322 if (idx >= array.size())
323 return;
324 WritNode nth_node (writ_, array[idx++]);
325 nth_node & arg;
326 };
327 std::apply ([&] (auto &...parampack) {
328 (load_arg (parampack), ...); // fold expression to unpack parampack
329 }, tuple);
330 return true;
331 }
332 return false;
333}
334
335inline bool
336WritNode::serialize (Value &val, const String &fieldname, const StringS &typedata)
337{
338 if (in_save() && Writ::typedata_is_storable (typedata, fieldname))
339 {
340 value_ = val;
341 return true;
342 }
343 if (in_load() && Writ::typedata_is_loadable (typedata, fieldname))
344 {
345 val = value_;
346 return true;
347 }
348 return false;
349}
350
351inline bool
352WritNode::serialize (ValueS &vec, const String &fieldname, const StringS &typedata)
353{
354 if (in_save() && Writ::typedata_is_storable (typedata, fieldname))
355 {
356 value_ = vec;
357 return true;
358 }
359 if (in_load() && Writ::typedata_is_loadable (typedata, fieldname) &&
360 value_.index() == Value::ARRAY)
361 {
362 vec = std::get<ValueS> (value_);
363 return true;
364 }
365 return false;
366}
367
368inline bool
369WritNode::serialize (ValueR &rec, const String &fieldname, const StringS &typedata)
370{
371 if (in_save() && Writ::typedata_is_storable (typedata, fieldname))
372 {
373 value_ = rec;
374 return true;
375 }
376 if (in_load() && Writ::typedata_is_loadable (typedata, fieldname) &&
377 value_.index() == Value::RECORD)
378 {
379 rec = std::get<ValueR> (value_);
380 return true;
381 }
382 return false;
383}
384
385// == WritConverter int + float ==
386template<typename T>
387struct WritConverter<T, REQUIRESv< std::is_integral<T>::value || std::is_floating_point<T>::value > >
388{
389 static bool ASE_NOINLINE
390 save_value (WritNode node, T i, const StringS &typedata, const String &fieldname)
391 {
392 node.value() = i;
393 return true;
394 }
395 static bool ASE_NOINLINE
396 load_value (WritNode node, T &v, const StringS &typedata, const String &fieldname)
397 {
398 if constexpr (!std::is_const<T>::value) // ignore loading of const types
399 {
400 T tmp = {};
401 if constexpr (std::is_integral<T>::value)
402 tmp = node.value().as_int();
403 else
404 tmp = node.value().as_double();
405 bool valid = true;
406 long double limit = 0;
407 if (Writ::typedata_find_minimum (typedata, fieldname, &limit))
408 valid = valid && tmp >= limit;
409 if (Writ::typedata_find_maximum (typedata, fieldname, &limit))
410 valid = valid && tmp <= limit;
411 if (valid)
412 {
413 v = tmp;
414 return true;
415 }
416 }
417 return false;
418 }
419};
420
421// == WritConverter Jsonipc::Enum ==
422template<typename T>
423struct WritConverter<T, REQUIRESv< std::is_enum<T>::value > >
424{
425 static void
426 check ()
427 {
429 Writ::blank_enum (typeid_name<T>());
430 }
431 static bool
432 save_value (WritNode node, T i, const StringS &typedata, const String &fieldname)
433 {
434 check();
435 // node.value() = int64 (i);
437 return true;
438 }
439 static bool
440 load_value (WritNode node, T &v, const StringS &typedata, const String &fieldname)
441 {
442 check();
443 // v = T (node.value().as_int());
444 if (node.value().index() == Value::STRING)
445 {
446 v = Jsonipc::Enum<T>::get_value (node.value().as_string(), T{});
447 return true;
448 }
449 return false;
450 }
451};
452
453// == WritConverter Jsonipc::Serializable ==
454template<typename T>
455struct WritConverter<T, REQUIRESv< !std::is_base_of<Serializable,T>::value &&
456 !Jsonipc::DerivesVector<T>::value &&
457 !Jsonipc::DerivesSharedPtr<T>::value &&
458 std::is_class<T>::value > >
459{
460 static bool
461 save_value (WritNode node, T &obj, const StringS &typedata, const String &fieldname)
462 {
464 {
465 if constexpr (Has_serialize_f<T>::value)
466 {
467 ASE_ASSERT_RETURN (node.value().index() == Value::NONE, false);
468 node.value() = ValueR::empty_record; // linking not supported
469 serialize (obj, node);
470 node.purge_value();
471 return true;
472 }
473 Writ::not_serializable (typeid_name<T>());
474 return false;
475 }
476 rapidjson::Document document (rapidjson::kNullType);
477 Jsonipc::JsonValue &docroot = document;
478 docroot = Jsonipc::Serializable<T>::serialize_to_json (obj, document.GetAllocator()); // move semantics!
479 node.value() = Jsonipc::from_json<Value> (docroot);
480 node.purge_value();
481 return true;
482 }
483 static bool
484 load_value (WritNode node, T &obj, const StringS &typedata, const String &fieldname)
485 {
487 {
488 if constexpr (Has_serialize_f<T>::value)
489 {
490 if (node.value().index() == Value::RECORD)
491 serialize (obj, node); // linking not supported
492 return true;
493 }
494 Writ::not_serializable (typeid_name<T>());
495 return false;
496 }
497 rapidjson::Document document (rapidjson::kNullType);
498 Jsonipc::JsonValue &docroot = document;
499 docroot = Jsonipc::to_json<Value> (node.value(), document.GetAllocator()); // move semantics!
500 std::shared_ptr<T> target { &obj, [] (T*) {} }; // dummy shared_ptr with NOP deleter
502 return true;
503 return false;
504 }
505};
506
507// == WritConverter Ase::Serializable ==
508template<typename T>
509struct WritConverter<T, REQUIRESv< std::is_base_of<Serializable,T>::value > >
510{
511 static bool
512 save_value (WritNode node, Serializable &serializable, const StringS &typedata, const String &fieldname)
513 {
514 return node.serialize (serializable);
515 }
516 static bool
517 load_value (WritNode node, Serializable &serializable, const StringS &typedata, const String &fieldname)
518 {
519 if (node.value().index() == Value::RECORD)
520 {
521 node.serialize (serializable);
522 return true;
523 }
524 return false;
525 }
526};
527
528// == JSON ==
529template<class T> inline String
530json_stringify (const T &source, Writ::Flags flags)
531{
532 Writ writ (flags);
533 writ.save (const_cast<T&> (source));
534 return writ.to_json();
535}
536
537template<class T> inline bool
538json_parse (const String &jsonstring, T &target)
539{
540 Writ writ;
541 if (writ.from_json (jsonstring) && writ.load (target))
542 return true;
543 return false;
544}
545
546template<class T> inline T
547json_parse (const String &jsonstring)
548{
549 Writ writ;
550 if (writ.from_json (jsonstring))
551 {
552 T target = {};
553 if (writ.load (target))
554 return target;
555 }
556 return {};
557}
558
559} // Ase
560
561#endif // __ASE_SERIALIZE_HH__
T apply(T... args)
Interface for serializable objects with Reflink support.
Definition defs.hh:97
One entry in a Writ serialization document.
Definition serialize.hh:24
bool skip_emptystring() const
Omit empty strings during in_save()
Definition serialize.hh:187
WritNode push()
Append new WritNode for serializing arrays during in_save().
Definition serialize.cc:285
Writ & writ()
Access the Writ of this node.
Definition serialize.hh:205
Value & value()
Access the Value of this node.
Definition serialize.hh:199
bool in_load() const
Return true during deserialization.
Definition serialize.hh:175
bool skip_zero() const
Omit zero integers or floats during in_save()
Definition serialize.hh:193
bool in_save() const
Return true during serialization.
Definition serialize.hh:181
WritNodeS to_nodes()
Create std::vector<WritNode> for serialized arrays during in_load().
Definition serialize.cc:270
void purge_value()
Clean up defaults in Value.
Definition serialize.cc:354
bool loadable(const String &key) const
True if in_load() && has (key).
Definition serialize.hh:211
bool operator&(T &v)
Serialization operator.
Definition serialize.hh:218
Document containing all information needed to serialize and deserialize a Value.
Definition serialize.hh:71
bool in_save() const
Return true during serialization.
Definition serialize.hh:102
static bool typedata_find_minimum(const StringS &typedata, const std::string &fieldname, long double *limit)
Find the minimum value for field of typedata.
Definition serialize.cc:139
static bool typedata_find_maximum(const StringS &typedata, const std::string &fieldname, long double *limit)
Find the maximum value for field of typedata.
Definition serialize.cc:147
static bool typedata_is_loadable(const StringS &typedata, const String &fieldname)
Check for the writable and storage flags in the hints field of typedata.
Definition serialize.cc:123
bool in_load() const
Return true during deserialization.
Definition serialize.hh:101
static bool typedata_is_storable(const StringS &typedata, const String &fieldname)
Check for the readable and storage flags in the hints field of typedata.
Definition serialize.cc:131
Keep track of temporary instances during IpcDispatcher::dispatch_message().
Definition jsonipc.hh:163
#define ASE_ASSERT_RETURN(expr,...)
Return from the current function if expr evaluates to false and issue an assertion warning.
Definition cxxaux.hh:82
The Anklang C++ API namespace.
Definition api.hh:9
bool json_parse(const String &jsonstring, T &target)
Parse a well formed JSON string and assign contents to target.
Definition serialize.hh:538
std::vector< String > StringS
Convenience alias for a std::vector<std::string>.
Definition cxxaux.hh:36
int64_t int64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:29
typename ::std::enable_if< value, void >::type REQUIRESv
REQUIRESv<value> - Simplified version of std::enable_if<cond,void>::type to use SFINAE in struct temp...
Definition cxxaux.hh:263
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
String json_stringify(const T &source, Writ::Flags flags=Writ::Flags(0))
Create JSON string from source.
Definition serialize.hh:530
Template to specialize string conversion for various data types.
Definition serialize.hh:124
typedef uint64_t
Has_serialize_f<T> - Check if serialize(T&,WritNode&) is provided for T.
Definition serialize.hh:133
Value type used to interface with various property types.
Definition value.hh:54
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
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
StringS keys() const
List the field names of a RECORD Value.
Definition value.cc:39
DerivesSharedPtr<T> - Check if T derives from std::shared_ptr<>.
Definition jsonipc.hh:114
DerivesVector<T> - Check if T derives from std::vector<>.
Definition jsonipc.hh:308
Jsonipc wrapper type for objects that support field-wise serialization to/from JSON.
Definition jsonipc.hh:1185