//+----------------------------------------------------------------------------+ //| Description: Magic Set Editor - Program to make Magic (tm) cards | //| Copyright: (C) Twan van Laarhoven and the other MSE developers | //| License: GNU General Public License 2 or later (see file COPYING) | //+----------------------------------------------------------------------------+ #pragma once /** @file util/reflect.hpp * * @brief Reflection of classes, currently reflection is used for (de)serialization. */ // ----------------------------------------------------------------------------- : Includes #include #include #include // ----------------------------------------------------------------------------- : Declaring reflection /// Declare that a class supports reflection /** Reflection allows the member variables of a class to be inspected at runtime. */ #define DECLARE_REFLECTION_PREFIX(PREFIX,SUFFIX) \ private: \ template void reflect_impl(Handler&); \ protected: \ friend class Reader; \ friend class Writer; \ friend class GetDefaultMember; \ friend class GetMember; \ PREFIX void reflect(Reader& reader) SUFFIX; \ PREFIX void reflect(Writer& writer) SUFFIX; \ PREFIX void reflect(GetDefaultMember& gdm) SUFFIX; \ PREFIX void reflect(GetMember& gm) SUFFIX #define DECLARE_REFLECTION() DECLARE_REFLECTION_PREFIX(,) /// Declare that a class supports reflection, which can be overridden in derived classes #define DECLARE_REFLECTION_VIRTUAL() DECLARE_REFLECTION_PREFIX(virtual,) #define DECLARE_REFLECTION_OVERRIDE() DECLARE_REFLECTION_PREFIX(,override) // ----------------------------------------------------------------------------- : Implementing reflection /// Implement the refelection of a class type Cls /** Reflection allows the member variables of a class to be inspected at runtime. * * Currently creates the methods: * - Reader ::handle(Cls&) * - Writer ::handle(const Cls&) * - GetMember ::handle(const Cls&) * Usage: * @code * IMPLEMENT_REFLECTION(MyClass) { * REFLECT(a_variable_in_my_class); * REFLECT(another_variable_in_my_class); * } * @endcode */ #define IMPLEMENT_REFLECTION(Cls) \ REFLECT_OBJECT_READER(Cls) \ REFLECT_OBJECT_WRITER(Cls) \ REFLECT_OBJECT_GET_DEFAULT_MEMBER_NOT(Cls) \ REFLECT_OBJECT_GET_MEMBER(Cls) \ template \ void Cls::reflect_impl(Handler& handler) /// Implement the refelection of a class type Cls that only uses REFLECT_NAMELESS #define IMPLEMENT_REFLECTION_NAMELESS(Cls) \ REFLECT_OBJECT_READER(Cls) \ REFLECT_OBJECT_WRITER(Cls) \ REFLECT_OBJECT_GET_DEFAULT_MEMBER(Cls) \ REFLECT_OBJECT_GET_MEMBER_NOT(Cls) \ template \ void Cls::reflect_impl(Handler& handler) /// Implement the refelection of a class type Cls, but only for Reader and Writer, /** There is custom code for GetMember and GetDefaultMember */ #define IMPLEMENT_REFLECTION_NO_GET_MEMBER(Cls) \ REFLECT_OBJECT_READER(Cls) \ REFLECT_OBJECT_WRITER(Cls) \ template \ void Cls::reflect_impl(Handler& handler) /// Implement the refelection of a class type Cls, but only for Reader and Writer /** There is no code for GetMember and GetDefaultMember */ #define IMPLEMENT_REFLECTION_NO_SCRIPT(Cls) \ REFLECT_OBJECT_READER(Cls) \ REFLECT_OBJECT_WRITER(Cls) \ REFLECT_OBJECT_GET_DEFAULT_MEMBER_NOT(Cls) \ REFLECT_OBJECT_GET_MEMBER_NOT(Cls) \ template \ void Cls::reflect_impl(Handler& handler) /// Reflect a variable #define REFLECT(var) handler.handle(_(#var), var) /// Reflect a variable under the given name #define REFLECT_N(name, var) handler.handle(_(name), var) /// Reflect a variable without a name, should be used only once per class #define REFLECT_NAMELESS(var) handler.handle(var) /// Declare that the variables of a base class should also be reflected #define REFLECT_BASE(Base) Base::reflect(handler) /// Reflect a group of declarations only when reading /** Usage: * @code * REFLECT_IF_READING { * // only executed by Reader * } * @endcode */ #define REFLECT_IF_READING if (Handler::isReading) /// Reflect a group of declarations only when *not* reading /** Usage: * @code * REFLECT_IF_NOT_READING { * // only executed by Writer, GetMember, GetDefaultMember * } * @endcode */ #define REFLECT_IF_NOT_READING if (!Handler::isReading) /// Reflect a group of declarations only when reading and when the value is a single line value #define REFLECT_IF_READING_SINGLE_VALUE if (Handler::isReading && !handler.isCompound()) #define REFLECT_IF_READING_SINGLE_VALUE_AND(cond) if (Handler::isReading && !handler.isCompound() && cond) /// Add an alias for backwards compatibility /** If a key 'name' is encountered in the input file, it is interpreted as 'var' for versions < version * Example: * @code * REFLECT_COMPAT(<300, "style", stylesheet) // prior to 0.3.0 style was used instead of stylesheet * @encode */ #define REFLECT_COMPAT(cond, name, var) if (handler.formatVersion() cond) REFLECT_N(name,var) /// Ignore things for backwards compatibility for versions < 'version' #define REFLECT_COMPAT_IGNORE(cond, name, Type) if (reflector.formatVersion() cond) {Type ignored; REFLECT_N(name,ignored);} /// Reflect a variable, ignores the variable for scripting #define REFLECT_NO_SCRIPT(var) handler.handleNoScript(_(#var), var) /// Reflect a variable under the given name #define REFLECT_NO_SCRIPT_N(name, var) handler.handleNoScript(_(name), var) /// Explicitly instantiate reflection; this is occasionally required. #define INSTANTIATE_REFLECTION(Class) \ template void Class::reflect_impl (Reader&); \ template void Class::reflect_impl (Writer&); \ template void Class::reflect_impl (GetMember&); #define INSTANTIATE_REFLECTION_NAMELESS(Class) \ template void Class::reflect_impl (Reader&); \ template void Class::reflect_impl (Writer&); \ template void Class::reflect_impl (GetDefaultMember&); // ----------------------------------------------------------------------------- : Reflecting enums template struct ReflectEnum {}; /// Implement the refelection of a enumeration type Enum /** Usage: * @code * IMPLEMENT_REFLECTION_ENUM(MyEnum) { * VALUE(value_of_enum_1); * VALUE(value_of_enum_2); * } * @endcode * * When reading the first value declared is the default value * * Currently creates the methods: * - Reader::handle(Enum&) * - Writer::handle(const Enum&) * - GetDefaultMember::handle(const Enum&) */ #define IMPLEMENT_REFLECTION_ENUM(Enum) \ template <> struct ReflectEnum { \ template static void reflect(Enum&, Handler&); \ }; \ REFLECT_ENUM_READER(Enum) \ REFLECT_ENUM_WRITER(Enum) \ REFLECT_ENUM_GET_MEMBER(Enum) \ template \ void ReflectEnum::reflect(Enum& enum_, Handler& handler) /// Declare a possible value of an enum #define VALUE(val) handler.handle(_(#val), val, enum_) /// Declare a possible value of an enum under the given name #define VALUE_N(name, val) handler.handle(_(name), val, enum_)