[ugc] Template and macro magic for visitors.

This commit is contained in:
Yuri Gorshenin 2017-06-21 01:07:36 +03:00 committed by Yuri Gorshenin
parent 5634717482
commit 41800988f0
3 changed files with 114 additions and 149 deletions

View file

@ -28,63 +28,33 @@ class Serializer
public:
Serializer(Sink & sink, HeaderV0 const & header) : m_sink(sink), m_header(header) {}
void operator()(uint8_t const d) { WriteToSink(m_sink, d); }
void operator()(uint32_t const d) { WriteToSink(m_sink, d); }
void operator()(uint64_t const d) { WriteToSink(m_sink, d); }
void operator()(std::string const & s) { utils::WriteString(m_sink, s); }
void operator()(uint8_t const d, char const * /* name */ = nullptr) { WriteToSink(m_sink, d); }
void operator()(uint32_t const d, char const * /* name */ = nullptr) { WriteToSink(m_sink, d); }
void operator()(uint64_t const d, char const * /* name */ = nullptr) { WriteToSink(m_sink, d); }
void operator()(std::string const & s, char const * /* name */ = nullptr)
{
utils::WriteString(m_sink, s);
}
void SerRating(float const f)
void VisitRating(float const f, char const * /* name */ = nullptr)
{
CHECK_GREATER_OR_EQUAL(f, 0.0, ());
auto const d = static_cast<uint32_t>(round(f * 10));
SerVarUint(d);
VisitVarUint(d);
}
template <typename T>
void SerVarUint(T const & t)
void VisitVarUint(T const & t, char const * /* name */ = nullptr)
{
WriteVarUint(m_sink, t);
}
template <typename T>
void operator()(vector<T> const & vs)
void operator()(Time const & t, char const * /* name */ = nullptr)
{
SerVarUint(static_cast<uint32_t>(vs.size()));
for (auto const & v : vs)
(*this)(v);
VisitVarUint(ToDaysSinceEpoch(t));
}
void operator()(RatingRecord const & ratingRecord)
{
(*this)(ratingRecord.m_key);
SerRating(ratingRecord.m_value);
}
void operator()(Rating const & rating)
{
(*this)(rating.m_ratings);
SerRating(rating.m_aggValue);
}
void operator()(UID const & uid)
{
(*this)(uid.m_hi);
(*this)(uid.m_lo);
}
void operator()(Author const & author)
{
(*this)(author.m_uid);
(*this)(author.m_name);
}
void operator()(Text const & text)
{
(*this)(text.m_lang);
(*this)(text.m_text);
}
void operator()(Sentiment sentiment)
void operator()(Sentiment sentiment, char const * /* name */ = nullptr)
{
switch (sentiment)
{
@ -93,27 +63,18 @@ public:
}
}
void operator()(Review const & review)
template <typename T>
void operator()(vector<T> const & vs, char const * /* name */ = nullptr)
{
(*this)(review.m_id);
(*this)(review.m_text);
(*this)(review.m_author);
SerRating(review.m_rating);
(*this)(review.m_sentiment);
SerVarUint(review.DaysSinceEpoch());
VisitVarUint(static_cast<uint32_t>(vs.size()));
for (auto const & v : vs)
(*this)(v);
}
void operator()(Attribute const & attribute)
template <typename R>
void operator()(R const & r, char const * /* name */ = nullptr)
{
(*this)(attribute.m_key);
(*this)(attribute.m_value);
}
void operator()(UGC const & ugc)
{
(*this)(ugc.m_rating);
(*this)(ugc.m_reviews);
(*this)(ugc.m_attributes);
r.Visit(*this);
}
private:
@ -127,20 +88,31 @@ class DeserializerV0
public:
DeserializerV0(Source & source, HeaderV0 const & header) : m_source(source), m_header(header) {}
void operator()(uint8_t & d) { ReadPrimitiveFromSource(m_source, d); }
void operator()(uint32_t & d) { ReadPrimitiveFromSource(m_source, d); }
void operator()(uint64_t & d) { ReadPrimitiveFromSource(m_source, d); }
void operator()(std::string & s) { utils::ReadString(m_source, s); }
void DesRating(float & f)
void operator()(uint8_t & d, char const * /* name */ = nullptr)
{
uint32_t d = 0;
DesVarUint(d);
ReadPrimitiveFromSource(m_source, d);
}
void operator()(uint32_t & d, char const * /* name */ = nullptr)
{
ReadPrimitiveFromSource(m_source, d);
}
void operator()(uint64_t & d, char const * /* name */ = nullptr)
{
ReadPrimitiveFromSource(m_source, d);
}
void operator()(std::string & s, char const * /* name */ = nullptr)
{
utils::ReadString(m_source, s);
}
void VisitRating(float & f, char const * /* name */ = nullptr)
{
auto const d = DesVarUint<uint32_t>();
f = static_cast<float>(d) / 10;
}
template <typename T>
void DesVarUint(T & t)
void VisitVarUint(T & t, char const * /* name */ = nullptr)
{
t = ReadVarUint<T, Source>(m_source);
}
@ -151,46 +123,12 @@ public:
return ReadVarUint<T, Source>(m_source);
}
void operator()(RatingRecord & ratingRecord)
void operator()(Time & t, char const * /* name */ = nullptr)
{
(*this)(ratingRecord.m_key);
DesRating(ratingRecord.m_value);
t = FromDaysSinceEpoch(DesVarUint<uint32_t>());
}
template <typename T>
void operator()(vector<T> & vs)
{
auto const size = DesVarUint<uint32_t>();
vs.resize(size);
for (auto & v : vs)
(*this)(v);
}
void operator()(Rating & rating)
{
(*this)(rating.m_ratings);
DesRating(rating.m_aggValue);
}
void operator()(UID & uid)
{
(*this)(uid.m_hi);
(*this)(uid.m_lo);
}
void operator()(Author & author)
{
(*this)(author.m_uid);
(*this)(author.m_name);
}
void operator()(Text & text)
{
(*this)(text.m_lang);
(*this)(text.m_text);
}
void operator()(Sentiment & sentiment)
void operator()(Sentiment & sentiment, char const * /* name */ = nullptr)
{
uint8_t s = 0;
(*this)(s);
@ -202,27 +140,19 @@ public:
}
}
void operator()(Review & review)
template <typename T>
void operator()(vector<T> & vs, char const * /* name */ = nullptr)
{
(*this)(review.m_id);
(*this)(review.m_text);
(*this)(review.m_author);
DesRating(review.m_rating);
(*this)(review.m_sentiment);
review.SetDaysSinceEpoch(DesVarUint<uint32_t>());
auto const size = DesVarUint<uint32_t>();
vs.resize(size);
for (auto & v : vs)
(*this)(v);
}
void operator()(Attribute & attribute)
template <typename R>
void operator()(R & r, char const * /* name */ = nullptr)
{
(*this)(attribute.m_key);
(*this)(attribute.m_value);
}
void operator()(UGC & ugc)
{
(*this)(ugc.m_rating);
(*this)(ugc.m_reviews);
(*this)(ugc.m_attributes);
r.Visit(*this);
}
private:

