forked from organicmaps/organicmaps
[ugc] Template and macro magic for visitors.
This commit is contained in:
parent
5634717482
commit
41800988f0
3 changed files with 114 additions and 149 deletions
176
ugc/serdes.hpp
176
ugc/serdes.hpp
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ());
|
||||
|
|
Loading…
Add table
Reference in a new issue