Skip to content

Commit 106d803

Browse files
committed
Simplify WithParams
1 parent 483ed45 commit 106d803

File tree

2 files changed

+28
-124
lines changed

2 files changed

+28
-124
lines changed

src/serialize.h

Lines changed: 26 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -214,47 +214,7 @@ template<typename X> const X& AsBase(const X& x) { return x; }
214214
template<typename Stream, typename Type, typename Operation> \
215215
static inline void SerializationOps(Type& obj, Stream& s, Operation ser_action) \
216216

217-
/** Variant of FORMATTER_METHODS that supports a declared parameter type.
218-
*
219-
* If a formatter has a declared parameter type, it must be invoked directly or
220-
* indirectly with a parameter of that type. This permits making serialization
221-
* depend on run-time context in a type-safe way.
222-
*
223-
* Example use:
224-
* struct BarParameter { bool fancy; ... };
225-
* struct Bar { ... };
226-
* struct FooFormatter {
227-
* FORMATTER_METHODS(Bar, obj, BarParameter, param) {
228-
* if (param.fancy) {
229-
* READWRITE(VARINT(obj.value));
230-
* } else {
231-
* READWRITE(obj.value);
232-
* }
233-
* }
234-
* };
235-
* which would then be invoked as
236-
* READWRITE(WithParams(BarParameter{...}, Using<FooFormatter>(obj.foo)))
237-
*
238-
* Note that WithParams(parameter, obj) can be invoked anywhere in the call
239-
* stack; it is passed down recursively into all serialization code, until
240-
* another WithParams overrides it.
241-
*
242-
* Parameters will be implicitly converted where appropriate. This means that
243-
* "parent" serialization code can use a parameter that derives from, or is
244-
* convertible to, a "child" formatter's parameter type.
245-
*
246-
* Compilation will fail in any context where serialization is invoked but
247-
* no parameter of a type convertible to BarParameter is provided.
248-
*/
249-
#define FORMATTER_METHODS_PARAMS(cls, obj, paramcls, paramobj) \
250-
template<typename Stream> \
251-
static void Ser(Stream& s, const cls& obj) { SerializationOps(obj, s, CSerActionSerialize(), s.GetParams()); } \
252-
template<typename Stream> \
253-
static void Unser(Stream& s, cls& obj) { SerializationOps(obj, s, CSerActionUnserialize(), s.GetParams()); } \
254-
template<typename Stream, typename Type, typename Operation> \
255-
static inline void SerializationOps(Type& obj, Stream& s, Operation ser_action, const paramcls& paramobj) \
256-
257-
#define BASE_SERIALIZE_METHODS(cls) \
217+
#define SERIALIZE_METHODS(cls, obj) \
258218
template<typename Stream> \
259219
void Serialize(Stream& s) const \
260220
{ \
@@ -266,26 +226,13 @@ template<typename X> const X& AsBase(const X& x) { return x; }
266226
{ \
267227
static_assert(std::is_same<cls&, decltype(*this)>::value, "Unserialize type mismatch"); \
268228
Unser(s, *this); \
269-
}
270-
271-
/**
272-
* Implement the Serialize and Unserialize methods by delegating to a single templated
273-
* static method that takes the to-be-(de)serialized object as a parameter. This approach
274-
* has the advantage that the constness of the object becomes a template parameter, and
275-
* thus allows a single implementation that sees the object as const for serializing
276-
* and non-const for deserializing, without casts.
277-
*/
278-
#define SERIALIZE_METHODS(cls, obj) \
279-
BASE_SERIALIZE_METHODS(cls) \
229+
} \
280230
FORMATTER_METHODS(cls, obj)
281231

