diff options
Diffstat (limited to 'src/serialize.h')
-rw-r--r-- | src/serialize.h | 99 |
1 files changed, 82 insertions, 17 deletions
diff --git a/src/serialize.h b/src/serialize.h index c84a1567f1..7fa669ebdb 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -199,6 +199,52 @@ template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; } SerializationOp(s, CSerActionUnserialize()); \ } +/** + * Implement the Ser and Unser methods needed for implementing a formatter (see Using below). + * + * Both Ser and Unser are delegated to a single static method SerializationOps, which is polymorphic + * in the serialized/deserialized type (allowing it to be const when serializing, and non-const when + * deserializing). + * + * Example use: + * struct FooFormatter { + * FORMATTER_METHODS(Class, obj) { READWRITE(obj.val1, VARINT(obj.val2)); } + * } + * would define a class FooFormatter that defines a serialization of Class objects consisting + * of serializing its val1 member using the default serialization, and its val2 member using + * VARINT serialization. That FooFormatter can then be used in statements like + * READWRITE(Using<FooFormatter>(obj.bla)). + */ +#define FORMATTER_METHODS(cls, obj) \ + template<typename Stream> \ + static void Ser(Stream& s, const cls& obj) { SerializationOps(obj, s, CSerActionSerialize()); } \ + template<typename Stream> \ + static void Unser(Stream& s, cls& obj) { SerializationOps(obj, s, CSerActionUnserialize()); } \ + template<typename Stream, typename Type, typename Operation> \ + static inline void SerializationOps(Type& obj, Stream& s, Operation ser_action) \ + +/** + * Implement the Serialize and Unserialize methods by delegating to a single templated + * static method that takes the to-be-(de)serialized object as a parameter. This approach + * has the advantage that the constness of the object becomes a template parameter, and + * thus allows a single implementation that sees the object as const for serializing + * and non-const for deserializing, without casts. + */ +#define SERIALIZE_METHODS(cls, obj) \ + template<typename Stream> \ + void Serialize(Stream& s) const \ + { \ + static_assert(std::is_same<const cls&, decltype(*this)>::value, "Serialize type mismatch"); \ + Ser(s, *this); \ + } \ + template<typename Stream> \ + void Unserialize(Stream& s) \ + { \ + static_assert(std::is_same<cls&, decltype(*this)>::value, "Unserialize type mismatch"); \ + Unser(s, *this); \ + } \ + FORMATTER_METHODS(cls, obj) + #ifndef CHAR_EQUALS_INT8 template<typename Stream> inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char #endif @@ -418,26 +464,48 @@ I ReadVarInt(Stream& is) } } -#define VARINT(obj, ...) WrapVarInt<__VA_ARGS__>(REF(obj)) -#define COMPACTSIZE(obj) CCompactSize(REF(obj)) -#define LIMITED_STRING(obj,n) LimitedString< n >(REF(obj)) - -template<VarIntMode Mode, typename I> -class CVarInt +/** Simple wrapper class to serialize objects using a formatter; used by Using(). */ +template<typename Formatter, typename T> +class Wrapper { + static_assert(std::is_lvalue_reference<T>::value, "Wrapper needs an lvalue reference type T"); protected: - I &n; + T m_object; public: - explicit CVarInt(I& nIn) : n(nIn) { } + explicit Wrapper(T obj) : m_object(obj) {} + template<typename Stream> void Serialize(Stream &s) const { Formatter().Ser(s, m_object); } + template<typename Stream> void Unserialize(Stream &s) { Formatter().Unser(s, m_object); } +}; - template<typename Stream> - void Serialize(Stream &s) const { - WriteVarInt<Stream,Mode,I>(s, n); +/** Cause serialization/deserialization of an object to be done using a specified formatter class. + * + * To use this, you need a class Formatter that has public functions Ser(stream, const object&) for + * serialization, and Unser(stream, object&) for deserialization. Serialization routines (inside + * READWRITE, or directly with << and >> operators), can then use Using<Formatter>(object). + * + * This works by constructing a Wrapper<Formatter, T>-wrapped version of object, where T is + * const during serialization, and non-const during deserialization, which maintains const + * correctness. + */ +template<typename Formatter, typename T> +static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T&>(t); } + +#define VARINT(obj, ...) Using<VarIntFormatter<__VA_ARGS__>>(obj) +#define COMPACTSIZE(obj) CCompactSize(REF(obj)) +#define LIMITED_STRING(obj,n) LimitedString< n >(REF(obj)) + +/** Serialization wrapper class for integers in VarInt format. */ +template<VarIntMode Mode=VarIntMode::DEFAULT> +struct VarIntFormatter +{ + template<typename Stream, typename I> void Ser(Stream &s, I v) + { + WriteVarInt<Stream,Mode,typename std::remove_cv<I>::type>(s, v); } - template<typename Stream> - void Unserialize(Stream& s) { - n = ReadVarInt<Stream,Mode,I>(s); + template<typename Stream, typename I> void Unser(Stream& s, I& v) + { + v = ReadVarInt<Stream,Mode,typename std::remove_cv<I>::type>(s); } }; @@ -522,9 +590,6 @@ public: } }; -template<VarIntMode Mode=VarIntMode::DEFAULT, typename I> -CVarInt<Mode, I> WrapVarInt(I& n) { return CVarInt<Mode, I>{n}; } - template<typename I> BigEndian<I> WrapBigEndian(I& n) { return BigEndian<I>(n); } |