From f62d1862e0e4b5ccb2d55c0d888be58b7b226a22 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Tue, 20 Oct 2020 19:17:18 -0700 Subject: [PATCH] sim: Refactor how serialization types are handled in the backend. The parseParam and showParam functions partially worked using template specialization, and partially worked using function overloading. The template specialization could be resolved later once other functions were added, but the regular function overloads could not. That meant that it was practically impossible to add new definitions of those two functions local to the types they worked with. Also, because C++ does not allow partial specialization of template functions, it would not be possible to truly use specialization to wire in BitUnion types. To fix these problems, these functions have been turned into structs which wrap static functions. These can be partially specialized as desired, making them compatible with BitUnions. Also, it's not possible to overload structures like it is with functions, so only specialization is considered, not overloading. While making these changes, these functions (now structs) were also reworked so that they share implementation more, and are generally more streamlined. Given the fact that the previous parseParam and showParam functions could not actually be expanded beyond serialize.hh, and were not actually called directly by any code outside of that file, they should have never been considered part of the API. Now that these structs actually *can* be specialized outside of this file, they should be considered part of the interface. Change-Id: Ic8e677b97fda8378ee1da1f3cf6001e02783fde3 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/36280 Reviewed-by: Jason Lowe-Power Reviewed-by: Richard Cooper Reviewed-by: Andreas Sandberg Maintainer: Andreas Sandberg Tested-by: kokoro --- src/sim/serialize.hh | 218 +++++++++++++++++++++---------------------- 1 file changed, 106 insertions(+), 112 deletions(-) diff --git a/src/sim/serialize.hh b/src/sim/serialize.hh index b5fb47ca9..3adcc5b1a 100644 --- a/src/sim/serialize.hh +++ b/src/sim/serialize.hh @@ -320,139 +320,131 @@ class Serializable /** * @ingroup api_serialize + * @{ */ -template -bool -parseParam(const std::string &s, T &value) -{ - // The base implementations use to_number for parsing and '<<' for - // displaying, suitable for integer types. - return to_number(s, value); -} -/** - * @ingroup api_serialize - */ -template -void -showParam(CheckpointOut &os, const T &value) -{ - os << value; -} +// To add support for a new type of field that can be serialized, define +// template specializations of the two classes below, ParseParam and ShowParam, +// as described above each. The way ParseParam is specialized for std::string +// or ShowParam is specialied for bool can be used as examples. -/** - * @ingroup api_serialize +/* + * A structure which should be specialized to contain a static method with the + * signature: + * + * bool parse(const std::string &s, T &value) + * + * which fills in value using the contents of s, and returns if that was + * successful. */ -template -bool -parseParam(const std::string &s, BitUnionType &value) -{ - // Zero initialize storage to avoid leaking an uninitialized value - BitUnionBaseType storage = BitUnionBaseType(); - auto res = to_number(s, storage); - value = storage; - return res; -} +template +struct ParseParam; -/** - * @ingroup api_serialize - */ +// Specialization for anything to_number can accept. template -void -showParam(CheckpointOut &os, const BitUnionType &value) +struct ParseParam()), void())> { - auto storage = static_cast>(value); - - // For a BitUnion8, the storage type is an unsigned char. - // Since we want to serialize a number we need to cast to - // unsigned int - os << ((sizeof(storage) == 1) ? - static_cast(storage) : storage); -} + static bool + parse(const std::string &s, T &value) + { + return to_number(s, value); + } +}; -/** - * @ingroup api_serialize - */ template <> -inline void -showParam(CheckpointOut &os, const char &value) +struct ParseParam { - // Treat 8-bit ints (chars) as ints on output, not as chars - os << (int)value; -} + static bool + parse(const std::string &s, bool &value) + { + return to_bool(s, value); + } +}; -/** - * @ingroup api_serialize - */ template <> -inline void -showParam(CheckpointOut &os, const signed char &value) +struct ParseParam { - os << (int)value; -} + static bool + parse(const std::string &s, std::string &value) + { + // String requires no processing to speak of + value = s; + return true; + } +}; -/** - * @ingroup api_serialize - */ -template <> -inline void -showParam(CheckpointOut &os, const unsigned char &value) +// Specialization for BitUnion types. +template +struct ParseParam> { - os << (unsigned int)value; -} + static bool + parse(const std::string &s, BitUnionType &value) + { + // Zero initialize storage to avoid leaking an uninitialized value + BitUnionBaseType storage = BitUnionBaseType(); + auto res = to_number(s, storage); + value = storage; + return res; + } +}; -/** - * @ingroup api_serialize +/* + * A structure which should be specialized to contain a static method with the + * signature: + * + * void show(std::ostream &os, const T &value) + * + * which outputs value to the stream os. + * + * This default implementation falls back to the << operator which should work + * for many types. */ -template <> -inline bool -parseParam(const std::string &s, float &value) +template +struct ShowParam { - return to_number(s, value); -} + static void show(std::ostream &os, const T &value) { os << value; } +}; -/** - * @ingroup api_serialize - */ -template <> -inline bool -parseParam(const std::string &s, double &value) +// Handle characters specially so that we print their value, not the character +// they encode. +template +struct ShowParam::value || + std::is_same::value || + std::is_same::value>> { - return to_number(s, value); -} + static void + show(std::ostream &os, const T &value) + { + if (std::is_signed::value) + os << (int)value; + else + os << (unsigned int)value; + } +}; -/** - * @ingroup api_serialize - */ template <> -inline bool -parseParam(const std::string &s, bool &value) +struct ShowParam { - return to_bool(s, value); -} + static void + show(std::ostream &os, const bool &value) + { + // Display bools as strings + os << (value ? "true" : "false"); + } +}; -/** - * @ingroup api_serialize - */ -template <> -inline void -showParam(CheckpointOut &os, const bool &value) +template +struct ShowParam> { - // Display bools as strings - os << (value ? "true" : "false"); -} + static void + show(std::ostream &os, const BitUnionType &value) + { + ShowParam>::show( + os, static_cast &>(value)); + } +}; -/** - * @ingroup api_serialize - */ -template <> -inline bool -parseParam(const std::string &s, std::string &value) -{ - // String requires no processing to speak of - value = s; - return true; -} +/** @} */ /** * This function is used for writing parameters to a checkpoint. @@ -466,7 +458,7 @@ void paramOut(CheckpointOut &os, const std::string &name, const T ¶m) { os << name << "="; - showParam(os, param); + ShowParam::show(os, param); os << "\n"; } @@ -476,7 +468,7 @@ paramInImpl(CheckpointIn &cp, const std::string &name, T ¶m) { const std::string §ion(Serializable::currentSection()); std::string str; - return cp.find(section, name, str) && parseParam(str, param); + return cp.find(section, name, str) && ParseParam::parse(str, param); } /** @@ -529,11 +521,12 @@ arrayParamOut(CheckpointOut &os, const std::string &name, { os << name << "="; auto it = start; + using Elem = std::remove_cv_t>; if (it != end) - showParam(os, *it++); + ShowParam::show(os, *it++); while (it != end) { os << " "; - showParam(os, *it++); + ShowParam::show(os, *it++); } os << "\n"; } @@ -593,7 +586,8 @@ arrayParamIn(CheckpointIn &cp, const std::string &name, for (const auto &token: tokens) { T value; - fatal_if(!parseParam(token, value), "Could not parse \"%s\".", str); + fatal_if(!ParseParam::parse(token, value), + "Could not parse \"%s\".", str); *inserter = value; } } -- 2.30.2