openMSX
serialize_meta.hh
Go to the documentation of this file.
1 #ifndef SERIALIZE_META_HH
2 #define SERIALIZE_META_HH
3 
4 #include "hash_map.hh"
5 #include "likely.hh"
6 #include "xxhash.hh"
7 #include <memory>
8 #include <tuple>
9 #include <typeindex>
10 #include <type_traits>
11 #include <vector>
12 
13 namespace openmsx {
14 
29 template<typename T> class Creator
30 {
31 public:
32  template<typename TUPLE>
33  std::unique_ptr<T> operator()(TUPLE args) {
34  DoInstantiate<std::tuple_size<TUPLE>::value, TUPLE> inst;
35  return inst(args);
36  }
37 
38 private:
39  template<int I, typename TUPLE> struct DoInstantiate;
40  template<typename TUPLE> struct DoInstantiate<0, TUPLE> {
41  std::unique_ptr<T> operator()(TUPLE /*args*/) {
42  return std::make_unique<T>();
43  }
44  };
45  template<typename TUPLE> struct DoInstantiate<1, TUPLE> {
46  std::unique_ptr<T> operator()(TUPLE args) {
47  return std::make_unique<T>(std::get<0>(args));
48  }
49  };
50  template<typename TUPLE> struct DoInstantiate<2, TUPLE> {
51  std::unique_ptr<T> operator()(TUPLE args) {
52  return std::make_unique<T>(
53  std::get<0>(args), std::get<1>(args));
54  }
55  };
56  template<typename TUPLE> struct DoInstantiate<3, TUPLE> {
57  std::unique_ptr<T> operator()(TUPLE args) {
58  return std::make_unique<T>(
59  std::get<0>(args), std::get<1>(args),
60  std::get<2>(args));
61  }
62  };
63 };
64 
66 
67 // Polymorphic class loader/saver
68 
69 // forward declarations
70 // ClassSaver: used to save actually save a class. We also store the name of
71 // the class so that the loader knows which concrete class it should load.
72 template<typename T> struct ClassSaver;
73 // NonPolymorphicPointerLoader: once we know which concrete type to load,
74 // we use the 'normal' class loader to load it.
75 template<typename T> struct NonPolymorphicPointerLoader;
76 // Used by PolymorphicInitializer to initialize a concrete type.
77 template<typename T> struct ClassLoader;
78 
84 template<typename T> struct PolymorphicConstructorArgs;
85 
89 template<typename T> struct PolymorphicBaseClass;
90 
91 template<typename Base> struct MapConstrArgsEmpty
92 {
94  std::tuple<> operator()(const TUPLEIn& /*t*/)
95  {
96  return std::make_tuple();
97  }
98 };
99 template<typename Base, typename Derived> struct MapConstrArgsCopy
100 {
103  static_assert(std::is_same<TUPLEIn, TUPLEOut>::value,
104  "constructor argument types must match");
106  {
107  return t;
108  }
109 };
110 
123 template<typename Base, typename Derived> struct MapConstructorArguments
124  : std::conditional_t<std::is_same<std::tuple<>,
125  typename PolymorphicConstructorArgs<Derived>::type>::value,
126  MapConstrArgsEmpty<Base>,
127  MapConstrArgsCopy<Base, Derived>> {};
128 
135 template<typename Base> struct BaseClassName;
136 
137 template<typename Archive> class PolymorphicSaverBase
138 {
139 public:
141  virtual void save(Archive& ar, const void* p) const = 0;
142 };
143 
144 template<typename Archive> class PolymorphicLoaderBase
145 {
146 public:
148  virtual void* load(Archive& ar, unsigned id, const void* args) const = 0;
149 };
150 
151 template<typename Archive> class PolymorphicInitializerBase
152 {
153 public:
155  virtual void init(Archive& ar, void* t, unsigned id) const = 0;
156 };
157 
158 template<typename Archive, typename T>
159 class PolymorphicSaver : public PolymorphicSaverBase<Archive>
160 {
161 public:
162  explicit PolymorphicSaver(const char* name_)
163  : name(name_)
164  {
165  }
166  void save(Archive& ar, const void* v) const override
167  {
168  using BaseType = typename PolymorphicBaseClass<T>::type;
169  auto base = static_cast<const BaseType*>(v);
170  auto tp = static_cast<const T*>(base);
171  ClassSaver<T> saver;
172  saver(ar, *tp, true, name, true); // save id, type, constr-args
173  }
174 private:
175  const char* name;
176 };
177 
178 template<typename Archive, typename T>
180 {
181 public:
182  void* load(Archive& ar, unsigned id, const void* args) const override
183  {
184  using BaseType = typename PolymorphicBaseClass<T>::type;
185  using TUPLEIn = typename PolymorphicConstructorArgs<BaseType>::type;
186  using TUPLEOut = typename PolymorphicConstructorArgs<T>::type;
187  auto& argsIn = *static_cast<const TUPLEIn*>(args);
189  TUPLEOut argsOut = mapArgs(argsIn);
191  return loader(ar, id, argsOut);
192  }
193 };
194 
195 void polyInitError(const char* expected, const char* actual);
196 template<typename Archive, typename T>
198 {
199 public:
200  void init(Archive& ar, void* v, unsigned id) const override
201  {
202  using BaseType = typename PolymorphicBaseClass<T>::type;
203  auto base = static_cast<BaseType*>(v);
204  if (unlikely(dynamic_cast<T*>(base) != static_cast<T*>(base))) {
205  polyInitError(typeid(T).name(), typeid(*base).name());
206  }
207  auto t = static_cast<T*>(base);
208  ClassLoader<T> loader;
209  loader(ar, *t, std::make_tuple(), id);
210  }
211 };
212 
213 
214 template<typename Archive>
216 {
217 public:
218  static PolymorphicSaverRegistry& instance();
219 
220  template<typename T> void registerClass(const char* name)
221  {
222  static_assert(std::is_polymorphic<T>::value,
223  "must be a polymorphic type");
224  static_assert(!std::is_abstract<T>::value,
225  "can't be an abstract type");
226  registerHelper(typeid(T),
227  std::make_unique<PolymorphicSaver<Archive, T>>(name));
228  }
229 
230  template<typename T> static void save(Archive& ar, T* t)
231  {
232  save(ar, t, typeid(*t));
233  }
234  template<typename T> static void save(const char* tag, Archive& ar, T& t)
235  {
236  save(tag, ar, &t, typeid(t));
237  }
238 
239 private:
240  PolymorphicSaverRegistry() = default;
241  ~PolymorphicSaverRegistry() = default;
242  void registerHelper(const std::type_info& type,
243  std::unique_ptr<PolymorphicSaverBase<Archive>> saver);
244  static void save(Archive& ar, const void* t,
245  const std::type_info& typeInfo);
246  static void save(const char* tag, Archive& ar, const void* t,
247  const std::type_info& typeInfo);
248 
249  std::vector<std::pair<std::type_index,
250  std::unique_ptr<PolymorphicSaverBase<Archive>>>> saverMap;
251  bool initialized = false;
252 };
253 
254 template<typename Archive>
256 {
257 public:
258  static PolymorphicLoaderRegistry& instance();
259 
260  template<typename T> void registerClass(const char* name)
261  {
262  static_assert(std::is_polymorphic<T>::value,
263  "must be a polymorphic type");
264  static_assert(!std::is_abstract<T>::value,
265  "can't be an abstract type");
266  registerHelper(name,
267  std::make_unique<PolymorphicLoader<Archive, T>>());
268  }
269 
270  static void* load(Archive& ar, unsigned id, const void* args);
271 
272 private:
273  PolymorphicLoaderRegistry() = default;
274  ~PolymorphicLoaderRegistry() = default;
275  void registerHelper(
276  const char* name,
277  std::unique_ptr<PolymorphicLoaderBase<Archive>> loader);
278 
280  loaderMap;
281 };
282 
283 template<typename Archive>
285 {
286 public:
287  static PolymorphicInitializerRegistry& instance();
288 
289  template<typename T> void registerClass(const char* name)
290  {
291  static_assert(std::is_polymorphic<T>::value,
292  "must be a polymorphic type");
293  static_assert(!std::is_abstract<T>::value,
294  "can't be an abstract type");
295  registerHelper(name,
296  std::make_unique<PolymorphicInitializer<Archive, T>>());
297  }
298 
299  static void init(const char* tag, Archive& ar, void* t);
300 
301 private:
302  PolymorphicInitializerRegistry() = default;
303  ~PolymorphicInitializerRegistry() = default;
304  void registerHelper(
305  const char* name,
306  std::unique_ptr<PolymorphicInitializerBase<Archive>> initializer);
307 
309  initializerMap;
310 };
311 
312 
313 template<typename Archive, typename T> struct RegisterSaverHelper
314 {
315  explicit RegisterSaverHelper(const char* name)
316  {
318  template registerClass<T>(name);
319  }
320 };
321 template<typename Archive, typename T> struct RegisterLoaderHelper
322 {
323  explicit RegisterLoaderHelper(const char* name)
324  {
326  template registerClass<T>(name);
327  }
328 };
329 template<typename Archive, typename T> struct RegisterInitializerHelper
330 {
331  explicit RegisterInitializerHelper(const char* name)
332  {
334  template registerClass<T>(name);
335  }
336 };
337 
338 #define REGISTER_CONSTRUCTOR_ARGS_0(C) \
339 template<> struct PolymorphicConstructorArgs<C> \
340 { using type = std::tuple<>; };
341 
342 #define REGISTER_CONSTRUCTOR_ARGS_1(C,T1) \
343 template<> struct PolymorphicConstructorArgs<C> \
344 { using type = std::tuple<T1>; };
345 
346 #define REGISTER_CONSTRUCTOR_ARGS_2(C,T1,T2) \
347 template<> struct PolymorphicConstructorArgs<C> \
348 { using type = std::tuple<T1,T2>; };
349 
350 #define REGISTER_CONSTRUCTOR_ARGS_3(C,T1,T2,T3) \
351 template<> struct PolymorphicConstructorArgs<C> \
352 { using type = std::tuple<T1,T2,T3>; };
353 
354 class MemInputArchive;
355 class MemOutputArchive;
356 class XmlInputArchive;
357 class XmlOutputArchive;
358 
359 /*#define REGISTER_POLYMORPHIC_CLASS_HELPER(B,C,N) \
360 static_assert(std::is_base_of<B,C>::value, "must be base and sub class"); \
361 static RegisterLoaderHelper<TextInputArchive, C> registerHelper1##C(N); \
362 static RegisterSaverHelper <TextOutputArchive, C> registerHelper2##C(N); \
363 static RegisterLoaderHelper<XmlInputArchive, C> registerHelper3##C(N); \
364 static RegisterSaverHelper <XmlOutputArchive, C> registerHelper4##C(N); \
365 static RegisterLoaderHelper<MemInputArchive, C> registerHelper5##C(N); \
366 static RegisterSaverHelper <MemOutputArchive, C> registerHelper6##C(N); \*/
367 #define REGISTER_POLYMORPHIC_CLASS_HELPER(B,C,N) \
368 static_assert(std::is_base_of<B,C>::value, "must be base and sub class"); \
369 static RegisterLoaderHelper<MemInputArchive, C> registerHelper3##C(N); \
370 static RegisterSaverHelper <MemOutputArchive, C> registerHelper4##C(N); \
371 static RegisterLoaderHelper<XmlInputArchive, C> registerHelper5##C(N); \
372 static RegisterSaverHelper <XmlOutputArchive, C> registerHelper6##C(N); \
373 template<> struct PolymorphicBaseClass<C> { using type = B; };
374 
375 #define REGISTER_POLYMORPHIC_INITIALIZER_HELPER(B,C,N) \
376 static_assert(std::is_base_of<B,C>::value, "must be base and sub class"); \
377 static RegisterInitializerHelper<MemInputArchive, C> registerHelper3##C(N); \
378 static RegisterSaverHelper <MemOutputArchive, C> registerHelper4##C(N); \
379 static RegisterInitializerHelper<XmlInputArchive, C> registerHelper5##C(N); \
380 static RegisterSaverHelper <XmlOutputArchive, C> registerHelper6##C(N); \
381 template<> struct PolymorphicBaseClass<C> { using type = B; };
382 
383 #define REGISTER_BASE_NAME_HELPER(B,N) \
384 template<> struct BaseClassName<B> \
385 { static const char* getName() { static const char* name = N; return name; } };
386 
387 // public macros
388 // these are a more convenient way to define specializations of the
389 // PolymorphicConstructorArgs and PolymorphicBaseClass classes
390 #define REGISTER_POLYMORPHIC_CLASS(BASE,CLASS,NAME) \
391  REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
392  REGISTER_CONSTRUCTOR_ARGS_0(CLASS)
393 
394 #define REGISTER_POLYMORPHIC_CLASS_1(BASE,CLASS,NAME,TYPE1) \
395  REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
396  REGISTER_CONSTRUCTOR_ARGS_1(CLASS,TYPE1)
397 
398 #define REGISTER_POLYMORPHIC_CLASS_2(BASE,CLASS,NAME,TYPE1,TYPE2) \
399  REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
400  REGISTER_CONSTRUCTOR_ARGS_2(CLASS,TYPE1,TYPE2)
401 
402 #define REGISTER_POLYMORPHIC_CLASS_3(BASE,CLASS,NAME,TYPE1,TYPE2,TYPE3) \
403  REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
404  REGISTER_CONSTRUCTOR_ARGS_3(CLASS,TYPE1,TYPE2,TYPE3)
405 
406 #define REGISTER_BASE_CLASS(CLASS,NAME) \
407  REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
408  REGISTER_CONSTRUCTOR_ARGS_0(CLASS)
409 
410 #define REGISTER_BASE_CLASS_1(CLASS,NAME,TYPE1) \
411  REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
412  REGISTER_CONSTRUCTOR_ARGS_1(CLASS,TYPE1)
413 
414 #define REGISTER_BASE_CLASS_2(CLASS,NAME,TYPE1,TYPE2) \
415  REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
416  REGISTER_CONSTRUCTOR_ARGS_2(CLASS,TYPE1,TYPE2)
417 
418 #define REGISTER_BASE_CLASS_3(CLASS,NAME,TYPE1,TYPE2,TYPE3) \
419  REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
420  REGISTER_CONSTRUCTOR_ARGS_3(CLASS,TYPE1,TYPE2,TYPE3)
421 
422 
423 #define REGISTER_POLYMORPHIC_INITIALIZER(BASE,CLASS,NAME) \
424  REGISTER_POLYMORPHIC_INITIALIZER_HELPER(BASE,CLASS,NAME)
425 
427 
443 template<typename T> struct SerializeClassVersion
444 {
445  static const unsigned value = 1;
446 };
447 #define SERIALIZE_CLASS_VERSION(CLASS, VERSION) \
448 template<> struct SerializeClassVersion<CLASS> \
449 { \
450  static const unsigned value = VERSION; \
451 };
452 
453 } // namespace openmsx
454 
455 #endif
typename PolymorphicConstructorArgs< Derived >::type TUPLEOut
std::unique_ptr< T > operator()(TUPLE args)
void polyInitError(const char *expected, const char *actual)
std::tuple operator()(const TUPLEIn &)
#define unlikely(x)
Definition: likely.hh:15
Define mapping between constructor arg list of base- and subclass.
static PolymorphicInitializerRegistry & instance()
Store association between (polymorphic) sub- and baseclass.
static PolymorphicLoaderRegistry & instance()
Store association between polymorphic class (base- or subclass) and the list of constructor arguments...
Utility to do T* t = new T(...)
static void save(const char *tag, Archive &ar, T &t)
static PolymorphicSaverRegistry & instance()
Stores the name of a base class.
void init(Archive &ar, void *v, unsigned id) const override
PolymorphicSaver(const char *name_)
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
RegisterInitializerHelper(const char *name)
XMLElement load(string_view filename, string_view systemID)
Definition: XMLLoader.cc:31
RegisterSaverHelper(const char *name)
void save(Archive &ar, const void *v) const override
void save(SDL_Surface *image, const std::string &filename)
Definition: PNG.cc:366
typename PolymorphicConstructorArgs< Base >::type TUPLEIn
RegisterLoaderHelper(const char *name)
typename PolymorphicConstructorArgs< Base >::type TUPLEIn
void * load(Archive &ar, unsigned id, const void *args) const override
void registerClass(const char *name)
TUPLEOut operator()(const TUPLEIn &t)
void registerClass(const char *name)
static void save(Archive &ar, T *t)