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