openMSX
serialize_core.hh
Go to the documentation of this file.
1 #ifndef SERIALIZE_CORE_HH
2 #define SERIALIZE_CORE_HH
3 
4 #include "serialize_constr.hh"
5 #include "serialize_meta.hh"
6 #include "one_of.hh"
7 #include "xrange.hh"
8 #include <string>
9 #include <type_traits>
10 #include <cassert>
11 #include <memory>
12 
13 namespace openmsx {
14 
15 // Type-queries for serialization framework
16 // is_primitive<T>
17 template<typename T> struct is_primitive : std::false_type {};
18 template<> struct is_primitive<bool> : std::true_type {};
19 template<> struct is_primitive<char> : std::true_type {};
20 template<> struct is_primitive<signed char> : std::true_type {};
21 template<> struct is_primitive<signed short> : std::true_type {};
22 template<> struct is_primitive<signed int> : std::true_type {};
23 template<> struct is_primitive<signed long> : std::true_type {};
24 template<> struct is_primitive<unsigned char> : std::true_type {};
25 template<> struct is_primitive<unsigned short> : std::true_type {};
26 template<> struct is_primitive<unsigned int> : std::true_type {};
27 template<> struct is_primitive<unsigned long> : std::true_type {};
28 template<> struct is_primitive<float> : std::true_type {};
29 template<> struct is_primitive<double> : std::true_type {};
30 template<> struct is_primitive<long double> : std::true_type {};
31 template<> struct is_primitive<long long> : std::true_type {};
32 template<> struct is_primitive<unsigned long long> : std::true_type {};
33 template<> struct is_primitive<std::string> : std::true_type {};
34 
35 
36 
37 // Normally to make a class serializable, you have to implement a serialize()
38 // method on the class. For some classes we cannot extend the source code. So
39 // we need an alternative, non-intrusive, way to make those classes
40 // serializable.
41 template<typename Archive, typename T>
42 void serialize(Archive& ar, T& t, unsigned version)
43 {
44  // By default use the serialize() member. But this function can
45  // be overloaded to serialize classes in a non-intrusive way.
46  t.serialize(ar, version);
47 }
48 
49 template<typename Archive, typename T1, typename T2>
50 void serialize(Archive& ar, std::pair<T1, T2>& p, unsigned /*version*/)
51 {
52  ar.serialize("first", p.first,
53  "second", p.second);
54 }
55 template<typename T1, typename T2> struct SerializeClassVersion<std::pair<T1, T2>>
56 {
57  static constexpr unsigned value = 0;
58 };
59 
61 
98 template<typename T> struct serialize_as_enum : std::false_type {};
99 
100 template<typename T> struct enum_string {
101  const char* str;
102  T e;
103 };
104 void enumError(const std::string& str);
105 template<typename T> struct serialize_as_enum_impl : std::true_type {
106  explicit serialize_as_enum_impl(std::initializer_list<enum_string<T>> info_)
107  : info(info_) {}
108  std::string toString(T t) const {
109  for (auto& i : info) {
110  if (i.e == t) return i.str;
111  }
112  assert(false);
113  return "internal-error-unknown-enum-value";
114  }
115  T fromString(const std::string& str) const {
116  for (auto& i : info) {
117  if (i.str == str) return i.e;
118  }
119  enumError(str); // does not return (throws)
120  return T(); // avoid warning
121  }
122 private:
123  std::initializer_list<enum_string<T>> info;
124 };
125 
126 #define SERIALIZE_ENUM(TYPE,INFO) \
127 template<> struct serialize_as_enum< TYPE > : serialize_as_enum_impl< TYPE > { \
128  serialize_as_enum() : serialize_as_enum_impl<TYPE>(INFO) {} \
129 };
130 
132 
133 // serialize_as_pointer<T>
134 //
135 // Type-trait class that indicates whether a certain type should be serialized
136 // as a pointer or not. There can be multiple pointers to the same object,
137 // only the first such pointer is actually stored, the others are stored as
138 // a reference to this first object.
139 //
140 // By default all pointer types are treated as pointer, but also smart pointer
141 // can be treated as such. Though only unique_ptr<T> is implemented ATM.
142 //
143 // The serialize_as_pointer class has the following members:
144 // - static bool value
145 // True iff this type must be serialized as a pointer.
146 // The fields below are only valid (even only present) if this variable
147 // is true.
148 // - using type = T
149 // The pointed-to type
150 // - T* getPointer(X x)
151 // Get an actual pointer from the abstract pointer x
152 // (X can be a smart pointer type)
153 // - void setPointer(X x, T* p, Archive& ar)
154 // Copy the raw-pointer p to the abstract pointer x
155 // The archive can be used to store per-archive data, this is for example
156 // needed for shared_ptr.
157 
158 template<typename T> struct serialize_as_pointer : std::false_type {};
159 template<typename T> struct serialize_as_pointer_impl : std::true_type
160 {
161  // pointer to primitive types not supported
162  static_assert(!is_primitive<T>::value,
163  "can't serialize ptr to primitive type");
164  using type = T;
165 };
166 template<typename T> struct serialize_as_pointer<T*>
168 {
169  static inline T* getPointer(T* t) { return t; }
170  template<typename Archive>
171  static inline void setPointer(T*& t, T* p, Archive& /*ar*/) {
172  t = p;
173  }
174 };
175 template<typename T> struct serialize_as_pointer<std::unique_ptr<T>>
177 {
178  static inline T* getPointer(const std::unique_ptr<T>& t) { return t.get(); }
179  template<typename Archive>
180  static inline void setPointer(std::unique_ptr<T>& t, T* p, Archive& /*ar*/) {
181  t.reset(p);
182  }
183 };
184 template<typename T> struct serialize_as_pointer<std::shared_ptr<T>>
186 {
187  static T* getPointer(const std::shared_ptr<T>& t) { return t.get(); }
188  template<typename Archive>
189  static void setPointer(std::shared_ptr<T>& t, T* p, Archive& ar) {
190  ar.resetSharedPtr(t, p);
191  }
192 };
193 
195 
196 // serialize_as_collection<T>
197 //
198 // Type-trait class that indicates whether a certain type should be serialized
199 // as a collection or not. The serialization code 'knows' how to serialize
200 // collections, so as a user of the serializer you only need to list the
201 // collection to have it serialized (you don't have to iterate over it
202 // manually).
203 //
204 // By default arrays, std::vector, std::list, std::deque and std::map are
205 // recognized as collections. Though for STL collections you need to add
206 // #include "serialize_stl.hh"
207 //
208 // The serialize_as_collection class has the following members:
209 //
210 // - static bool value
211 // True iff this type must be serialized as a collection.
212 // The fields below are only valid (even only present) if this variable
213 // is true.
214 // - int size
215 // The size of the collection, -1 for variable sized collections.
216 // Fixed sized collections can be serialized slightly more efficient
217 // because we don't need to explicitly store the size.
218 // - using value_type = ...
219 // The type stored in the collection (only homogeneous collections are
220 // supported).
221 // - bool loadInPlace
222 // Indicates whether we can directly load the elements in the correct
223 // position in the container, otherwise there will be an extra assignment.
224 // For this to be possible, the output iterator must support a dereference
225 // operator that returns a 'regular' value_type.
226 // - using const_iterator = ...
227 // - const_iterator begin(...)
228 // - const_iterator end(...)
229 // Returns begin/end iterator for the given collection. Used for saving.
230 // - void prepare(..., int n)
231 // - output_iterator output(...)
232 // These are used for loading. The prepare() method should prepare the
233 // collection to receive 'n' elements. The output() method returns an
234 // output_iterator to the beginning of the collection.
235 
236 template<typename T> struct serialize_as_collection : std::false_type {};
237 template<typename T, int N> struct serialize_as_collection<T[N]> : std::true_type
238 {
239  static constexpr int size = N; // fixed size
240  using value_type = T;
241  // save
242  using const_iterator = const T*;
243  static const T* begin(const T (&array)[N]) { return &array[0]; }
244  static const T* end (const T (&array)[N]) { return &array[N]; }
245  // load
246  static constexpr bool loadInPlace = true;
247  static void prepare(T (&/*array*/)[N], int /*n*/) { }
248  static T* output(T (&array)[N]) { return &array[0]; }
249 };
250 
252 
253 // Implementation of the different save-strategies.
254 //
255 // ATM we have
256 // - PrimitiveSaver
257 // Saves primitive values: int, bool, string, ...
258 // Primitive values cannot be versioned.
259 // - EnumSaver
260 // Depending on the archive type, enums are either saved as strings (XML
261 // archive) or as integers (memory archive).
262 // This does not work automatically: it needs a specialization of
263 // serialize_as_enum, see above.
264 // - ClassSaver
265 // From a serialization POV classes are a (heterogeneous) collection of
266 // other to-be-serialized items.
267 // Classes can have a version number, this allows to evolve the class
268 // structure while still being able to load older versions (the load
269 // method receive the version number as parameter, the user still needs
270 // to keep the old loading code in place).
271 // Optionally the name of the (concrete) class is saved in the stream.
272 // This is used to support loading of polymorphic classes.
273 // There is also an (optional) id saved in the stream. This used to
274 // resolve possible multiple pointers to the same class.
275 // - PointerSaver
276 // Saves a pointer to a class (pointers to primitive types are not
277 // supported). See also serialize_as_pointer
278 // - IDSaver
279 // Weaker version of PointerSaver: it can only save pointers to objects
280 // that are already saved before (so it's will be saved by storing a
281 // reference). To only reason to use IDSaver (iso PointerSaver) is that
282 // it will not instantiate the object construction code.
283 // - CollectionSaver
284 // Saves a whole collection. See also serialize_as_collection
285 //
286 // All these strategies have a method:
287 // template<typename Archive> void operator()(Archive& ar, const T& t)
288 // 'ar' is archive where the serialized stream will go
289 // 't' is the to-be-saved object
290 // 'saveId' Should ID be saved
291 
292 template<typename T> struct PrimitiveSaver
293 {
294  template<typename Archive> void operator()(Archive& ar, const T& t,
295  bool /*saveId*/)
296  {
297  static_assert(is_primitive<T>::value, "must be primitive type");
298  ar.save(t);
299  }
300 };
301 template<typename T> struct EnumSaver
302 {
303  template<typename Archive> void operator()(Archive& ar, const T& t,
304  bool /*saveId*/)
305  {
306  if constexpr (Archive::TRANSLATE_ENUM_TO_STRING) {
308  std::string str = sae.toString(t);
309  ar.save(str);
310  } else {
311  ar.save(int(t));
312  }
313  }
314 };
315 template<typename T> struct ClassSaver
316 {
317  template<typename Archive> void operator()(
318  Archive& ar, const T& t, bool saveId,
319  const char* type = nullptr, bool saveConstrArgs = false)
320  {
321  // Order is important (for non-xml archives). We use this order:
322  // - id
323  // - type
324  // - version
325  // - constructor args
326  // Rational:
327  // - 'id' must be first: it could be nullptr, in that
328  // case the other fields are not even present.
329  // - 'type' must be before version, because for some types we
330  // might not need to store version (though it's unlikely)
331  // - version must be before constructor args because the
332  // constr args depend on the version
333  if (saveId) {
334  unsigned id = ar.generateId(&t);
335  ar.attribute("id", id);
336  }
337 
338  if (type) {
339  ar.attribute("type", type);
340  }
341 
342  unsigned version = SerializeClassVersion<T>::value;
343  if ((version != 0) && ar.NEED_VERSION) {
344  if (!ar.CAN_HAVE_OPTIONAL_ATTRIBUTES ||
345  (version != 1)) {
346  ar.attribute("version", version);
347  }
348  }
349 
350  if (saveConstrArgs) {
351  // save local constructor args (if any)
352  SerializeConstructorArgs<T> constrArgs;
353  constrArgs.save(ar, t);
354  }
355 
356  using TNC = std::remove_const_t<T>;
357  auto& t2 = const_cast<TNC&>(t);
358  serialize(ar, t2, version);
359  }
360 };
361 template<typename TP> struct PointerSaver
362 {
363  // note: we only support pointer to class
364  template<typename Archive> void operator()(Archive& ar, const TP& tp2,
365  bool /*saveId*/)
366  {
367  static_assert(serialize_as_pointer<TP>::value,
368  "must be serialized as pointer");
369  using T = typename serialize_as_pointer<TP>::type;
370  const T* tp = serialize_as_pointer<TP>::getPointer(tp2);
371  if (!tp) {
372  unsigned id = 0;
373  ar.attribute("id_ref", id);
374  return;
375  }
376  if (unsigned id = ar.getId(tp)) {
377  ar.attribute("id_ref", id);
378  } else {
379  if constexpr (std::is_polymorphic_v<T>) {
381  } else {
382  ClassSaver<T> saver;
383  // don't store type
384  // store id, constr-args
385  saver(ar, *tp, true, nullptr, true);
386  }
387  }
388  }
389 };
390 template<typename TP> struct IDSaver
391 {
392  template<typename Archive> void operator()(Archive& ar, const TP& tp2)
393  {
394  static_assert(serialize_as_pointer<TP>::value,
395  "must be serialized as pointer");
397  unsigned id;
398  if (!tp) {
399  id = 0;
400  } else {
401  id = ar.getId(tp);
402  assert(id);
403  }
404  ar.attribute("id_ref", id);
405  }
406 };
407 template<typename TC> struct CollectionSaver
408 {
409  template<typename Archive> void operator()(Archive& ar, const TC& tc,
410  bool saveId)
411  {
412  using sac = serialize_as_collection<TC>;
413  static_assert(sac::value, "must be serialized as collection");
414  auto begin = sac::begin(tc);
415  auto end = sac::end (tc);
416  if ((sac::size < 0) && (!ar.CAN_COUNT_CHILDREN)) {
417  // variable size
418  // e.g. in an XML archive the loader can look-ahead and
419  // count the number of sub-tags, so no need to
420  // explicitly store the size for such archives.
421  int n = int(std::distance(begin, end));
422  ar.serialize("size", n);
423  }
424  for (; begin != end; ++begin) {
425  if (saveId) {
426  ar.serializeWithID("item", *begin);
427  } else {
428  ar.serialize("item", *begin);
429  }
430  }
431  }
432 };
433 
434 // Delegate to a specific Saver class
435 // (implemented as inheriting from a specific baseclass).
436 template<typename T> struct Saver
437  : std::conditional_t<is_primitive<T>::value, PrimitiveSaver<T>,
438  std::conditional_t<serialize_as_enum<T>::value, EnumSaver<T>,
439  std::conditional_t<serialize_as_pointer<T>::value, PointerSaver<T>,
440  std::conditional_t<serialize_as_collection<T>::value, CollectionSaver<T>,
441  ClassSaver<T>>>>> {};
442 
444 
445 // Implementation of the different load-strategies.
446 //
447 // This matches very closely with the save-strategies above.
448 //
449 // All these strategies have a method:
450 // template<typename Archive, typename TUPLE>
451 // void operator()(Archive& ar, const T& t, TUPLE args)
452 // 'ar' Is archive where the serialized stream will go
453 // 't' Is the object that has to be restored.
454 // In case of a class (not a pointer to a class) the actual object
455 // is already constructed, but it still needs to be filled in with
456 // the correct data.
457 // 'args' (Only used by PointerLoader) holds extra parameters used
458 // to construct objects.
459 // 'id' Used to skip loading an ID, see comment in ClassLoader
460 
461 template<typename T> struct PrimitiveLoader
462 {
463  template<typename Archive, typename TUPLE>
464  void operator()(Archive& ar, T& t, TUPLE /*args*/, int /*id*/)
465  {
466  static_assert(std::tuple_size_v<TUPLE> == 0,
467  "can't have constructor arguments");
468  ar.load(t);
469  }
470 };
471 template<typename T> struct EnumLoader
472 {
473  template<typename Archive, typename TUPLE>
474  void operator()(Archive& ar, T& t, TUPLE /*args*/, int /*id*/)
475  {
476  static_assert(std::tuple_size_v<TUPLE> == 0,
477  "can't have constructor arguments");
478  if constexpr (Archive::TRANSLATE_ENUM_TO_STRING) {
479  std::string str;
480  ar.load(str);
482  t = sae.fromString(str);
483  } else {
484  int i;
485  ar.load(i);
486  t = T(i);
487  }
488  }
489 };
490 
491 unsigned loadVersionHelper(MemInputArchive& ar, const char* className,
492  unsigned latestVersion);
493 unsigned loadVersionHelper(XmlInputArchive& ar, const char* className,
494  unsigned latestVersion);
495 template<typename T, typename Archive> unsigned loadVersion(Archive& ar)
496 {
497  unsigned latestVersion = SerializeClassVersion<T>::value;
498  if ((latestVersion != 0) && ar.NEED_VERSION) {
499  return loadVersionHelper(ar, typeid(T).name(), latestVersion);
500  } else {
501  return latestVersion;
502  }
503 }
504 template<typename T> struct ClassLoader
505 {
506  template<typename Archive, typename TUPLE>
507  void operator()(Archive& ar, T& t, TUPLE /*args*/, int id = 0,
508  int version = -1)
509  {
510  static_assert(std::tuple_size_v<TUPLE> == 0,
511  "can't have constructor arguments");
512 
513  // id == -1: don't load id, don't addPointer
514  // id == 0: load id from archive, addPointer
515  // id == N: id already loaded, still addPointer
516  if (id != -1) {
517  if (id == 0) {
518  ar.attribute("id", id);
519  }
520  ar.addPointer(id, &t);
521  }
522 
523  // version == -1: load version
524  // version == N: version already loaded
525  if (version == -1) {
526  version = loadVersion<T>(ar);
527  }
528 
529  using TNC = std::remove_const_t<T>;
530  auto& t2 = const_cast<TNC&>(t);
531  serialize(ar, t2, version);
532  }
533 };
534 template<typename T> struct NonPolymorphicPointerLoader
535 {
536  template<typename Archive, typename GlobalTuple>
537  T* operator()(Archive& ar, unsigned id, GlobalTuple globalArgs)
538  {
539  int version = loadVersion<T>(ar);
540 
541  // load (local) constructor args (if any)
542  using TNC = std::remove_const_t<T>;
543  using ConstrArgs = SerializeConstructorArgs<TNC>;
544  ConstrArgs constrArgs;
545  auto localArgs = constrArgs.load(ar, version);
546 
547  // combine global and local constr args
548  auto args = std::tuple_cat(globalArgs, localArgs);
549  // TODO make combining global/local constr args configurable
550 
551  Creator<T> creator;
552  auto tp = creator(args);
553  ClassLoader<T> loader;
554  loader(ar, *tp, std::tuple<>(), id, version);
555  return tp.release();
556  }
557 };
558 template<typename T> struct PolymorphicPointerLoader
559 {
560  template<typename Archive, typename TUPLE>
561  T* operator()(Archive& ar, unsigned id, TUPLE args)
562  {
563  using ArgsType = typename PolymorphicConstructorArgs<T>::type;
564  static_assert(std::is_same_v<TUPLE, ArgsType>,
565  "constructor arguments types must match");
566  return static_cast<T*>(
568  }
569 };
570 template<typename T> struct PointerLoader2
571  // extra indirection needed because inlining the body of
572  // NonPolymorphicPointerLoader in PointerLoader does not compile
573  // for abstract types
574  : std::conditional_t<std::is_polymorphic_v<T>,
575  PolymorphicPointerLoader<T>,
576  NonPolymorphicPointerLoader<T>> {};
577 
578 template<typename TP> struct PointerLoader
579 {
580  template<typename Archive, typename GlobalTuple>
581  void operator()(Archive& ar, TP& tp2, GlobalTuple globalArgs, int /*id*/)
582  {
583  static_assert(serialize_as_pointer<TP>::value,
584  "must be serialized as a pointer");
585  // in XML archives we use 'id_ref' or 'id', in other archives
586  // we don't care about the name
587  unsigned id;
588  if (ar.CAN_HAVE_OPTIONAL_ATTRIBUTES &&
589  ar.findAttribute("id_ref", id)) {
590  // nothing, 'id' already filled in
591  } else {
592  ar.attribute("id", id);
593  }
594 
595  using T = typename serialize_as_pointer<TP>::type;
596  T* tp;
597  if (id == 0) {
598  tp = nullptr;
599  } else {
600  if (void* p = ar.getPointer(id)) {
601  tp = static_cast<T*>(p);
602  } else {
603  PointerLoader2<T> loader;
604  tp = loader(ar, id, globalArgs);
605  }
606  }
608  }
609 };
610 void pointerError(unsigned id);
611 template<typename TP> struct IDLoader
612 {
613  template<typename Archive>
614  void operator()(Archive& ar, TP& tp2)
615  {
616  static_assert(serialize_as_pointer<TP>::value,
617  "must be serialized as a pointer");
618  unsigned id;
619  ar.attribute("id_ref", id);
620 
621  using T = typename serialize_as_pointer<TP>::type;
622  T* tp;
623  if (id == 0) {
624  tp = nullptr;
625  } else {
626  void* p = ar.getPointer(id);
627  if (!p) {
628  pointerError(id);
629  }
630  tp = static_cast<T*>(p);
631  }
633  }
634 };
635 
636 template<typename sac, bool IN_PLACE = sac::loadInPlace> struct CollectionLoaderHelper;
637 template<typename sac> struct CollectionLoaderHelper<sac, true>
638 {
639  // used for array and vector
640  template<typename Archive, typename TUPLE, typename OUT_ITER>
641  void operator()(Archive& ar, TUPLE args, OUT_ITER it, int id)
642  {
643  ar.doSerialize("item", *it, args, id);
644  }
645 };
646 template<typename sac> struct CollectionLoaderHelper<sac, false>
647 {
648  // We can't directly load the element in the correct position:
649  // This screws-up id/pointer management because the element is still
650  // copied after construction (and pointer value of initial object is
651  // stored).
652  template<typename Archive, typename TUPLE, typename OUT_ITER>
653  void operator()(Archive& ar, TUPLE args, OUT_ITER it, int id)
654  {
655  typename sac::value_type elem;
656  ar.doSerialize("item", elem, args, id);
657  *it = std::move(elem);
658  }
659 };
660 template<typename TC> struct CollectionLoader
661 {
662  template<typename Archive, typename TUPLE>
663  void operator()(Archive& ar, TC& tc, TUPLE args, int id = 0)
664  {
665  assert(id == one_of(0, -1));
666  using sac = serialize_as_collection<TC>;
667  static_assert(sac::value, "must be serialized as a collection");
668  int n = sac::size;
669  if (n < 0) {
670  // variable size
671  if constexpr (Archive::CAN_COUNT_CHILDREN) {
672  n = ar.countChildren();
673  } else {
674  ar.serialize("size", n);
675  }
676  }
677  sac::prepare(tc, n);
678  auto it = sac::output(tc);
679  CollectionLoaderHelper<sac> loadOneElement;
680  repeat(n, [&] {
681  loadOneElement(ar, args, it, id);
682  ++it;
683  });
684  }
685 };
686 template<typename T> struct Loader
687  : std::conditional_t<is_primitive<T>::value, PrimitiveLoader<T>,
688  std::conditional_t<serialize_as_enum<T>::value, EnumLoader<T>,
689  std::conditional_t<serialize_as_pointer<T>::value, PointerLoader<T>,
690  std::conditional_t<serialize_as_collection<T>::value, CollectionLoader<T>,
691  ClassLoader<T>>>>> {};
692 
693 } // namespace openmsx
694 
695 #endif
TclObject t
Definition: one_of.hh:7
static void * load(Archive &ar, unsigned id, const void *args)
static void save(Archive &ar, T *t)
This file implemented 3 utility functions:
Definition: Autofire.cc:9
void enumError(const std::string &str)
unsigned loadVersion(Archive &ar)
constexpr unsigned N
Definition: ResampleHQ.cc:229
void serialize(Archive &ar, T &t, unsigned version)
void pointerError(unsigned id)
unsigned loadVersionHelper(MemInputArchive &, const char *, unsigned)
size_t size(std::string_view utf8)
auto distance(octet_iterator first, octet_iterator last)
void operator()(Archive &ar, T &t, TUPLE, int id=0, int version=-1)
void operator()(Archive &ar, const T &t, bool saveId, const char *type=nullptr, bool saveConstrArgs=false)
void operator()(Archive &ar, TUPLE args, OUT_ITER it, int id)
void operator()(Archive &ar, TUPLE args, OUT_ITER it, int id)
void operator()(Archive &ar, TC &tc, TUPLE args, int id=0)
void operator()(Archive &ar, const TC &tc, bool saveId)
Utility to do T* t = new T(...)
void operator()(Archive &ar, T &t, TUPLE, int)
void operator()(Archive &ar, const T &t, bool)
void operator()(Archive &ar, TP &tp2)
void operator()(Archive &ar, const TP &tp2)
T * operator()(Archive &ar, unsigned id, GlobalTuple globalArgs)
void operator()(Archive &ar, TP &tp2, GlobalTuple globalArgs, int)
void operator()(Archive &ar, const TP &tp2, bool)
Store association between polymorphic class (base- or subclass) and the list of constructor arguments...
T * operator()(Archive &ar, unsigned id, TUPLE args)
void operator()(Archive &ar, T &t, TUPLE, int)
void operator()(Archive &ar, const T &t, bool)
Store serialization-version number of a class.
static constexpr unsigned value
Serialize (local) constructor arguments.
type load(Archive &, unsigned)
void save(Archive &, const T &)
static const T * begin(const T(&array)[N])
static const T * end(const T(&array)[N])
std::string toString(T t) const
T fromString(const std::string &str) const
serialize_as_enum_impl(std::initializer_list< enum_string< T >> info_)
serialize_as_enum<T>
static void setPointer(T *&t, T *p, Archive &)
static T * getPointer(const std::shared_ptr< T > &t)
static void setPointer(std::shared_ptr< T > &t, T *p, Archive &ar)
static T * getPointer(const std::unique_ptr< T > &t)
static void setPointer(std::unique_ptr< T > &t, T *p, Archive &)
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition: xrange.hh:170
constexpr auto begin(const zstring_view &x)
Definition: zstring_view.hh:82
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:83