serialize_core.hh 23 KB

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