OXYGENHUB
◆ main openmsx / src / serialize_meta.hh
13.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef SERIALIZE_META_HH
2
#define SERIALIZE_META_HH
3
4
#include "hash_map.hh"
5
#include "xxhash.hh"
6
7
#include <memory>
8
#include <tuple>
9
#include <type_traits>
10
#include <typeindex>
11
#include <utility>
12
#include <vector>
13
14
namespace openmsx {
15
16
/** Utility to do     T* t = new T(...)
17
 *
18
 * The tricky part is that the constructor of T can have a variable number
19
 * of parameters.
20
 *
21
 * For example:
22
 *       Creator<Foo> creator;
23
 *       auto args = std::tuple(42, 3.14);
24
 *       std::unique_ptr<Foo> foo = creator(args);
25
 * This is equivalent to
26
 *       auto foo = std::make_unique<Foo>(42, 3.14);
27
 * But the former can be used in a generic context (where the number of
28
 * constructor parameters is unknown).
29
 */
30
template<typename T> struct Creator
31
{
32
	template<typename TUPLE>
33
	std::unique_ptr<T> operator()(TUPLE tuple) {
34
		auto makeT = []<typename... Args>(Args&& ...args) {
35
			return std::make_unique<T>(std::forward<Args>(args)...);
36
		};
37
		return std::apply(makeT, tuple);
38
	}
39
};
40
41
///////////////////////////////
42
43
// Polymorphic class loader/saver
44
45
// forward declarations
46
// ClassSaver: used to actually save a class. We also store the name of
47
//   the class so that the loader knows which concrete class it should load.
48
template<typename T> struct ClassSaver;
49
// NonPolymorphicPointerLoader: once we know which concrete type to load,
50
//   we use the 'normal' class loader to load it.
51
template<typename T> struct NonPolymorphicPointerLoader;
52
// Used by PolymorphicInitializer to initialize a concrete type.
53
template<typename T> struct ClassLoader;
54
55
/** Store association between polymorphic class (base- or subclass) and
56
 *  the list of constructor arguments.
57
 * Specializations of this class should store the constructor arguments
58
 * as a 'using type = tuple<...>'.
59
 */
60
template<typename T> struct PolymorphicConstructorArgs;
61
62
/** Store association between (polymorphic) sub- and base class.
63
 * Specialization of this class should provide a 'using type = <base>'.
64
 */
65
template<typename T> struct PolymorphicBaseClass;
66
67
template<typename Base> struct MapConstrArgsEmpty
68
{
69
	using TUPLEIn = typename PolymorphicConstructorArgs<Base>::type;
70
	std::tuple<> operator()(const TUPLEIn& /*t*/) const
71
	{
72
		return {};
73
	}
74
};
75
template<typename Base, typename Derived> struct MapConstrArgsCopy
76
{
77
	using TUPLEIn  = typename PolymorphicConstructorArgs<Base>::type;
78
	using TUPLEOut = typename PolymorphicConstructorArgs<Derived>::type;
79
	static_assert(std::is_same_v<TUPLEIn, TUPLEOut>,
80
	              "constructor argument types must match");
81
	TUPLEOut operator()(const TUPLEIn& t)
82
	{
83
		return t;
84
	}
85
};
86
87
/** Define mapping between constructor arg list of base- and subclass.
88
 *
89
 * When loading a polymorphic base class, the user must provide the union of
90
 * constructor arguments for all subclasses (because it's not yet known which
91
 * concrete subtype will be deserialized). This class defines the mapping
92
 * between this union of parameters and the subset used for a specific
93
 * subclass.
94
 *
95
 * In case the parameter list of the subclass is empty or if it is the same
96
 * as the base class, this mapping will be defined automatically. In the other
97
 * cases, the user must define a specialization of this class.
98
 */
99
template<typename Base, typename Derived> struct MapConstructorArguments
100
	: std::conditional_t<std::is_same_v<std::tuple<>,
101
	                                    typename PolymorphicConstructorArgs<Derived>::type>,
102
	      MapConstrArgsEmpty<Base>,
103
	      MapConstrArgsCopy<Base, Derived>> {};
104
105
/** Stores the name of a base class.
106
 * This name is used as tag-name in XML archives.
107
 *
108
 * Specializations of this class should provide a function
109
 *   static const char* getName()
110
 */
111
template<typename Base> struct BaseClassName;
112
113
[[noreturn]] void polyInitError(const char* expected, const char* actual);
114
115
template<typename Archive>
116
class PolymorphicSaverRegistry
117
{
118
public:
119
	PolymorphicSaverRegistry(const PolymorphicSaverRegistry&) = delete;
120
	PolymorphicSaverRegistry(PolymorphicSaverRegistry&&) = delete;
121
	PolymorphicSaverRegistry& operator=(const PolymorphicSaverRegistry&) = delete;
122
	PolymorphicSaverRegistry& operator=(PolymorphicSaverRegistry&&) = delete;
123
124
	static PolymorphicSaverRegistry& instance();
125
126
	template<typename T> void registerClass(const char* name)
127
	{
128
		static_assert(std::is_polymorphic_v<T>,
129
		              "must be a polymorphic type");
130
		static_assert(!std::is_abstract_v<T>,
131
		              "can't be an abstract type");
132
		registerHelper(typeid(T), [name](Archive& ar, const void* v) {
133
			using BaseType = typename PolymorphicBaseClass<T>::type;
134
			auto base = static_cast<const BaseType*>(v);
135
			auto tp = static_cast<const T*>(base);
136
			ClassSaver<T> saver;
137
			saver(ar, *tp, true, name, true); // save id, type, constr-args
138
		});
139
	}
140
141
	template<typename T> static void save(Archive& ar, T* t)
142
	{
143
		save(ar, t, typeid(*t));
144
	}
145
	template<typename T> static void save(const char* tag, Archive& ar, T& t)
146
	{
147
		save(tag, ar, &t, typeid(t));
148
	}
149
150
private:
151
	PolymorphicSaverRegistry() = default;
152
	~PolymorphicSaverRegistry() = default;
153
154
	using SaveFunction = std::function<void(Archive&, const void*)>;
155
	void registerHelper(const std::type_info& type,
156
	                    SaveFunction saver);
157
	static void save(Archive& ar, const void* t,
158
	                 const std::type_info& typeInfo);
159
	static void save(const char* tag, Archive& ar, const void* t,
160
	                 const std::type_info& typeInfo);
161
162
	struct Entry {
163
		std::type_index index;
164
		SaveFunction saver;
165
	};
166
	std::vector<Entry> saverMap;
167
	bool initialized = false;
168
};
169
170
template<typename Archive>
171
class PolymorphicLoaderRegistry
172
{
173
public:
174
	PolymorphicLoaderRegistry(const PolymorphicLoaderRegistry&) = delete;
175
	PolymorphicLoaderRegistry(PolymorphicLoaderRegistry&&) = delete;
176
	PolymorphicLoaderRegistry& operator=(const PolymorphicLoaderRegistry&) = delete;
177
	PolymorphicLoaderRegistry& operator=(PolymorphicLoaderRegistry&&) = delete;
178
179
	static PolymorphicLoaderRegistry& instance();
180
181
	template<typename T> void registerClass(const char* name)
182
	{
183
		static_assert(std::is_polymorphic_v<T>,
184
		              "must be a polymorphic type");
185
		static_assert(!std::is_abstract_v<T>,
186
		              "can't be an abstract type");
187
		registerHelper(name, [](Archive& ar, unsigned id, const void* args) {
188
			using BaseType = typename PolymorphicBaseClass<T>::type;
189
			using TUPLEIn  = typename PolymorphicConstructorArgs<BaseType>::type;
190
			using TUPLEOut = typename PolymorphicConstructorArgs<T>::type;
191
			auto& argsIn = *static_cast<const TUPLEIn*>(args);
192
			MapConstructorArguments<BaseType, T> mapArgs;
193
			TUPLEOut argsOut = mapArgs(argsIn);
194
			NonPolymorphicPointerLoader<T> loader;
195
			return loader(ar, id, argsOut);
196
		});
197
	}
198
199
	static void* load(Archive& ar, unsigned id, const void* args);
200
201
private:
202
	PolymorphicLoaderRegistry() = default;
203
	~PolymorphicLoaderRegistry() = default;
204
205
	using LoadFunction = std::function<void*(Archive&, unsigned, const void*)>;
206
	void registerHelper(const char* name, LoadFunction loader);
207
208
	hash_map<std::string_view, LoadFunction, XXHasher> loaderMap;
209
};
210
211
template<typename Archive>
212
class PolymorphicInitializerRegistry
213
{
214
public:
215
	PolymorphicInitializerRegistry(const PolymorphicInitializerRegistry&) = delete;
216
	PolymorphicInitializerRegistry(PolymorphicInitializerRegistry&&) = delete;
217
	PolymorphicInitializerRegistry& operator=(const PolymorphicInitializerRegistry&) = delete;
218
	PolymorphicInitializerRegistry& operator=(PolymorphicInitializerRegistry&&) = delete;
219
220
	static PolymorphicInitializerRegistry& instance();
221
222
	template<typename T> void registerClass(const char* name)
223
	{
224
		static_assert(std::is_polymorphic_v<T>,
225
		              "must be a polymorphic type");
226
		static_assert(!std::is_abstract_v<T>,
227
		              "can't be an abstract type");
228
		registerHelper(name, [](Archive& ar, void* v, unsigned id) {
229
			using BaseType = typename PolymorphicBaseClass<T>::type;
230
			auto base = static_cast<BaseType*>(v);
231
			if (dynamic_cast<T*>(base) != static_cast<T*>(base)) [[unlikely]] {
232
				polyInitError(typeid(T).name(), typeid(*base).name());
233
			}
234
			auto t = static_cast<T*>(base);
235
			ClassLoader<T> loader;
236
			loader(ar, *t, std::tuple<>(), id);
237
		});
238
	}
239
240
	static void init(const char* tag, Archive& ar, void* t);
241
242
private:
243
	PolymorphicInitializerRegistry() = default;
244
	~PolymorphicInitializerRegistry() = default;
245
246
	using InitFunction = std::function<void(Archive&, void*, unsigned)>;
247
	void registerHelper(const char* name, InitFunction initializer);
248
249
	hash_map<std::string_view, InitFunction, XXHasher> initializerMap;
250
};
251
252
253
template<typename Archive, typename T> struct RegisterSaverHelper
254
{
255
	explicit RegisterSaverHelper(const char* name)
256
	{
257
		PolymorphicSaverRegistry<Archive>::instance().
258
			template registerClass<T>(name);
259
	}
260
};
261
template<typename Archive, typename T> struct RegisterLoaderHelper
262
{
263
	explicit RegisterLoaderHelper(const char* name)
264
	{
265
		PolymorphicLoaderRegistry<Archive>::instance().
266
			template registerClass<T>(name);
267
	}
268
};
269
template<typename Archive, typename T> struct RegisterInitializerHelper
270
{
271
	explicit RegisterInitializerHelper(const char* name)
272
	{
273
		PolymorphicInitializerRegistry<Archive>::instance().
274
			template registerClass<T>(name);
275
	}
276
};
277
278
#define REGISTER_CONSTRUCTOR_ARGS_0(C) \
279
template<> struct PolymorphicConstructorArgs<C> \
280
{ using type = std::tuple<>; };
281
282
#define REGISTER_CONSTRUCTOR_ARGS_1(C,T1) \
283
template<> struct PolymorphicConstructorArgs<C> \
284
{ using type = std::tuple<T1>; };
285
286
#define REGISTER_CONSTRUCTOR_ARGS_2(C,T1,T2) \
287
template<> struct PolymorphicConstructorArgs<C> \
288
{ using type = std::tuple<T1,T2>; };
289
290
#define REGISTER_CONSTRUCTOR_ARGS_3(C,T1,T2,T3) \
291
template<> struct PolymorphicConstructorArgs<C> \
292
{ using type = std::tuple<T1,T2,T3>; };
293
294
class MemInputArchive;
295
class MemOutputArchive;
296
class XmlInputArchive;
297
class XmlOutputArchive;
298
299
/*#define REGISTER_POLYMORPHIC_CLASS_HELPER(B,C,N) \
300
static_assert(std::is_base_of_v<B,C>, "must be base and sub class"); \
301
static RegisterLoaderHelper<TextInputArchive,  C> registerHelper1##C(N); \
302
static RegisterSaverHelper <TextOutputArchive, C> registerHelper2##C(N); \
303
static RegisterLoaderHelper<XmlInputArchive,   C> registerHelper3##C(N); \
304
static RegisterSaverHelper <XmlOutputArchive,  C> registerHelper4##C(N); \
305
static RegisterLoaderHelper<MemInputArchive,   C> registerHelper5##C(N); \
306
static RegisterSaverHelper <MemOutputArchive,  C> registerHelper6##C(N); \*/
307
#define REGISTER_POLYMORPHIC_CLASS_HELPER(B,C,N) \
308
static_assert(std::is_base_of_v<B,C>, "must be base and sub class"); \
309
static const RegisterLoaderHelper<MemInputArchive,  C> registerHelper3##C(N); \
310
static const RegisterSaverHelper <MemOutputArchive, C> registerHelper4##C(N); \
311
static const RegisterLoaderHelper<XmlInputArchive,  C> registerHelper5##C(N); \
312
static const RegisterSaverHelper <XmlOutputArchive, C> registerHelper6##C(N); \
313
template<> struct PolymorphicBaseClass<C> { using type = B; };
314
315
#define REGISTER_POLYMORPHIC_INITIALIZER_HELPER(B,C,N) \
316
static_assert(std::is_base_of_v<B,C>, "must be base and sub class"); \
317
static const RegisterInitializerHelper<MemInputArchive,  C> registerHelper3##C(N); \
318
static const RegisterSaverHelper      <MemOutputArchive, C> registerHelper4##C(N); \
319
static const RegisterInitializerHelper<XmlInputArchive,  C> registerHelper5##C(N); \
320
static const RegisterSaverHelper      <XmlOutputArchive, C> registerHelper6##C(N); \
321
template<> struct PolymorphicBaseClass<C> { using type = B; };
322
323
#define REGISTER_BASE_NAME_HELPER(B,N) \
324
template<> struct BaseClassName<B> \
325
{ static const char* getName() { static constexpr const char* const name = N; return name; } };
326
327
// public macros
328
//   these are a more convenient way to define specializations of the
329
//   PolymorphicConstructorArgs and PolymorphicBaseClass classes
330
#define REGISTER_POLYMORPHIC_CLASS(BASE,CLASS,NAME) \
331
	REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
332
	REGISTER_CONSTRUCTOR_ARGS_0(CLASS)
333
334
#define REGISTER_POLYMORPHIC_CLASS_1(BASE,CLASS,NAME,TYPE1) \
335
	REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
336
	REGISTER_CONSTRUCTOR_ARGS_1(CLASS,TYPE1)
337
338
#define REGISTER_POLYMORPHIC_CLASS_2(BASE,CLASS,NAME,TYPE1,TYPE2) \
339
	REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
340
	REGISTER_CONSTRUCTOR_ARGS_2(CLASS,TYPE1,TYPE2)
341
342
#define REGISTER_POLYMORPHIC_CLASS_3(BASE,CLASS,NAME,TYPE1,TYPE2,TYPE3) \
343
	REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
344
	REGISTER_CONSTRUCTOR_ARGS_3(CLASS,TYPE1,TYPE2,TYPE3)
345
346
#define REGISTER_BASE_CLASS(CLASS,NAME) \
347
	REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
348
	REGISTER_CONSTRUCTOR_ARGS_0(CLASS)
349
350
#define REGISTER_BASE_CLASS_1(CLASS,NAME,TYPE1) \
351
	REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
352
	REGISTER_CONSTRUCTOR_ARGS_1(CLASS,TYPE1)
353
354
#define REGISTER_BASE_CLASS_2(CLASS,NAME,TYPE1,TYPE2) \
355
	REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
356
	REGISTER_CONSTRUCTOR_ARGS_2(CLASS,TYPE1,TYPE2)
357
358
#define REGISTER_BASE_CLASS_3(CLASS,NAME,TYPE1,TYPE2,TYPE3) \
359
	REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
360
	REGISTER_CONSTRUCTOR_ARGS_3(CLASS,TYPE1,TYPE2,TYPE3)
361
362
363
#define REGISTER_POLYMORPHIC_INITIALIZER(BASE,CLASS,NAME) \
364
	REGISTER_POLYMORPHIC_INITIALIZER_HELPER(BASE,CLASS,NAME)
365
366
//////////////
367
368
/** Store serialization-version number of a class.
369
 *
370
 * Classes are individually versioned. Use the SERIALIZE_CLASS_VERSION
371
 * macro below as a convenient way to set the version of a class.
372
 *
373
 * The initial (=default) version is 1. When the layout of a class changes
374
 * in an incompatible way, you should increase the version number. But
375
 * remember: to be able to load older version the (de)serialize code for the
376
 * older version(s) must be kept.
377
 *
378
 * Version number 0 is special. It means the layout for this class will _NEVER_
379
 * change. This can be a bit more efficient because the version number must
380
 * not be stored in the stream. Though be careful to only use version 0 for
381
 * _VERY_ stable classes, std::pair is a good example of a stable class.
382
 */
383
template<typename T> struct SerializeClassVersion
384
{
385
	static constexpr unsigned value = 1;
386
};
387
#define SERIALIZE_CLASS_VERSION(CLASS, VERSION) \
388
template<> struct SerializeClassVersion<CLASS> \
389
{ \
390
	static constexpr unsigned value = VERSION; \
391
};
392
393
} // namespace openmsx
394
395
#endif