Unverified Commit 6043ad86 authored by Milo Yip's avatar Milo Yip Committed by GitHub

Merge pull request #1138 from Tencent/archiver_example

Add archiver example
parents 83f149e7 f2a28ee4
......@@ -32,6 +32,8 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
endif()
add_executable(archivertest archiver/archiver.cpp archiver/archivertest.cpp)
foreach (example ${EXAMPLES})
add_executable(${example} ${example}/${example}.cpp)
endforeach()
......
#include "archiver.h"
#include <cassert>
#include <stack>
#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson;
struct JsonReaderStackItem {
enum State {
BeforeStart, //!< An object/array is in the stack but it is not yet called by StartObject()/StartArray().
Started, //!< An object/array is called by StartObject()/StartArray().
Closed //!< An array is closed after read all element, but before EndArray().
};
JsonReaderStackItem(const Value* value, State state) : value(value), state(state), index() {}
const Value* value;
State state;
SizeType index; // For array iteration
};
typedef std::stack<JsonReaderStackItem> JsonReaderStack;
#define DOCUMENT reinterpret_cast<Document*>(mDocument)
#define STACK (reinterpret_cast<JsonReaderStack*>(mStack))
#define TOP (STACK->top())
#define CURRENT (*TOP.value)
JsonReader::JsonReader(const char* json) : mDocument(), mStack(), mError(false) {
mDocument = new Document;
DOCUMENT->Parse(json);
if (DOCUMENT->HasParseError())
mError = true;
else {
mStack = new JsonReaderStack;
STACK->push(JsonReaderStackItem(DOCUMENT, JsonReaderStackItem::BeforeStart));
}
}
JsonReader::~JsonReader() {
delete DOCUMENT;
delete STACK;
}
// Archive concept
JsonReader& JsonReader::StartObject() {
if (!mError) {
if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::BeforeStart)
TOP.state = JsonReaderStackItem::Started;
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::EndObject() {
if (!mError) {
if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started)
Next();
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::Member(const char* name) {
if (!mError) {
if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started) {
Value::ConstMemberIterator memberItr = CURRENT.FindMember(name);
if (memberItr != CURRENT.MemberEnd())
STACK->push(JsonReaderStackItem(&memberItr->value, JsonReaderStackItem::BeforeStart));
else
mError = true;
}
else
mError = true;
}
return *this;
}
bool JsonReader::HasMember(const char* name) const {
if (!mError && CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started)
return CURRENT.HasMember(name);
return false;
}
JsonReader& JsonReader::StartArray(size_t* size) {
if (!mError) {
if (CURRENT.IsArray() && TOP.state == JsonReaderStackItem::BeforeStart) {
TOP.state = JsonReaderStackItem::Started;
if (size)
*size = CURRENT.Size();
if (!CURRENT.Empty()) {
const Value* value = &CURRENT[TOP.index];
STACK->push(JsonReaderStackItem(value, JsonReaderStackItem::BeforeStart));
}
else
TOP.state = JsonReaderStackItem::Closed;
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::EndArray() {
if (!mError) {
if (CURRENT.IsArray() && TOP.state == JsonReaderStackItem::Closed)
Next();
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::operator&(bool& b) {
if (!mError) {
if (CURRENT.IsBool()) {
b = CURRENT.GetBool();
Next();
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::operator&(unsigned& u) {
if (!mError) {
if (CURRENT.IsUint()) {
u = CURRENT.GetUint();
Next();
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::operator&(int& i) {
if (!mError) {
if (CURRENT.IsInt()) {
i = CURRENT.GetInt();
Next();
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::operator&(double& d) {
if (!mError) {
if (CURRENT.IsNumber()) {
d = CURRENT.GetDouble();
Next();
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::operator&(std::string& s) {
if (!mError) {
if (CURRENT.IsString()) {
s = CURRENT.GetString();
Next();
}
else
mError = true;
}
return *this;
}
JsonReader& JsonReader::SetNull() {
// This function is for JsonWriter only.
mError = true;
return *this;
}
void JsonReader::Next() {
if (!mError) {
assert(!STACK->empty());
STACK->pop();
if (!STACK->empty() && CURRENT.IsArray()) {
if (TOP.state == JsonReaderStackItem::Started) { // Otherwise means reading array item pass end
if (TOP.index < CURRENT.Size() - 1) {
const Value* value = &CURRENT[++TOP.index];
STACK->push(JsonReaderStackItem(value, JsonReaderStackItem::BeforeStart));
}
else
TOP.state = JsonReaderStackItem::Closed;
}
else
mError = true;
}
}
}
#undef DOCUMENT
#undef STACK
#undef TOP
#undef CURRENT
////////////////////////////////////////////////////////////////////////////////
// JsonWriter
#define WRITER reinterpret_cast<PrettyWriter<StringBuffer>*>(mWriter)
#define STREAM reinterpret_cast<StringBuffer*>(mStream)
JsonWriter::JsonWriter() : mWriter(), mStream() {
mStream = new StringBuffer;
mWriter = new PrettyWriter<StringBuffer>(*STREAM);
}
JsonWriter::~JsonWriter() {
delete WRITER;
delete STREAM;
}
const char* JsonWriter::GetString() const {
return STREAM->GetString();
}
JsonWriter& JsonWriter::StartObject() {
WRITER->StartObject();
return *this;
}
JsonWriter& JsonWriter::EndObject() {
WRITER->EndObject();
return *this;
}
JsonWriter& JsonWriter::Member(const char* name) {
WRITER->String(name, static_cast<SizeType>(strlen(name)));
return *this;
}
bool JsonWriter::HasMember(const char*) const {
// This function is for JsonReader only.
assert(false);
return false;
}
JsonWriter& JsonWriter::StartArray(size_t*) {
WRITER->StartArray();
return *this;
}
JsonWriter& JsonWriter::EndArray() {
WRITER->EndArray();
return *this;
}
JsonWriter& JsonWriter::operator&(bool& b) {
WRITER->Bool(b);
return *this;
}
JsonWriter& JsonWriter::operator&(unsigned& u) {
WRITER->Uint(u);
return *this;
}
JsonWriter& JsonWriter::operator&(int& i) {
WRITER->Int(i);
return *this;
}
JsonWriter& JsonWriter::operator&(double& d) {
WRITER->Double(d);
return *this;
}
JsonWriter& JsonWriter::operator&(std::string& s) {
WRITER->String(s.c_str(), static_cast<SizeType>(s.size()));
return *this;
}
JsonWriter& JsonWriter::SetNull() {
WRITER->Null();
return *this;
}
#undef STREAM
#undef WRITER
#ifndef ARCHIVER_H_
#define ARCHIVER_H_
#include <cstddef>
#include <string>
/**
\class Archiver
\brief Archiver concept
Archiver can be a reader or writer for serialization or deserialization respectively.
class Archiver {
public:
/// \returns true if the archiver is in normal state. false if it has errors.
operator bool() const;
/// Starts an object
Archiver& StartObject();
/// After calling StartObject(), assign a member with a name
Archiver& Member(const char* name);
/// After calling StartObject(), check if a member presents
bool HasMember(const char* name) const;
/// Ends an object
Archiver& EndObject();
/// Starts an array
/// \param size If Archiver::IsReader is true, the size of array is written.
Archiver& StartArray(size_t* size = 0);
/// Ends an array
Archiver& EndArray();
/// Read/Write primitive types.
Archiver& operator&(bool& b);
Archiver& operator&(unsigned& u);
Archiver& operator&(int& i);
Archiver& operator&(double& d);
Archiver& operator&(std::string& s);
/// Write primitive types.
Archiver& SetNull();
//! Whether it is a reader.
static const bool IsReader;
//! Whether it is a writer.
static const bool IsWriter;
};
*/
/// Represents a JSON reader which implements Archiver concept.
class JsonReader {
public:
/// Constructor.
/**
\param json A non-const source json string for in-situ parsing.
\note in-situ means the source JSON string will be modified after parsing.
*/
JsonReader(const char* json);
/// Destructor.
~JsonReader();
// Archive concept
operator bool() const { return !mError; }
JsonReader& StartObject();
JsonReader& Member(const char* name);
bool HasMember(const char* name) const;
JsonReader& EndObject();
JsonReader& StartArray(size_t* size = nullptr);
JsonReader& EndArray();
JsonReader& operator&(bool& b);
JsonReader& operator&(unsigned& u);
JsonReader& operator&(int& i);
JsonReader& operator&(double& d);
JsonReader& operator&(std::string& s);
JsonReader& SetNull();
static const bool IsReader = true;
static const bool IsWriter = !IsReader;
private:
void Next();
// PIMPL
void* mDocument; ///< DOM result of parsing.
void* mStack; ///< Stack for iterating the DOM
bool mError; ///< Whether an error is occured.
};
class JsonWriter {
public:
/// Constructor.
JsonWriter();
/// Destructor.
~JsonWriter();
/// Obtains the serialized JSON string.
const char* GetString() const;
// Archive concept
operator bool() const { return true; }
JsonWriter& StartObject();
JsonWriter& Member(const char* name);
bool HasMember(const char* name) const;
JsonWriter& EndObject();
JsonWriter& StartArray(size_t* size = 0);
JsonWriter& EndArray();
JsonWriter& operator&(bool& b);
JsonWriter& operator&(unsigned& u);
JsonWriter& operator&(int& i);
JsonWriter& operator&(double& d);
JsonWriter& operator&(std::string& s);
JsonWriter& SetNull();
static const bool IsReader = false;
static const bool IsWriter = !IsReader;
private:
// PIMPL idiom
void* mWriter; ///< JSON writer.
void* mStream; ///< Stream buffer.
};
#endif // ARCHIVER_H__
#include "archiver.h"
#include <iostream>
#include <vector>
//////////////////////////////////////////////////////////////////////////////
// Test1: simple object
struct Student {
std::string name;
unsigned age;
double height;
bool canSwim;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Student& s) {
ar.StartObject();
ar.Member("name") & s.name;
ar.Member("age") & s.age;
ar.Member("height") & s.height;
ar.Member("canSwim") & s.canSwim;
return ar.EndObject();
}
std::ostream& operator<<(std::ostream& os, const Student& s) {
return os << s.name << " " << s.age << " " << s.height << " " << s.canSwim;
}
void test1() {
std::string json;
// Serialize
{
Student s = { "Lua", 9, 150.5, true };
JsonWriter writer;
writer & s;
json = writer.GetString();
std::cout << json << std::endl;
}
// Deserialize
{
Student s;
JsonReader reader(json.c_str());
reader & s;
std::cout << s << std::endl;
}
}
//////////////////////////////////////////////////////////////////////////////
// Test2: std::vector <=> JSON array
//
// You can map a JSON array to other data structures as well
struct Group {
std::string groupName;
std::vector<Student> students;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Group& g) {
ar.StartObject();
ar.Member("groupName");
ar & g.groupName;
ar.Member("students");
size_t studentCount = g.students.size();
ar.StartArray(&studentCount);
if (ar.IsReader)
g.students.resize(studentCount);
for (size_t i = 0; i < studentCount; i++)
ar & g.students[i];
ar.EndArray();
return ar.EndObject();
}
std::ostream& operator<<(std::ostream& os, const Group& g) {
os << g.groupName << std::endl;
for (std::vector<Student>::const_iterator itr = g.students.begin(); itr != g.students.end(); ++itr)
os << *itr << std::endl;
return os;
}
void test2() {
std::string json;
// Serialize
{
Group g;
g.groupName = "Rainbow";
Student s1 = { "Lua", 9, 150.5, true };
Student s2 = { "Mio", 7, 120.0, false };
g.students.push_back(s1);
g.students.push_back(s2);
JsonWriter writer;
writer & g;
json = writer.GetString();
std::cout << json << std::endl;
}
// Deserialize
{
Group g;
JsonReader reader(json.c_str());
reader & g;
std::cout << g << std::endl;
}
}
//////////////////////////////////////////////////////////////////////////////
// Test3: polymorphism & friend
//
// Note that friendship is not necessary but make things simpler.
class Shape {
public:
virtual ~Shape() {}
virtual const char* GetType() const = 0;
virtual void Print(std::ostream& os) const = 0;
protected:
Shape() {}
Shape(double x, double y) : x_(x), y_(y) {}
template <typename Archiver>
friend Archiver& operator&(Archiver& ar, Shape& s);
double x_, y_;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Shape& s) {
ar.Member("x") & s.x_;
ar.Member("y") & s.y_;
return ar;
}
class Circle : public Shape {
public:
Circle() {}
Circle(double x, double y, double radius) : Shape(x, y), radius_(radius) {}
~Circle() {}
const char* GetType() const { return "Circle"; }
void Print(std::ostream& os) const {
os << "Circle (" << x_ << ", " << y_ << ")" << " radius = " << radius_;
}
private:
template <typename Archiver>
friend Archiver& operator&(Archiver& ar, Circle& c);
double radius_;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Circle& c) {
ar & static_cast<Shape&>(c);
ar.Member("radius") & c.radius_;
return ar;
}
class Box : public Shape {
public:
Box() {}
Box(double x, double y, double width, double height) : Shape(x, y), width_(width), height_(height) {}
~Box() {}
const char* GetType() const { return "Box"; }
void Print(std::ostream& os) const {
os << "Box (" << x_ << ", " << y_ << ")" << " width = " << width_ << " height = " << height_;
}
private:
template <typename Archiver>
friend Archiver& operator&(Archiver& ar, Box& b);
double width_, height_;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Box& b) {
ar & static_cast<Shape&>(b);
ar.Member("width") & b.width_;
ar.Member("height") & b.height_;
return ar;
}
class Canvas {
public:
Canvas() {}
~Canvas() { Clear(); }
void Clear() {
for (std::vector<Shape*>::iterator itr = shapes_.begin(); itr != shapes_.end(); ++itr)
delete *itr;
}
void AddShape(Shape* shape) { shapes_.push_back(shape); }
void Print(std::ostream& os) {
for (std::vector<Shape*>::iterator itr = shapes_.begin(); itr != shapes_.end(); ++itr) {
(*itr)->Print(os);
std::cout << std::endl;
}
}
private:
template <typename Archiver>
friend Archiver& operator&(Archiver& ar, Canvas& c);
std::vector<Shape*> shapes_;
};
template <typename Archiver>
Archiver& operator&(Archiver& ar, Shape*& shape) {
std::string type = ar.IsReader ? "" : shape->GetType();
ar.StartObject();
ar.Member("type") & type;
if (type == "Circle") {
if (ar.IsReader) shape = new Circle;
ar & static_cast<Circle&>(*shape);
}
else if (type == "Box") {
if (ar.IsReader) shape = new Box;
ar & static_cast<Box&>(*shape);
}
return ar.EndObject();
}
template <typename Archiver>
Archiver& operator&(Archiver& ar, Canvas& c) {
size_t shapeCount = c.shapes_.size();
ar.StartArray(&shapeCount);
if (ar.IsReader) {
c.Clear();
c.shapes_.resize(shapeCount);
}
for (size_t i = 0; i < shapeCount; i++)
ar & c.shapes_[i];
return ar.EndArray();
}
void test3() {
std::string json;
// Serialize
{
Canvas c;
c.AddShape(new Circle(1.0, 2.0, 3.0));
c.AddShape(new Box(4.0, 5.0, 6.0, 7.0));
JsonWriter writer;
writer & c;
json = writer.GetString();
std::cout << json << std::endl;
}
// Deserialize
{
Canvas c;
JsonReader reader(json.c_str());
reader & c;
c.Print(std::cout);
}
}
//////////////////////////////////////////////////////////////////////////////
int main() {
test1();
test2();
test3();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment