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