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 <array>
10#include <cassert>
11#include <initializer_list>
12#include <memory>
13#include <string>
14#include <type_traits>
15#include <variant>
16
17namespace openmsx {
18
19// Is there a specialized way to serialize 'T'.
20template<typename T> struct Serializer : std::false_type {
21 struct Saver {};
22 struct Loader {};
23};
24
25// Type-queries for serialization framework
26// is_primitive<T>
27template<typename T> struct is_primitive : std::false_type {};
28template<> struct is_primitive<bool> : std::true_type {};
29template<> struct is_primitive<char> : std::true_type {};
30template<> struct is_primitive<signed char> : std::true_type {};
31template<> struct is_primitive<signed short> : std::true_type {};
32template<> struct is_primitive<signed int> : std::true_type {};
33template<> struct is_primitive<signed long> : std::true_type {};
34template<> struct is_primitive<unsigned char> : std::true_type {};
35template<> struct is_primitive<unsigned short> : std::true_type {};
36template<> struct is_primitive<unsigned int> : std::true_type {};
37template<> struct is_primitive<unsigned long> : std::true_type {};
38template<> struct is_primitive<float> : std::true_type {};
39template<> struct is_primitive<double> : std::true_type {};
40template<> struct is_primitive<long double> : std::true_type {};
41template<> struct is_primitive<long long> : std::true_type {};
42template<> struct is_primitive<unsigned long long> : std::true_type {};
43template<> struct is_primitive<std::string> : std::true_type {};
44
45
46// Normally to make a class serializable, you have to implement a serialize()
47// method on the class. For some classes we cannot extend the source code. So
48// we need an alternative, non-intrusive, way to make those classes
49// serializable.
50template<typename Archive, typename T>
51void serialize(Archive& ar, T& t, unsigned version)
52{
53 // By default use the serialize() member. But this function can
54 // be overloaded to serialize classes in a non-intrusive way.
55 t.serialize(ar, version);
56}
57
58template<typename Archive, typename T1, typename T2>
59void serialize(Archive& ar, std::pair<T1, T2>& p, unsigned /*version*/)
60{
61 ar.serialize("first", p.first,
62 "second", p.second);
63}
64template<typename T1, typename T2> struct SerializeClassVersion<std::pair<T1, T2>>
65{
66 static constexpr unsigned value = 0;
67};
68
70
107template<typename T> struct serialize_as_enum : std::false_type {};
108
109template<typename T> struct enum_string {
110 const char* str;
111 T e;
112};
113[[noreturn]] void enumError(std::string_view str);
114
115template<typename T>
116inline std::string toString(std::initializer_list<enum_string<T>> list, T t_)
117{
118 for (auto& [str, t] : list) {
119 if (t == t_) return str;
120 }
121 assert(false);
122 return "internal-error-unknown-enum-value";
123}
124
125template<typename T>
126T fromString(std::initializer_list<enum_string<T>> list, std::string_view str_)
127{
128 for (auto& [str, t] : list) {
129 if (str == str_) return t;
130 }
131 enumError(str_); // does not return (throws)
132 return T(); // avoid warning
133}
134
135#define SERIALIZE_ENUM(TYPE,INFO) \
136template<> struct serialize_as_enum< TYPE > : std::true_type { \
137 serialize_as_enum() : info(INFO) {} \
138 std::initializer_list<enum_string< TYPE >> info; \
139};
140
141template<typename Archive, typename T, typename SaveAction>
142void saveEnum(std::initializer_list<enum_string<T>> list, T t, SaveAction save)
143{
144 if constexpr (Archive::TRANSLATE_ENUM_TO_STRING) {
145 save(toString(list, t));
146 } else {
147 save(int(t));
148 }
149}
150
151template<typename Archive, typename T, typename LoadAction>
152void loadEnum(std::initializer_list<enum_string<T>> list, T& t, LoadAction load)
153{
154 if constexpr (Archive::TRANSLATE_ENUM_TO_STRING) {
155 std::string str;
156 load(str);
157 t = fromString(list, str);
158 } else {
159 int i;
160 load(i);
161 t = T(i);
162 }
163}
164
166
167template<size_t I, typename Variant>
169 Variant operator()(size_t index) const {
170 if constexpr (I == std::variant_size_v<Variant>) {
172 } else if (index == I) {
173 return std::variant_alternative_t<I, Variant>{};
174 } else {
176 }
177 }
178};
179template<typename Variant>
180Variant defaultConstructVariant(size_t index)
181{
183}
184
185template<typename V> struct VariantSerializer : std::true_type
186{
187 template<typename A>
188 static constexpr size_t index = get_index<A, V>::value;
189
190 struct Saver {
191 template<typename Archive>
192 void operator()(Archive& ar, const V& v, bool saveId) const {
193 saveEnum<Archive>(Serializer<V>::list, v.index(),
194 [&](const auto& t) { ar.attribute("type", t); });
195 std::visit([&]<typename T>(T& e) {
196 using TNC = std::remove_cvref_t<T>;
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) const {
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([&]<typename T>(T& e) {
211 ClassLoader<T> 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
247template<typename T> struct serialize_as_pointer : std::false_type {};
248template<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};
255template<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};
264template<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};
273template<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
325template<typename T> struct serialize_as_collection : std::false_type {};
326
327template<typename T, size_t N> struct serialize_as_collection<std::array<T, N>> : std::true_type
328{
329 static constexpr int size = N; // fixed size
330 using value_type = T;
331 // save
332 using const_iterator = typename std::array<T, N>::const_iterator;
333 static auto begin(const std::array<T, N>& a) { return a.begin(); }
334 static auto end (const std::array<T, N>& a) { return a.end(); }
335 // load
336 static constexpr bool loadInPlace = true;
337 static void prepare(std::array<T, N>& /*a*/, int /*n*/) { }
338 static T* output(std::array<T, N>& a) { return a.data(); }
339};
340
342
343// Implementation of the different save-strategies.
344//
345// ATM we have
346// - PrimitiveSaver
347// Saves primitive values: int, bool, string, ...
348// Primitive values cannot be versioned.
349// - EnumSaver
350// Depending on the archive type, enums are either saved as strings (XML
351// archive) or as integers (memory archive).
352// This does not work automatically: it needs a specialization of
353// serialize_as_enum, see above.
354// - ClassSaver
355// From a serialization POV classes are a (heterogeneous) collection of
356// other to-be-serialized items.
357// Classes can have a version number, this allows to evolve the class
358// structure while still being able to load older versions (the load
359// method receive the version number as parameter, the user still needs
360// to keep the old loading code in place).
361// Optionally the name of the (concrete) class is saved in the stream.
362// This is used to support loading of polymorphic classes.
363// There is also an (optional) id saved in the stream. This used to
364// resolve possible multiple pointers to the same class.
365// - PointerSaver
366// Saves a pointer to a class (pointers to primitive types are not
367// supported). See also serialize_as_pointer
368// - IDSaver
369// Weaker version of PointerSaver: it can only save pointers to objects
370// that are already saved before (so it's will be saved by storing a
371// reference). To only reason to use IDSaver (iso PointerSaver) is that
372// it will not instantiate the object construction code.
373// - CollectionSaver
374// Saves a whole collection. See also serialize_as_collection
375//
376// All these strategies have a method:
377// template<typename Archive> void operator()(Archive& ar, const T& t)
378// 'ar' is archive where the serialized stream will go
379// 't' is the to-be-saved object
380// 'saveId' Should ID be saved
381
382template<typename T> struct PrimitiveSaver
383{
384 template<typename Archive> void operator()(Archive& ar, const T& t,
385 bool /*saveId*/) const
386 {
387 static_assert(is_primitive<T>::value, "must be primitive type");
388 ar.save(t);
389 }
390};
391template<typename T> struct EnumSaver
392{
393 template<typename Archive> void operator()(Archive& ar, const T& t,
394 bool /*saveId*/) const
395 {
397 saveEnum<Archive>(sae.info, t,
398 [&](const auto& s) { ar.save(s); });
399 }
400};
401template<typename T> struct ClassSaver
402{
403 template<typename Archive> void operator()(
404 Archive& ar, const T& t, bool saveId,
405 const char* type = nullptr, bool saveConstrArgs = false) const
406 {
407 // Order is important (for non-xml archives). We use this order:
408 // - id
409 // - type
410 // - version
411 // - constructor args
412 // Rational:
413 // - 'id' must be first: it could be nullptr, in that
414 // case the other fields are not even present.
415 // - 'type' must be before version, because for some types we
416 // might not need to store version (though it's unlikely)
417 // - version must be before constructor args because the
418 // constr args depend on the version
419 if (saveId) {
420 unsigned id = ar.generateId(&t);
421 ar.attribute("id", id);
422 }
423
424 if (type) {
425 ar.attribute("type", type);
426 }
427
428 unsigned version = SerializeClassVersion<T>::value;
429 if ((version != 0) && ar.NEED_VERSION) {
430 if (!ar.CAN_HAVE_OPTIONAL_ATTRIBUTES ||
431 (version != 1)) {
432 ar.attribute("version", version);
433 }
434 }
435
436 if (saveConstrArgs) {
437 // save local constructor args (if any)
439 constrArgs.save(ar, t);
440 }
441
442 using TNC = std::remove_const_t<T>;
443 auto& t2 = const_cast<TNC&>(t);
444 serialize(ar, t2, version);
445 }
446};
447template<typename TP> struct PointerSaver
448{
449 // note: we only support pointer to class
450 template<typename Archive> void operator()(Archive& ar, const TP& tp2,
451 bool /*saveId*/) const
452 {
454 "must be serialized as pointer");
455 using T = typename serialize_as_pointer<TP>::type;
456 const T* tp = serialize_as_pointer<TP>::getPointer(tp2);
457 if (!tp) {
458 unsigned id = 0;
459 ar.attribute("id_ref", id);
460 return;
461 }
462 if (unsigned id = ar.getId(tp)) {
463 ar.attribute("id_ref", id);
464 } else {
465 if constexpr (std::is_polymorphic_v<T>) {
467 } else {
468 ClassSaver<T> saver;
469 // don't store type
470 // store id, constr-args
471 saver(ar, *tp, true, nullptr, true);
472 }
473 }
474 }
475};
476template<typename TP> struct IDSaver
477{
478 template<typename Archive> void operator()(Archive& ar, const TP& tp2) const
479 {
481 "must be serialized as pointer");
483 unsigned id;
484 if (!tp) {
485 id = 0;
486 } else {
487 id = ar.getId(tp);
488 assert(id);
489 }
490 ar.attribute("id_ref", id);
491 }
492};
493template<typename TC> struct CollectionSaver
494{
495 template<typename Archive> void operator()(Archive& ar, const TC& tc,
496 bool saveId) const
497 {
498 using sac = serialize_as_collection<TC>;
499 static_assert(sac::value, "must be serialized as collection");
500 auto begin = sac::begin(tc);
501 auto end = sac::end (tc);
502 if ((sac::size < 0) && (!ar.CAN_COUNT_CHILDREN)) {
503 // variable size
504 // e.g. in an XML archive the loader can look-ahead and
505 // count the number of sub-tags, so no need to
506 // explicitly store the size for such archives.
507 int n = int(std::distance(begin, end));
508 ar.serialize("size", n);
509 }
510 for (; begin != end; ++begin) {
511 if (saveId) {
512 ar.serializeWithID("item", *begin);
513 } else {
514 ar.serialize("item", *begin);
515 }
516 }
517 }
518};
519
520// Delegate to a specific Saver class
521// (implemented as inheriting from a specific base class).
522template<typename T> struct Saver
523 : std::conditional_t<is_primitive<T>::value, PrimitiveSaver<T>,
524 std::conditional_t<Serializer<T>::value, typename Serializer<T>::Saver,
525 std::conditional_t<serialize_as_enum<T>::value, EnumSaver<T>,
526 std::conditional_t<serialize_as_pointer<T>::value, PointerSaver<T>,
527 std::conditional_t<serialize_as_collection<T>::value, CollectionSaver<T>,
528 ClassSaver<T>>>>>> {};
529
531
532// Implementation of the different load-strategies.
533//
534// This matches very closely with the save-strategies above.
535//
536// All these strategies have a method:
537// template<typename Archive, typename TUPLE>
538// void operator()(Archive& ar, const T& t, TUPLE args)
539// 'ar' Is archive where the serialized stream will go
540// 't' Is the object that has to be restored.
541// In case of a class (not a pointer to a class) the actual object
542// is already constructed, but it still needs to be filled in with
543// the correct data.
544// 'args' (Only used by PointerLoader) holds extra parameters used
545// to construct objects.
546// 'id' Used to skip loading an ID, see comment in ClassLoader
547
548template<typename T> struct PrimitiveLoader
549{
550 template<typename Archive, typename TUPLE>
551 void operator()(Archive& ar, T& t, TUPLE /*args*/, int /*id*/) const
552 {
553 static_assert(std::tuple_size_v<TUPLE> == 0,
554 "can't have constructor arguments");
555 ar.load(t);
556 }
557};
558template<typename T> struct EnumLoader
559{
560 template<typename Archive, typename TUPLE>
561 void operator()(Archive& ar, T& t, TUPLE /*args*/, int /*id*/) const
562 {
563 static_assert(std::tuple_size_v<TUPLE> == 0,
564 "can't have constructor arguments");
566 loadEnum<Archive>(sae.info, t, [&](auto& l) { ar.load(l); });
567 }
568};
569
570unsigned loadVersionHelper(MemInputArchive& ar, const char* className,
571 unsigned latestVersion);
572unsigned loadVersionHelper(XmlInputArchive& ar, const char* className,
573 unsigned latestVersion);
574template<typename T, typename Archive> unsigned loadVersion(Archive& ar)
575{
576 unsigned latestVersion = SerializeClassVersion<T>::value;
577 if ((latestVersion != 0) && ar.NEED_VERSION) {
578 return loadVersionHelper(ar, typeid(T).name(), latestVersion);
579 } else {
580 return latestVersion;
581 }
582}
583template<typename T> struct ClassLoader
584{
585 template<typename Archive, typename TUPLE>
586 void operator()(Archive& ar, T& t, TUPLE /*args*/, int id = 0,
587 int version = -1) const
588 {
589 static_assert(std::tuple_size_v<TUPLE> == 0,
590 "can't have constructor arguments");
591
592 // id == -1: don't load id, don't addPointer
593 // id == 0: load id from archive, addPointer
594 // id == N: id already loaded, still addPointer
595 if (id != -1) {
596 if (id == 0) {
597 ar.attribute("id", id);
598 }
599 ar.addPointer(id, &t);
600 }
601
602 // version == -1: load version
603 // version == N: version already loaded
604 if (version == -1) {
605 version = loadVersion<T>(ar);
606 }
607
608 using TNC = std::remove_const_t<T>;
609 auto& t2 = const_cast<TNC&>(t);
610 serialize(ar, t2, version);
611 }
612};
613template<typename T> struct NonPolymorphicPointerLoader
614{
615 template<typename Archive, typename GlobalTuple>
616 T* operator()(Archive& ar, unsigned id, GlobalTuple globalArgs)
617 {
618 int version = loadVersion<T>(ar);
619
620 // load (local) constructor args (if any)
621 using TNC = std::remove_const_t<T>;
622 using ConstrArgs = SerializeConstructorArgs<TNC>;
623 ConstrArgs constrArgs;
624 auto localArgs = constrArgs.load(ar, version);
625
626 // combine global and local constr args
627 auto args = std::tuple_cat(globalArgs, localArgs);
628 // TODO make combining global/local constr args configurable
629
630 Creator<T> creator;
631 auto tp = creator(args);
632 ClassLoader<T> loader;
633 loader(ar, *tp, std::tuple<>(), id, version);
634 return tp.release();
635 }
636};
637template<typename T> struct PolymorphicPointerLoader
638{
639 template<typename Archive, typename TUPLE>
640 T* operator()(Archive& ar, unsigned id, TUPLE args)
641 {
642 using ArgsType = typename PolymorphicConstructorArgs<T>::type;
643 static_assert(std::is_same_v<TUPLE, ArgsType>,
644 "constructor arguments types must match");
645 return static_cast<T*>(
647 }
648};
649template<typename T> struct PointerLoader2
650 // extra indirection needed because inlining the body of
651 // NonPolymorphicPointerLoader in PointerLoader does not compile
652 // for abstract types
653 : std::conditional_t<std::is_polymorphic_v<T>,
654 PolymorphicPointerLoader<T>,
655 NonPolymorphicPointerLoader<T>> {};
656
657template<typename TP> struct PointerLoader
658{
659 template<typename Archive, typename GlobalTuple>
660 void operator()(Archive& ar, TP& tp2, GlobalTuple globalArgs, int /*id*/) const
661 {
663 "must be serialized as a pointer");
664 // in XML archives we use 'id_ref' or 'id', in other archives
665 // we don't care about the name
666 unsigned id = [&] {
667 if constexpr (Archive::CAN_HAVE_OPTIONAL_ATTRIBUTES) {
668 if (auto i = ar.template findAttributeAs<unsigned>("id_ref")) {
669 return *i;
670 }
671 }
672 unsigned i;
673 ar.attribute("id", i);
674 return i;
675 }();
676
677 using T = typename serialize_as_pointer<TP>::type;
678 T* tp = [&]() -> T* {
679 if (id == 0) {
680 return nullptr;
681 } else {
682 if (void* p = ar.getPointer(id)) {
683 return static_cast<T*>(p);
684 } else {
685 PointerLoader2<T> loader;
686 return loader(ar, id, globalArgs);
687 }
688 }
689 }();
691 }
692};
693[[noreturn]] void pointerError(unsigned id);
694template<typename TP> struct IDLoader
695{
696 template<typename Archive>
697 void operator()(Archive& ar, TP& tp2) const
698 {
700 "must be serialized as a pointer");
701 unsigned id;
702 ar.attribute("id_ref", id);
703
704 using T = typename serialize_as_pointer<TP>::type;
705 T* tp;
706 if (id == 0) {
707 tp = nullptr;
708 } else {
709 void* p = ar.getPointer(id);
710 if (!p) {
711 pointerError(id);
712 }
713 tp = static_cast<T*>(p);
714 }
716 }
717};
718
719template<typename sac, bool IN_PLACE = sac::loadInPlace> struct CollectionLoaderHelper;
720template<typename sac> struct CollectionLoaderHelper<sac, true>
721{
722 // used for array and vector
723 template<typename Archive, typename TUPLE, typename OUT_ITER>
724 void operator()(Archive& ar, TUPLE args, OUT_ITER it, int id) const
725 {
726 ar.doSerialize("item", *it, args, id);
727 }
728};
729template<typename sac> struct CollectionLoaderHelper<sac, false>
730{
731 // We can't directly load the element in the correct position:
732 // This screws-up id/pointer management because the element is still
733 // copied after construction (and pointer value of initial object is
734 // stored).
735 template<typename Archive, typename TUPLE, typename OUT_ITER>
736 void operator()(Archive& ar, TUPLE args, OUT_ITER it, int id) const
737 {
738 typename sac::value_type elem;
739 ar.doSerialize("item", elem, args, id);
740 *it = std::move(elem);
741 }
742};
743template<typename TC> struct CollectionLoader
744{
745 template<typename Archive, typename TUPLE>
746 void operator()(Archive& ar, TC& tc, TUPLE args, int id = 0) const
747 {
748 assert(id == one_of(0, -1));
749 using sac = serialize_as_collection<TC>;
750 static_assert(sac::value, "must be serialized as a collection");
751 int n = sac::size;
752 if (n < 0) {
753 // variable size
754 if constexpr (Archive::CAN_COUNT_CHILDREN) {
755 n = ar.countChildren();
756 } else {
757 ar.serialize("size", n);
758 }
759 }
760 sac::prepare(tc, n);
761 auto it = sac::output(tc);
762 CollectionLoaderHelper<sac> loadOneElement;
763 repeat(n, [&] {
764 loadOneElement(ar, args, it, id);
765 ++it;
766 });
767 }
768};
769template<typename T> struct Loader
770 : std::conditional_t<is_primitive<T>::value, PrimitiveLoader<T>,
771 std::conditional_t<Serializer<T>::value, typename Serializer<T>::Loader,
772 std::conditional_t<serialize_as_enum<T>::value, EnumLoader<T>,
773 std::conditional_t<serialize_as_pointer<T>::value, PointerLoader<T>,
774 std::conditional_t<serialize_as_collection<T>::value, CollectionLoader<T>,
775 ClassLoader<T>>>>>> {};
776
777} // namespace openmsx
778
779#endif
uintptr_t id
TclObject t
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:11
void enumError(std::string_view str)
void saveEnum(std::initializer_list< enum_string< T > > list, T t, SaveAction save)
Variant defaultConstructVariant(size_t index)
unsigned loadVersion(Archive &ar)
T fromString(std::initializer_list< enum_string< T > > list, std::string_view str_)
void serialize(Archive &ar, T &t, unsigned version)
std::string toString(const BooleanInput &input)
void pointerError(unsigned id)
unsigned loadVersionHelper(MemInputArchive &, const char *, unsigned)
void loadEnum(std::initializer_list< enum_string< T > > list, T &t, LoadAction load)
STL namespace.
void operator()(Archive &ar, T &t, TUPLE, int id=0, int version=-1) const
void operator()(Archive &ar, const T &t, bool saveId, const char *type=nullptr, bool saveConstrArgs=false) const
void operator()(Archive &ar, TUPLE args, OUT_ITER it, int id) const
void operator()(Archive &ar, TUPLE args, OUT_ITER it, int id) const
void operator()(Archive &ar, TC &tc, TUPLE args, int id=0) const
void operator()(Archive &ar, const TC &tc, bool saveId) const
Utility to do T* t = new T(...)
Variant operator()(size_t index) const
void operator()(Archive &ar, T &t, TUPLE, int) const
void operator()(Archive &ar, const T &t, bool) const
void operator()(Archive &ar, TP &tp2) const
void operator()(Archive &ar, const TP &tp2) const
T * operator()(Archive &ar, unsigned id, GlobalTuple globalArgs)
void operator()(Archive &ar, TP &tp2, GlobalTuple globalArgs, int) const
void operator()(Archive &ar, const TP &tp2, bool) const
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) const
void operator()(Archive &ar, const T &t, bool) const
Store serialization-version number of a class.
static constexpr unsigned value
Serialize (local) constructor arguments.
type load(Archive &, unsigned) const
void save(Archive &, const T &) const
void operator()(Archive &ar, V &v, TUPLE args, int id) const
void operator()(Archive &ar, const V &v, bool saveId) const
static constexpr size_t index
static auto end(const std::array< T, N > &a)
static void prepare(std::array< T, N > &, int)
static auto begin(const std::array< T, N > &a)
typename std::array< T, N >::const_iterator const_iterator
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
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition xrange.hh:147
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)