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