View file

@ -4,7 +4,6 @@
#include "coding/hex.hpp"
#include <chrono>
#include <cstdint>
#include <memory>
@ -13,6 +12,18 @@
#include <string>
#include <vector>
#define DECLARE_VISITOR(...) \
template <typename Visitor> \
void Visit(Visitor & visitor) \
{ \
__VA_ARGS__; \
} \
template <typename Visitor> \
void Visit(Visitor & visitor) const \
{ \
__VA_ARGS__; \
}
namespace ugc
{
using TranslationKey = std::string;
@ -33,11 +44,25 @@ inline std::string DebugPrint(Sentiment const & sentiment)
}
}
inline uint32_t ToDaysSinceEpoch(Time const & time)
{
auto const hours = std::chrono::duration_cast<std::chrono::hours>(time.time_since_epoch());
return static_cast<uint32_t>(hours.count()) / 24;
}
inline Time FromDaysSinceEpoch(uint32_t days)
{
auto const hours = std::chrono::hours(days * 24);
return Time(hours);
}
struct RatingRecord
{
RatingRecord() = default;
RatingRecord(TranslationKey const & key, float const value) : m_key(key), m_value(value) {}
DECLARE_VISITOR(visitor(m_key, "key"), visitor.VisitRating(m_value, "value"))
bool operator==(RatingRecord const & rhs) const
{
return m_key == rhs.m_key && m_value == rhs.m_value;
@ -62,6 +87,8 @@ struct Rating
{
}
DECLARE_VISITOR(visitor(m_ratings, "ratings"), visitor.VisitRating(m_aggValue, "aggValue"))
bool operator==(Rating const & rhs) const
{
return m_ratings == rhs.m_ratings && m_aggValue == rhs.m_aggValue;
@ -70,8 +97,8 @@ struct Rating
friend std::string DebugPrint(Rating const & rating)
{
std::ostringstream os;
os << "Rating [ ratings:" << ::DebugPrint(rating.m_ratings) << ", aggValue:" << rating.m_aggValue
<< " ]";
os << "Rating [ ratings:" << ::DebugPrint(rating.m_ratings)
<< ", aggValue:" << rating.m_aggValue << " ]";
return os.str();
}
@ -86,8 +113,9 @@ struct UID
std::string ToString() const { return NumToHex(m_hi) + NumToHex(m_lo); }
bool operator==(UID const & rhs) const { return m_hi == rhs.m_hi && m_lo == rhs.m_lo; }
DECLARE_VISITOR(visitor(m_hi, "hi"), visitor(m_lo, "lo"));
bool operator==(UID const & rhs) const { return m_hi == rhs.m_hi && m_lo == rhs.m_lo; }
friend std::string DebugPrint(UID const & uid)
{
std::ostringstream os;
@ -104,8 +132,9 @@ struct Author
Author() = default;
Author(UID const & uid, std::string const & name) : m_uid(uid), m_name(name) {}
bool operator==(Author const & rhs) const { return m_uid == rhs.m_uid && m_name == rhs.m_name; }
DECLARE_VISITOR(visitor(m_uid, "uid"), visitor(m_name, "name"));
bool operator==(Author const & rhs) const { return m_uid == rhs.m_uid && m_name == rhs.m_name; }
friend std::string DebugPrint(Author const & author)
{
std::ostringstream os;
@ -122,8 +151,9 @@ struct Text
Text() = default;
Text(std::string const & text, uint8_t const lang) : m_text(text), m_lang(lang) {}
bool operator==(Text const & rhs) const { return m_lang == rhs.m_lang && m_text == rhs.m_text; }
DECLARE_VISITOR(visitor(m_lang, "lang"), visitor(m_text, "text"));
bool operator==(Text const & rhs) const { return m_lang == rhs.m_lang && m_text == rhs.m_text; }
friend std::string DebugPrint(Text const & text)
{
std::ostringstream os;
@ -147,24 +177,16 @@ struct Review
{
}
DECLARE_VISITOR(visitor(m_id, "id"), visitor(m_text, "text"), visitor(m_author, "author"),
visitor.VisitRating(m_rating, "rating"), visitor(m_sentiment, "sentiment"),
visitor(m_time, "time"))
bool operator==(Review const & rhs) const
{
return m_id == rhs.m_id && m_text == rhs.m_text && m_author == rhs.m_author &&
m_rating == rhs.m_rating && m_sentiment == rhs.m_sentiment && m_time == rhs.m_time;
}
uint32_t DaysSinceEpoch() const
{
auto const hours = std::chrono::duration_cast<std::chrono::hours>(m_time.time_since_epoch());
return static_cast<uint32_t>(hours.count()) / 24;
}
void SetDaysSinceEpoch(uint32_t days)
{
auto const hours = std::chrono::hours(days * 24);
m_time = Time(hours);
}
friend std::string DebugPrint(Review const & review)
{
std::ostringstream os;
@ -174,7 +196,7 @@ struct Review
os << "author:" << DebugPrint(review.m_author) << ", ";
os << "rating:" << review.m_rating << ", ";
os << "sentiment:" << DebugPrint(review.m_sentiment) << ", ";
os << "days since epoch:" << review.DaysSinceEpoch() << " ]";
os << "days since epoch:" << ToDaysSinceEpoch(review.m_time) << " ]";
return os.str();
}
@ -196,6 +218,8 @@ struct Attribute
{
}
DECLARE_VISITOR(visitor(m_key, "key"), visitor(m_value, "value"))
bool operator==(Attribute const & rhs) const
{
return m_key == rhs.m_key && m_value == rhs.m_value;
@ -221,7 +245,11 @@ struct UGC
{
}
bool operator==(UGC const & rhs) const {
DECLARE_VISITOR(visitor(m_rating, "rating"), visitor(m_reviews, "review"),
visitor(m_attributes, "attributes"))
bool operator==(UGC const & rhs) const
{
return m_rating == rhs.m_rating && m_reviews == rhs.m_reviews &&
m_attributes == rhs.m_attributes;
}
@ -274,3 +302,5 @@ struct UGCUpdate
ReviewFeedback m_feedbacks;
};
} // namespace ugc
#undef DECLARE_VISITOR

View file

@ -29,6 +29,7 @@ Rating GetTestRating()
}
MemWriter<Buffer> MakeSink(Buffer & buffer) { return MemWriter<Buffer>(buffer); }
ReaderSource<MemReader> MakeSource(Buffer const & buffer)
{
MemReader reader(buffer.data(), buffer.size());
@ -37,7 +38,7 @@ ReaderSource<MemReader> MakeSource(Buffer const & buffer)
UNIT_TEST(SerDes_Rating)
{
auto const expectedRating = GetTestRating();
auto expectedRating = GetTestRating();
TEST_EQUAL(expectedRating, expectedRating, ());
HeaderV0 header;
@ -46,14 +47,16 @@ UNIT_TEST(SerDes_Rating)
{
auto sink = MakeSink(buffer);
Ser(sink, header)(expectedRating);
Ser ser(sink, header);
ser(expectedRating);
}
Rating actualRating({} /* ratings */, {} /* aggValue */);
{
auto source = MakeSource(buffer);
Des(source, header)(actualRating);
Des des(source, header);
des(actualRating);
}
TEST_EQUAL(expectedRating, actualRating, ());
@ -61,7 +64,7 @@ UNIT_TEST(SerDes_Rating)
UNIT_TEST(SerDes_UGC)
{
auto const expectedUGC = Api::MakeTestUGC1();
auto expectedUGC = Api::MakeTestUGC1();
TEST_EQUAL(expectedUGC, expectedUGC, ());
HeaderV0 header;
@ -70,13 +73,15 @@ UNIT_TEST(SerDes_UGC)
{
auto sink = MakeSink(buffer);
Ser(sink, header)(expectedUGC);
Ser ser(sink, header);
ser(expectedUGC);
}
UGC actualUGC({} /* rating */, {} /* reviews */, {} /* attributes */);
{
auto source = MakeSource(buffer);
Des(source, header)(actualUGC);
Des des(source, header);
des(actualUGC);
}
TEST_EQUAL(expectedUGC, actualUGC, ());