282-
/** Variant of SERIALIZE_METHODS that supports a declared parameter type.
283-
*
284-
* See FORMATTER_METHODS_PARAMS for more information on parameters.
285-
*/
232+
/** Variant of SERIALIZE_METHODS that supports WithParams serialization. */
286233
#define SERIALIZE_METHODS_PARAMS(cls, obj, paramcls, paramobj) \
287-
BASE_SERIALIZE_METHODS(cls) \
288-
FORMATTER_METHODS_PARAMS(cls, obj, paramcls, paramobj)
234+
template<typename Stream, typename Type, typename Operation> \
235+
static inline void SerializeWithParams(Type& obj, Stream& s, Operation ser_action, const paramcls& paramobj)
289236

290237
#ifndef CHAR_EQUALS_INT8
291238
template<typename Stream> inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char
@@ -513,10 +460,11 @@ class Wrapper
513460
static_assert(std::is_lvalue_reference<T>::value, "Wrapper needs an lvalue reference type T");
514461
protected:
515462
T m_object;
463+
mutable Formatter m_formatter;
516464
public:
517-
explicit Wrapper(T obj) : m_object(obj) {}
518-
template<typename Stream> void Serialize(Stream &s) const { Formatter().Ser(s, m_object); }
519-
template<typename Stream> void Unserialize(Stream &s) { Formatter().Unser(s, m_object); }
465+
template<typename... Args> Wrapper(T obj, Args&&... args) : m_object(obj), m_formatter(std::forward<Args>(args)...) {}
466+
template<typename Stream> void Serialize(Stream &s) const { m_formatter.Ser(s, m_object); }
467+
template<typename Stream> void Unserialize(Stream &s) { m_formatter.Unser(s, m_object); }
520468
};
521469

522470
/** Cause serialization/deserialization of an object to be done using a specified formatter class.
@@ -656,10 +604,13 @@ struct LimitedStringFormatter
656604
template<class Formatter>
657605
struct VectorFormatter
658606
{
607+
Formatter formatter;
608+
609+
template<typename... Args> VectorFormatter(Args&&... args) : formatter(std::forward<Args>(args)...) {}
610+
659611
template<typename Stream, typename V>
660612
void Ser(Stream& s, const V& v)
661613
{
662-
Formatter formatter;
663614
WriteCompactSize(s, v.size());
664615
for (const typename V::value_type& elem : v) {
665616
formatter.Ser(s, elem);
@@ -669,7 +620,6 @@ struct VectorFormatter
669620
template<typename Stream, typename V>
670621
void Unser(Stream& s, V& v)
671622
{
672-
Formatter formatter;
673623
v.clear();
674624
size_t size = ReadCompactSize(s);
675625
size_t allocated = 0;
@@ -1182,74 +1132,28 @@ size_t GetSerializeSizeMany(int nVersion, const T&... t)
11821132
return sc.size();
11831133
}
11841134

1185-
/** Wrapper that overrides the GetParams() function of a stream. */
1186-
template<typename Params, typename Stream>
1187-
class ParamsStream
1188-
{
1189-
const Params& m_params;
1190-
Stream& m_substream;
1191-
public:
1192-
ParamsStream(const Params& params, Stream& substream) : m_params(params), m_substream(substream) {}
1193-
template<typename U> inline ParamsStream& operator<<(const U& obj) { ::Serialize(*this, obj); return *this; }
1194-
template<typename U> inline ParamsStream& operator>>(U&& obj) { ::Unserialize(*this, obj); return *this; }
1195-
inline void write(const char* ptr, size_t size) { m_substream.write(ptr, size); }
1196-
inline void read(char* ptr, size_t size) { m_substream.read(ptr, size); }
1197-
inline size_t size() const { return m_substream.size(); }
1198-
inline const Params& GetParams() { return m_params; }
1199-
inline int GetVersion() const { return m_substream.GetVersion(); }
1200-
inline int GetType() const { return m_substream.GetType(); }
1201-
inline Stream& GetSubStream() const { return m_substream; }
1202-
};
1203-
1204-
/** Wrapper that serializes objects with the specified parameters. */
1205-
template<typename Params, typename T>
1206-
class ParamsWrapper
1135+
/** Formatter that serializes objects with the specified parameters. */
1136+
template<typename Params>
1137+
struct ParamsFormatter
12071138
{
1208-
static_assert(std::is_lvalue_reference<T>::value, "ParamsWrapper needs an lvalue reference type T");
1209-
const Params& m_params;
1210-
T m_object;
1211-
1212-
public:
1213-
explicit ParamsWrapper(const Params& params, T obj) : m_params(params), m_object(obj) {}
1214-
1215-
//! Serialize to another ParamsStream: optimize by skipping it.
1216-
template<typename Stream, typename PrevParams>
1217-
inline void Serialize(ParamsStream<PrevParams, Stream>& s) const
1218-
{
1219-
ParamsStream<Params, Stream> ss(m_params, s.GetSubStream());
1220-
::Serialize(ss, m_object);
1221-
}
1139+
const Params& params;
12221140

1223-
//! Serialize to any other stream
1224-
template<typename Stream>
1225-
inline void Serialize(Stream& s) const
1226-
{
1227-
ParamsStream<Params, Stream> ss(m_params, s);
1228-
::Serialize(ss, m_object);
1229-
}
1141+
ParamsFormatter(const Params& params) : params(params) {}
12301142

1231-
//! Deserialize from another ParamsStream: optimize by skipping it.
1232-
template<typename Stream, typename PrevParams>
1233-
inline void Unserialize(ParamsStream<PrevParams, Stream>& s)
1234-
{
1235-
ParamsStream<Params, Stream> ss(m_params, s.GetSubStream());
1236-
::Unserialize(ss, m_object);
1237-
}
1238-
1239-
//! Deserialize from any other stream
1240-
template<typename Stream>
1241-
inline void Unserialize(Stream& s)
1242-
{
1243-
ParamsStream<Params, Stream> ss(m_params, s);
1244-
::Unserialize(ss, m_object);
1245-
}
1143+
template<typename Stream, typename T> void Ser(Stream& s, const T& obj) { T::SerializeWithParams(obj, s, CSerActionSerialize(), params); }
1144+
template<typename Stream, typename T> void Unser(Stream& s, T& obj) { T::SerializeWithParams(obj, s, CSerActionUnserialize(), params); }
12461145
};
12471146

