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