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