1147+
/** Declare-only overloads to help WithParams figure out the right formatter type. */
1148+
template<typename T, typename Params> ParamsFormatter<Params> FormatterType(const T&, const Params&);
1149+
template<typename T, typename Params> VectorFormatter<ParamsFormatter<Params>> FormatterType(const std::vector<T>&, const Params&);
1150+
12481151
/** Return a wrapper around t that (de)serializes it with specified parameter params.
12491152
*
12501153
* See FORMATTER_METHODS_PARAMS for more information on serialization parameters.
12511154
*/
1155+
// FIXME: Argument order should be (t, params) not (params, t) and params should be a vararg
12521156
template<typename Params, typename T>
1253-
static inline ParamsWrapper<Params, T&> WithParams(const Params& params, T&& t) { return ParamsWrapper<Params, T&>(params, t); }
1157+
auto WithParams(const Params& params, T&& t) -> Wrapper<decltype(FormatterType(t, params)), T&> { return {t, params}; }
12541158

12551159
#endif // BITCOIN_SERIALIZE_H

src/test/serialize_tests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ class Base
391391
Base() : m_base_data(17) {}
392392
explicit Base(uint8_t data) : m_base_data(data) {}
393393

394-
#if 0 // the following two are equivalent
394+
#if 1 // the following two are equivalent
395395
SERIALIZE_METHODS_PARAMS(Base, obj, BaseFormat, fmt)
396396
{
397397
if (ser_action.ForRead()) {
@@ -401,7 +401,7 @@ class Base
401401
uint32_t data;
402402
bool ok;
403403
if (fmt == BaseFormat::DEC) {
404-
ok = ParseUint32(str, &data);
404+
ok = ParseUInt32(str, &data);
405405
} else {
406406
ok = IsHex(str);
407407
data = ParseHex(str)[0];

0 commit comments

Comments
 (0)