diff options
author | Ava Chow <github@achow101.com> | 2024-05-15 15:09:56 -0400 |
---|---|---|
committer | Ava Chow <github@achow101.com> | 2024-05-15 15:09:56 -0400 |
commit | 71f0f2273f6258e466c7b299c11982b4a04ae0d7 (patch) | |
tree | f658a4f7da5d2ddc82526ea79dab701340bc97eb /src/serialize.h | |
parent | 7a40f2a3f1cf744d136ecf534979114e79c5e71d (diff) | |
parent | 8d491ae9ecf1948ea29f67b50ca7259123f602aa (diff) |
Merge bitcoin/bitcoin#28929: serialization: Support for multiple parameters
8d491ae9ecf1948ea29f67b50ca7259123f602aa serialization: Add ParamsStream GetStream() method (Ryan Ofsky)
951203bcc496c4415b7754cd764544659b76067f net: Simplify ParamsStream usage (Ryan Ofsky)
e6794e475c84d9edca4a2876e2342cbb1d85f804 serialization: Accept multiple parameters in ParamsStream constructor (Ryan Ofsky)
cb28849a88339c1e7ba03ffc7e38998339074e6e serialization: Reverse ParamsStream constructor order (Ryan Ofsky)
83436d14f06551026bcf5529df3b63b4e8a679fb serialization: Drop unnecessary ParamsStream references (Ryan Ofsky)
84502b755bcc35413ad466047893b5edf134c53f serialization: Drop references to GetVersion/GetType (Ryan Ofsky)
f3a2b5237688e9f574444e793724664b00fb7f2a serialization: Support for multiple parameters (Ryan Ofsky)
Pull request description:
Currently it is only possible to attach one serialization parameter to a stream at a time. For example, it is not possible to set a parameter controlling the transaction format and a parameter controlling the address format at the same time because one parameter will override the other.
This limitation is inconvenient for multiprocess code since it is not possible to create just one type of stream and serialize any object to it. Instead it is necessary to create different streams for different object types, which requires extra boilerplate and makes using the new parameter fields a lot more awkward than the older version and type fields.
Fix this problem by allowing an unlimited number of serialization stream parameters to be set, and allowing them to be requested by type. Later parameters will still override earlier parameters, but only if they have the same type.
For an example of different ways multiple parameters can be set, see the new [`with_params_multi`](https://github.com/bitcoin/bitcoin/blob/40f505583f4edeb2859aeb70da20c6374d331a4f/src/test/serialize_tests.cpp#L394-L410) unit test.
This change requires replacing the `stream.GetParams()` method with a `stream.GetParams<T>()` method in order for serialization code to retrieve the desired parameters. The change is more verbose, but probably a good thing for readability because previously it could be difficult to know what type the `GetParams()` method would return, and now it is more obvious.
---
This PR is part of the [process separation project](https://github.com/bitcoin/bitcoin/issues/28722).
ACKs for top commit:
maflcko:
ACK 8d491ae9ecf1948ea29f67b50ca7259123f602aa 🔵
sipa:
utACK 8d491ae9ecf1948ea29f67b50ca7259123f602aa
TheCharlatan:
ACK 8d491ae9ecf1948ea29f67b50ca7259123f602aa
Tree-SHA512: 40b7041ee01c0372b1f86f7fd6f3b6af56ef24a6383f91ffcedd04d388e63407006457bf7ed056b0e37b4dec9ffd5ca006cb8192e488ea2c64678567e38d4647
Diffstat (limited to 'src/serialize.h')
-rw-r--r-- | src/serialize.h | 113 |
1 files changed, 78 insertions, 35 deletions
diff --git a/src/serialize.h b/src/serialize.h index 2f13fba582..35519056a5 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -182,9 +182,8 @@ const Out& AsBase(const In& x) static void SerializationOps(Type& obj, Stream& s, Operation ser_action) /** - * Variant of FORMATTER_METHODS that supports a declared parameter type. - * - * If a formatter has a declared parameter type, it must be invoked directly or + * Formatter methods can retrieve parameters attached to a stream using the + * SER_PARAMS(type) macro as long as the stream is created directly or * indirectly with a parameter of that type. This permits making serialization * depend on run-time context in a type-safe way. * @@ -192,7 +191,8 @@ const Out& AsBase(const In& x) * struct BarParameter { bool fancy; ... }; * struct Bar { ... }; * struct FooFormatter { - * FORMATTER_METHODS(Bar, obj, BarParameter, param) { + * FORMATTER_METHODS(Bar, obj) { + * auto& param = SER_PARAMS(BarParameter); * if (param.fancy) { * READWRITE(VARINT(obj.value)); * } else { @@ -214,13 +214,7 @@ const Out& AsBase(const In& x) * Compilation will fail in any context where serialization is invoked but * no parameter of a type convertible to BarParameter is provided. */ -#define FORMATTER_METHODS_PARAMS(cls, obj, paramcls, paramobj) \ - template <typename Stream> \ - static void Ser(Stream& s, const cls& obj) { SerializationOps(obj, s, ActionSerialize{}, s.GetParams()); } \ - template <typename Stream> \ - static void Unser(Stream& s, cls& obj) { SerializationOps(obj, s, ActionUnserialize{}, s.GetParams()); } \ - template <typename Stream, typename Type, typename Operation> \ - static void SerializationOps(Type& obj, Stream& s, Operation ser_action, const paramcls& paramobj) +#define SER_PARAMS(type) (s.template GetParams<type>()) #define BASE_SERIALIZE_METHODS(cls) \ template <typename Stream> \ @@ -247,15 +241,6 @@ const Out& AsBase(const In& x) BASE_SERIALIZE_METHODS(cls) \ FORMATTER_METHODS(cls, obj) -/** - * Variant of SERIALIZE_METHODS that supports a declared parameter type. - * - * See FORMATTER_METHODS_PARAMS for more information on parameters. - */ -#define SERIALIZE_METHODS_PARAMS(cls, obj, paramcls, paramobj) \ - BASE_SERIALIZE_METHODS(cls) \ - FORMATTER_METHODS_PARAMS(cls, obj, paramcls, paramobj) - // Templates for serializing to anything that looks like a stream, // i.e. anything that supports .read(Span<std::byte>) and .write(Span<const std::byte>) // @@ -1118,27 +1103,85 @@ size_t GetSerializeSize(const T& t) return (SizeComputer() << t).size(); } -/** Wrapper that overrides the GetParams() function of a stream (and hides GetVersion/GetType). */ -template <typename Params, typename SubStream> +//! Check if type contains a stream by seeing if has a GetStream() method. +template<typename T> +concept ContainsStream = requires(T t) { t.GetStream(); }; + +/** Wrapper that overrides the GetParams() function of a stream. */ +template <typename SubStream, typename Params> class ParamsStream { const Params& m_params; - SubStream& m_substream; // private to avoid leaking version/type into serialization code that shouldn't see it + // If ParamsStream constructor is passed an lvalue argument, Substream will + // be a reference type, and m_substream will reference that argument. + // Otherwise m_substream will be a substream instance and move from the + // argument. Letting ParamsStream contain a substream instance instead of + // just a reference is useful to make the ParamsStream object self contained + // and let it do cleanup when destroyed, for example by closing files if + // SubStream is a file stream. + SubStream m_substream; public: - ParamsStream(const Params& params LIFETIMEBOUND, SubStream& substream LIFETIMEBOUND) : m_params{params}, m_substream{substream} {} + ParamsStream(SubStream&& substream, const Params& params LIFETIMEBOUND) : m_params{params}, m_substream{std::forward<SubStream>(substream)} {} + + template <typename NestedSubstream, typename Params1, typename Params2, typename... NestedParams> + ParamsStream(NestedSubstream&& s, const Params1& params1 LIFETIMEBOUND, const Params2& params2 LIFETIMEBOUND, const NestedParams&... params LIFETIMEBOUND) + : ParamsStream{::ParamsStream{std::forward<NestedSubstream>(s), params2, params...}, params1} {} + template <typename U> ParamsStream& operator<<(const U& obj) { ::Serialize(*this, obj); return *this; } template <typename U> ParamsStream& operator>>(U&& obj) { ::Unserialize(*this, obj); return *this; } - void write(Span<const std::byte> src) { m_substream.write(src); } - void read(Span<std::byte> dst) { m_substream.read(dst); } - void ignore(size_t num) { m_substream.ignore(num); } - bool eof() const { return m_substream.eof(); } - size_t size() const { return m_substream.size(); } - const Params& GetParams() const { return m_params; } - int GetVersion() = delete; // Deprecated with Params usage - int GetType() = delete; // Deprecated with Params usage + void write(Span<const std::byte> src) { GetStream().write(src); } + void read(Span<std::byte> dst) { GetStream().read(dst); } + void ignore(size_t num) { GetStream().ignore(num); } + bool eof() const { return GetStream().eof(); } + size_t size() const { return GetStream().size(); } + + //! Get reference to stream parameters. + template <typename P> + const auto& GetParams() const + { + if constexpr (std::is_convertible_v<Params, P>) { + return m_params; + } else { + return m_substream.template GetParams<P>(); + } + } + + //! Get reference to underlying stream. + auto& GetStream() + { + if constexpr (ContainsStream<SubStream>) { + return m_substream.GetStream(); + } else { + return m_substream; + } + } + const auto& GetStream() const + { + if constexpr (ContainsStream<SubStream>) { + return m_substream.GetStream(); + } else { + return m_substream; + } + } }; +/** + * Explicit template deduction guide is required for single-parameter + * constructor so Substream&& is treated as a forwarding reference, and + * SubStream is deduced as reference type for lvalue arguments. + */ +template <typename Substream, typename Params> +ParamsStream(Substream&&, const Params&) -> ParamsStream<Substream, Params>; + +/** + * Template deduction guide for multiple params arguments that creates a nested + * ParamsStream. + */ +template <typename Substream, typename Params1, typename Params2, typename... Params> +ParamsStream(Substream&& s, const Params1& params1, const Params2& params2, const Params&... params) -> + ParamsStream<decltype(ParamsStream{std::forward<Substream>(s), params2, params...}), Params1>; + /** Wrapper that serializes objects with the specified parameters. */ template <typename Params, typename T> class ParamsWrapper @@ -1152,13 +1195,13 @@ public: template <typename Stream> void Serialize(Stream& s) const { - ParamsStream ss{m_params, s}; + ParamsStream ss{s, m_params}; ::Serialize(ss, m_object); } template <typename Stream> void Unserialize(Stream& s) { - ParamsStream ss{m_params, s}; + ParamsStream ss{s, m_params}; ::Unserialize(ss, m_object); } }; @@ -1176,7 +1219,7 @@ public: /** \ * Return a wrapper around t that (de)serializes it with specified parameter params. \ * \ - * See FORMATTER_METHODS_PARAMS for more information on serialization parameters. \ + * See SER_PARAMS for more information on serialization parameters. \ */ \ template <typename T> \ auto operator()(T&& t) const \ |