123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- #ifndef SERIALIZE_META_HH
- #define SERIALIZE_META_HH
- #include "hash_map.hh"
- #include "likely.hh"
- #include "xxhash.hh"
- #include <memory>
- #include <tuple>
- #include <typeindex>
- #include <type_traits>
- #include <utility>
- #include <vector>
- namespace openmsx {
- /** Utility to do T* t = new T(...)
- *
- * The tricky part is that the constructor of T can have a variable number
- * of parameters.
- *
- * For example:
- * Creator<Foo> creator;
- * auto args = std::tuple(42, 3.14);
- * std::unique_ptr<Foo> foo = creator(args);
- * This is equivalent to
- * auto foo = std::make_unique<Foo>(42, 3.14);
- * But the former can be used in a generic context (where the number of
- * constructor parameters is unknown).
- */
- template<typename T> struct Creator
- {
- template<typename TUPLE>
- std::unique_ptr<T> operator()(TUPLE tuple) {
- auto makeT = [](auto&& ...args) {
- return std::make_unique<T>(std::forward<decltype(args)>(args)...);
- };
- return std::apply(makeT, tuple);
- }
- };
- ///////////////////////////////
- // Polymorphic class loader/saver
- // forward declarations
- // ClassSaver: used to actually save a class. We also store the name of
- // the class so that the loader knows which concrete class it should load.
- template<typename T> struct ClassSaver;
- // NonPolymorphicPointerLoader: once we know which concrete type to load,
- // we use the 'normal' class loader to load it.
- template<typename T> struct NonPolymorphicPointerLoader;
- // Used by PolymorphicInitializer to initialize a concrete type.
- template<typename T> struct ClassLoader;
- /** Store association between polymorphic class (base- or subclass) and
- * the list of constructor arguments.
- * Specializations of this class should store the constructor arguments
- * as a 'using type = tupple<...>'.
- */
- template<typename T> struct PolymorphicConstructorArgs;
- /** Store association between (polymorphic) sub- and baseclass.
- * Specialization of this class should provide a 'using type = <base>'.
- */
- template<typename T> struct PolymorphicBaseClass;
- template<typename Base> struct MapConstrArgsEmpty
- {
- using TUPLEIn = typename PolymorphicConstructorArgs<Base>::type;
- std::tuple<> operator()(const TUPLEIn& /*t*/)
- {
- return std::tuple<>();
- }
- };
- template<typename Base, typename Derived> struct MapConstrArgsCopy
- {
- using TUPLEIn = typename PolymorphicConstructorArgs<Base>::type;
- using TUPLEOut = typename PolymorphicConstructorArgs<Derived>::type;
- static_assert(std::is_same_v<TUPLEIn, TUPLEOut>,
- "constructor argument types must match");
- TUPLEOut operator()(const TUPLEIn& t)
- {
- return t;
- }
- };
- /** Define mapping between constructor arg list of base- and subclass.
- *
- * When loading a polymorphic base class, the user must provide the union of
- * constructor arguments for all subclasses (because it's not yet known which
- * concrete subtype will be deserialized). This class defines the mapping
- * between this union of parameters and the subset used for a specific
- * subclass.
- *
- * In case the parameter list of the subclass is empty or if it is the same
- * as the base class, this mapping will be defined automatically. In the other
- * cases, the user must define a specialization of this class.
- */
- template<typename Base, typename Derived> struct MapConstructorArguments
- : std::conditional_t<std::is_same<std::tuple<>,
- typename PolymorphicConstructorArgs<Derived>::type>::value,
- MapConstrArgsEmpty<Base>,
- MapConstrArgsCopy<Base, Derived>> {};
- /** Stores the name of a base class.
- * This name is used as tag-name in XML archives.
- *
- * Specializations of this class should provide a function
- * static const char* getName()
- */
- template<typename Base> struct BaseClassName;
- template<typename Archive> class PolymorphicSaverBase
- {
- public:
- virtual ~PolymorphicSaverBase() = default;
- virtual void save(Archive& ar, const void* p) const = 0;
- };
- template<typename Archive> class PolymorphicLoaderBase
- {
- public:
- virtual ~PolymorphicLoaderBase() = default;
- virtual void* load(Archive& ar, unsigned id, const void* args) const = 0;
- };
- template<typename Archive> class PolymorphicInitializerBase
- {
- public:
- virtual ~PolymorphicInitializerBase() = default;
- virtual void init(Archive& ar, void* t, unsigned id) const = 0;
- };
- template<typename Archive, typename T>
- class PolymorphicSaver : public PolymorphicSaverBase<Archive>
- {
- public:
- explicit PolymorphicSaver(const char* name_)
- : name(name_)
- {
- }
- void save(Archive& ar, const void* v) const override
- {
- using BaseType = typename PolymorphicBaseClass<T>::type;
- auto base = static_cast<const BaseType*>(v);
- auto tp = static_cast<const T*>(base);
- ClassSaver<T> saver;
- saver(ar, *tp, true, name, true); // save id, type, constr-args
- }
- private:
- const char* name;
- };
- template<typename Archive, typename T>
- class PolymorphicLoader : public PolymorphicLoaderBase<Archive>
- {
- public:
- void* load(Archive& ar, unsigned id, const void* args) const override
- {
- using BaseType = typename PolymorphicBaseClass<T>::type;
- using TUPLEIn = typename PolymorphicConstructorArgs<BaseType>::type;
- using TUPLEOut = typename PolymorphicConstructorArgs<T>::type;
- auto& argsIn = *static_cast<const TUPLEIn*>(args);
- MapConstructorArguments<BaseType, T> mapArgs;
- TUPLEOut argsOut = mapArgs(argsIn);
- NonPolymorphicPointerLoader<T> loader;
- return loader(ar, id, argsOut);
- }
- };
- void polyInitError(const char* expected, const char* actual);
- template<typename Archive, typename T>
- class PolymorphicInitializer : public PolymorphicInitializerBase<Archive>
- {
- public:
- void init(Archive& ar, void* v, unsigned id) const override
- {
- using BaseType = typename PolymorphicBaseClass<T>::type;
- auto base = static_cast<BaseType*>(v);
- if (unlikely(dynamic_cast<T*>(base) != static_cast<T*>(base))) {
- polyInitError(typeid(T).name(), typeid(*base).name());
- }
- auto t = static_cast<T*>(base);
- ClassLoader<T> loader;
- loader(ar, *t, std::tuple<>(), id);
- }
- };
- template<typename Archive>
- class PolymorphicSaverRegistry
- {
- public:
- static PolymorphicSaverRegistry& instance();
- template<typename T> void registerClass(const char* name)
- {
- static_assert(std::is_polymorphic_v<T>,
- "must be a polymorphic type");
- static_assert(!std::is_abstract_v<T>,
- "can't be an abstract type");
- registerHelper(typeid(T),
- std::make_unique<PolymorphicSaver<Archive, T>>(name));
- }
- template<typename T> static void save(Archive& ar, T* t)
- {
- save(ar, t, typeid(*t));
- }
- template<typename T> static void save(const char* tag, Archive& ar, T& t)
- {
- save(tag, ar, &t, typeid(t));
- }
- private:
- PolymorphicSaverRegistry() = default;
- ~PolymorphicSaverRegistry() = default;
- void registerHelper(const std::type_info& type,
- std::unique_ptr<PolymorphicSaverBase<Archive>> saver);
- static void save(Archive& ar, const void* t,
- const std::type_info& typeInfo);
- static void save(const char* tag, Archive& ar, const void* t,
- const std::type_info& typeInfo);
- std::vector<std::pair<std::type_index,
- std::unique_ptr<PolymorphicSaverBase<Archive>>>> saverMap;
- bool initialized = false;
- };
- template<typename Archive>
- class PolymorphicLoaderRegistry
- {
- public:
- static PolymorphicLoaderRegistry& instance();
- template<typename T> void registerClass(const char* name)
- {
- static_assert(std::is_polymorphic_v<T>,
- "must be a polymorphic type");
- static_assert(!std::is_abstract_v<T>,
- "can't be an abstract type");
- registerHelper(name,
- std::make_unique<PolymorphicLoader<Archive, T>>());
- }
- static void* load(Archive& ar, unsigned id, const void* args);
- private:
- PolymorphicLoaderRegistry() = default;
- ~PolymorphicLoaderRegistry() = default;
- void registerHelper(
- const char* name,
- std::unique_ptr<PolymorphicLoaderBase<Archive>> loader);
- hash_map<std::string_view, std::unique_ptr<PolymorphicLoaderBase<Archive>>, XXHasher>
- loaderMap;
- };
- template<typename Archive>
- class PolymorphicInitializerRegistry
- {
- public:
- static PolymorphicInitializerRegistry& instance();
- template<typename T> void registerClass(const char* name)
- {
- static_assert(std::is_polymorphic_v<T>,
- "must be a polymorphic type");
- static_assert(!std::is_abstract_v<T>,
- "can't be an abstract type");
- registerHelper(name,
- std::make_unique<PolymorphicInitializer<Archive, T>>());
- }
- static void init(const char* tag, Archive& ar, void* t);
- private:
- PolymorphicInitializerRegistry() = default;
- ~PolymorphicInitializerRegistry() = default;
- void registerHelper(
- const char* name,
- std::unique_ptr<PolymorphicInitializerBase<Archive>> initializer);
- hash_map<std::string_view, std::unique_ptr<PolymorphicInitializerBase<Archive>>, XXHasher>
- initializerMap;
- };
- template<typename Archive, typename T> struct RegisterSaverHelper
- {
- explicit RegisterSaverHelper(const char* name)
- {
- PolymorphicSaverRegistry<Archive>::instance().
- template registerClass<T>(name);
- }
- };
- template<typename Archive, typename T> struct RegisterLoaderHelper
- {
- explicit RegisterLoaderHelper(const char* name)
- {
- PolymorphicLoaderRegistry<Archive>::instance().
- template registerClass<T>(name);
- }
- };
- template<typename Archive, typename T> struct RegisterInitializerHelper
- {
- explicit RegisterInitializerHelper(const char* name)
- {
- PolymorphicInitializerRegistry<Archive>::instance().
- template registerClass<T>(name);
- }
- };
- #define REGISTER_CONSTRUCTOR_ARGS_0(C) \
- template<> struct PolymorphicConstructorArgs<C> \
- { using type = std::tuple<>; };
- #define REGISTER_CONSTRUCTOR_ARGS_1(C,T1) \
- template<> struct PolymorphicConstructorArgs<C> \
- { using type = std::tuple<T1>; };
- #define REGISTER_CONSTRUCTOR_ARGS_2(C,T1,T2) \
- template<> struct PolymorphicConstructorArgs<C> \
- { using type = std::tuple<T1,T2>; };
- #define REGISTER_CONSTRUCTOR_ARGS_3(C,T1,T2,T3) \
- template<> struct PolymorphicConstructorArgs<C> \
- { using type = std::tuple<T1,T2,T3>; };
- class MemInputArchive;
- class MemOutputArchive;
- class XmlInputArchive;
- class XmlOutputArchive;
- /*#define REGISTER_POLYMORPHIC_CLASS_HELPER(B,C,N) \
- static_assert(std::is_base_of_v<B,C>, "must be base and sub class"); \
- static RegisterLoaderHelper<TextInputArchive, C> registerHelper1##C(N); \
- static RegisterSaverHelper <TextOutputArchive, C> registerHelper2##C(N); \
- static RegisterLoaderHelper<XmlInputArchive, C> registerHelper3##C(N); \
- static RegisterSaverHelper <XmlOutputArchive, C> registerHelper4##C(N); \
- static RegisterLoaderHelper<MemInputArchive, C> registerHelper5##C(N); \
- static RegisterSaverHelper <MemOutputArchive, C> registerHelper6##C(N); \*/
- #define REGISTER_POLYMORPHIC_CLASS_HELPER(B,C,N) \
- static_assert(std::is_base_of_v<B,C>, "must be base and sub class"); \
- static RegisterLoaderHelper<MemInputArchive, C> registerHelper3##C(N); \
- static RegisterSaverHelper <MemOutputArchive, C> registerHelper4##C(N); \
- static RegisterLoaderHelper<XmlInputArchive, C> registerHelper5##C(N); \
- static RegisterSaverHelper <XmlOutputArchive, C> registerHelper6##C(N); \
- template<> struct PolymorphicBaseClass<C> { using type = B; };
- #define REGISTER_POLYMORPHIC_INITIALIZER_HELPER(B,C,N) \
- static_assert(std::is_base_of_v<B,C>, "must be base and sub class"); \
- static RegisterInitializerHelper<MemInputArchive, C> registerHelper3##C(N); \
- static RegisterSaverHelper <MemOutputArchive, C> registerHelper4##C(N); \
- static RegisterInitializerHelper<XmlInputArchive, C> registerHelper5##C(N); \
- static RegisterSaverHelper <XmlOutputArchive, C> registerHelper6##C(N); \
- template<> struct PolymorphicBaseClass<C> { using type = B; };
- #define REGISTER_BASE_NAME_HELPER(B,N) \
- template<> struct BaseClassName<B> \
- { static const char* getName() { static constexpr const char* const name = N; return name; } };
- // public macros
- // these are a more convenient way to define specializations of the
- // PolymorphicConstructorArgs and PolymorphicBaseClass classes
- #define REGISTER_POLYMORPHIC_CLASS(BASE,CLASS,NAME) \
- REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
- REGISTER_CONSTRUCTOR_ARGS_0(CLASS)
- #define REGISTER_POLYMORPHIC_CLASS_1(BASE,CLASS,NAME,TYPE1) \
- REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
- REGISTER_CONSTRUCTOR_ARGS_1(CLASS,TYPE1)
- #define REGISTER_POLYMORPHIC_CLASS_2(BASE,CLASS,NAME,TYPE1,TYPE2) \
- REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
- REGISTER_CONSTRUCTOR_ARGS_2(CLASS,TYPE1,TYPE2)
- #define REGISTER_POLYMORPHIC_CLASS_3(BASE,CLASS,NAME,TYPE1,TYPE2,TYPE3) \
- REGISTER_POLYMORPHIC_CLASS_HELPER(BASE,CLASS,NAME) \
- REGISTER_CONSTRUCTOR_ARGS_3(CLASS,TYPE1,TYPE2,TYPE3)
- #define REGISTER_BASE_CLASS(CLASS,NAME) \
- REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
- REGISTER_CONSTRUCTOR_ARGS_0(CLASS)
- #define REGISTER_BASE_CLASS_1(CLASS,NAME,TYPE1) \
- REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
- REGISTER_CONSTRUCTOR_ARGS_1(CLASS,TYPE1)
- #define REGISTER_BASE_CLASS_2(CLASS,NAME,TYPE1,TYPE2) \
- REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
- REGISTER_CONSTRUCTOR_ARGS_2(CLASS,TYPE1,TYPE2)
- #define REGISTER_BASE_CLASS_3(CLASS,NAME,TYPE1,TYPE2,TYPE3) \
- REGISTER_BASE_NAME_HELPER(CLASS,NAME) \
- REGISTER_CONSTRUCTOR_ARGS_3(CLASS,TYPE1,TYPE2,TYPE3)
- #define REGISTER_POLYMORPHIC_INITIALIZER(BASE,CLASS,NAME) \
- REGISTER_POLYMORPHIC_INITIALIZER_HELPER(BASE,CLASS,NAME)
- //////////////
- /** Store serialization-version number of a class.
- *
- * Classes are individually versioned. Use the SERIALIZE_CLASS_VERSION
- * macro below as a convenient way to set the version of a class.
- *
- * The initial (=default) version is 1. When the layout of a class changes
- * in an incompatible way, you should increase the version number. But
- * remember: to be able to load older version the (de)serialize code for the
- * older version(s) must be kept.
- *
- * Version number 0 is special. It means the layout for this class will _NEVER_
- * change. This can be a bit more efficient because the version number must
- * not be stored in the stream. Though be careful to only use version 0 for
- * _VERY_ stable classes, std::pair is a good example of a stable class.
- */
- template<typename T> struct SerializeClassVersion
- {
- static constexpr unsigned value = 1;
- };
- #define SERIALIZE_CLASS_VERSION(CLASS, VERSION) \
- template<> struct SerializeClassVersion<CLASS> \
- { \
- static constexpr unsigned value = VERSION; \
- };
- } // namespace openmsx
- #endif
|