#include <type_traits>
#include <cstdint>
#include <cctype>
#include <vector>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cassert>
// Some basic types that should build serializable structures from
template<typename T>
using Array = std::vector<T>;
using Boolean = bool;
using Byte = uint8_t;
using Char = int8_t;
using Unsigned32 = uint32_t;
using Unsigned64 = uint64_t;
using Integer32 = int32_t;
using Integer64 = int64_t;
using Float32 = float;
using Float64 = double;
using String = std::string;
// discourage 16bit types, CPUs aren't so efficient at these
// TODO: enums
/*
about enums:
Looks like C++17 might make reflection of enums nice.
http://aantron.github.io/better-enums/demo/C++17ReflectionProposal.html
In the mean time, something like this gets close with the use of an ugly macro where the enums are declared
however it provides similar syntax to what C++17 may have for using the reflection
http://melpon.org/wandbox/permlink/QelcwZNLi4gIx8Ux
*/
struct EnumBaseValue
{
Integer32 id;
String name;
};
struct EnumBase
{
String name;
Array<EnumBaseValue> values;
};
#if 0
#if 1
template <class V, class T>
void Visit(V& v, T& var, typename V::Visit(T&))//, typename std::enable_if< ! std::is_function< decltype(T::Visit)(V&) >::value , T >::type* = 0 )
// typename std::enable_if< false /* !std::is_function<T::Visit>::value*/ >::type
//void Visit(V& v, T& var, typename std::enable_if< std::is_function<T::Visit>::type , T >::type* = 0 )
{
v.Visit(var);
}
#endif
template <class V, class T>
void Visit(V& v, T& var) // , typename T::Visit)
//, typename std::enable_if< std::is_function< decltype(T::Visit)(V&) >::value , T >::type* = 0 )
{
var.Visit(v);
}
template <class V>
void Visit(V& v, Integer32& i)
{
v.Visit(i);
}
template <class V>
void Visit(V& v, String& str)
{
v.Visit(str);
}
template <class V, class T>
void Visit(V& v, Array<T>& arr)
{
v.Visit(arr);
}
#endif
class XMLSerializerVisitor
{
public:
XMLSerializerVisitor()
{
reset();
}
void Enter(const char* name)
{
indent(depth);
output += "<";
output += name;
output += ">\n";
depth++;
}
void Exit(const char* name)
{
depth--;
indent(depth);
output += "</";
output += name;
output += ">\n";
}
template <class T>
void Visit(Array<T>& arr)
{
/*
Integer32 siz = arr.size();
Visit("length", siz);
for (int i = 0; i < arr.size(); i++)
{
char buf[1024];
snprintf(buf, 1024, "element[%i]", i);
Visit(buf, arr.at(i));
}
*/
for (auto i : arr)
{
Visit("item", i);
}
}
void Visit(Integer32& var)
{
indent(depth);
output += std::to_string(var);
output += "\n";
}
void Visit(String& var)
{
// TODO: limitiation is that the string can not have a new line
indent(depth);
output += var;
output += "\n";
}
template <class T>
void Visit(const char* name, T& var)
{
Enter(name);
//::Visit(*this, var);
Visit(var);
Exit(name);
}
template <class T>
void Visit(T& var)
{
var.Visit(*this);
}
std::string& Output()
{
output += "</xml>\n";
return output;
}
void reset()
{
output = "<xml>\n";
depth = 1;
}
private:
int depth = 0;
std::string output;
void indent(int spaces)
{
for(int i = 0; i < spaces; i++)
{
output += " ";
}
}
};
class XMLDeserializerVisitor
{
public:
XMLDeserializerVisitor(std::string data) : input(data)
{
String str;
Visit(str);
// deserialize the <xml> tag
}
void Enter(const char* name)
{
String str;
Visit(str);
//printf("enter deserializing for -%s-, got -%s-\n", name, str.c_str());
}
void Exit(const char* name)
{
String str;
Visit(str);
//printf("exit deserializing for -%s-, got -%s-\n", name, str.c_str());
}
template <class T>
void Visit(Array<T>& arr)
{
/*
Integer32 siz = 0;
Visit("length", siz);
arr.resize(siz);
//printf("deserializing array of len: %i\n", siz);
for (int i = 0; i < arr.size(); i++)
{
char buf[1024];
snprintf(buf, 1024, "element[%i]", i);
Visit(buf, arr.at(i));
}
*/
while (true)
{
int oldOffset = offset;
String str;
Visit(str);
offset = oldOffset;
if (str != "<item>")
break;
T item;
Visit("item", item);
arr.push_back(item);
}
}
void Visit(Integer32& var)
{
const char* ptr = input.c_str();
char* endPtr;
var = strtol(ptr + offset, &endPtr, 10);
offset = endPtr - ptr;
}
void Visit(String& var)
{
stripWhiteSpace();
int newLineOff = input.find("\n", offset);
if (newLineOff != std::string::npos)
var = input.substr(offset, newLineOff-offset);
offset = newLineOff;
}
/*
template <class T>
void Visit(const char* name, T& var)
{
Enter(name);
::Visit(*this, var);
Exit(name);
}
*/
template <class T>
void Visit(const char* name, T& var)
{
Enter(name);
Visit(var);
Exit(name);
}
template <class T>
void Visit(T& var)
{
var.Visit(*this);
}
private:
std::string input;
int offset = 0;
void stripWhiteSpace()
{
while (isspace(input.c_str()[offset]))
{
offset++;
}
}
};
class JsonSerializerVisitor
{
public:
JsonSerializerVisitor()
{
reset();
}
void Enter(const char* name)
{
output += indent(depth) + quoted(name) + " : {\n";
depth++;
}
void Exit(const char* name)
{
output += "\n" + indent(--depth) + "}\n";
}
template <class T>
void Visit(Array<T>& arr)
{
output += "[ ";
for (int i = 0; i < arr.size(); i++)
{
Visit(arr.at(i));
if ( i != arr.size() - 1 )
output += " ,";
output += " ";
}
output += "]";
}
void Visit(Integer32& var)
{
output += std::to_string(var);
}
void Visit(String& var)
{
output += quoted(var.c_str());
}
template <class T>
void Visit(const char* name, T& var)
{
if (lastDepth == depth)
output += " ,\n";
lastDepth = depth;
output += indent(depth) + quoted(name) + " : ";
// ::Visit(*this, var);
Visit(var);
}
template <class T>
void Visit(T& var)
{
var.Visit(*this);
}
std::string& Output()
{
output += "}\n";
return output;
}
void reset()
{
output = "{\n";
depth = 1;
}
private:
int lastDepth = 0;
int depth = 0;
std::string output;
std::string quoted(const char* name)
{
return std::string("\"" + std::string(name) + "\"");
}
std::string indent(int spaces)
{
return std::string(spaces*2, ' ');
}
};
class JsonDeserializerVisitor
{
public:
JsonDeserializerVisitor(std::string data) : input(data)
{
String str;
ParseString(str); // gets the '{'
assert(str == "{");
}
void ParseString(String& var)
{
stripWhiteSpace();
int pos = offset;
while (input.c_str()[pos])
{
if (isspace(input.c_str()[pos]))
{
var = input.substr(offset, pos-offset);
offset = pos;
return;
}
pos++;
}
}
void Enter(const char* name)
{
String str;
ParseString(str); // gets the name
//printf("name: -%s-", quoted(name).c_str());
//printf("str: -%s-", str.c_str());
assert(str == quoted(name));
ParseString(str); // gets the ':'
assert(str == ":");
ParseString(str); // gets the '{'
assert(str == "{");
//printf("enter deserializing for -%s-, got -%s-\n", name, str.c_str());
}
void Exit(const char* name)
{
String str;
ParseString(str); // gets the '}'
assert(str == "}");
//printf("exit deserializing for -%s-, got -%s-\n", name, str.c_str());
}
template <class T>
void Visit(Array<T>& arr)
{
String str;
ParseString(str); // gets the '['
assert(str == "[");
while (true)
{
T item;
Visit(item);
arr.push_back(item);
String str;
ParseString(str); // gets the ',' or ']' or '],'
if (str != ",")
{
//printf("expecting ]");
//printf("str: -%s-", str.c_str());
assert(str == "]");
break;
}
}
}
void Visit(Integer32& var)
{
const char* ptr = input.c_str();
char* endPtr;
var = strtol(ptr + offset, &endPtr, 10);
offset = endPtr - ptr;
}
void Visit(String& var)
{
// TODO
stripWhiteSpace();
int newLineOff = input.find("\"", offset + 1);
if (newLineOff != std::string::npos)
var = input.substr(offset + 1, newLineOff - offset - 1);
offset = newLineOff + 1;
}
template <class T>
void Visit(const char* name, T& var)
{
String str;
ParseString(str); // gets the name
//printf("name: -%s-", quoted(name).c_str());
//printf("str: -%s-", str.c_str());
assert(str == quoted(name));
ParseString(str); // gets the ':'
assert(str == ":");
// ::Visit(*this, var);
Visit(var);
int oldOffset = offset;
ParseString(str); // gets the ',' if there
if (str != ",")
offset = oldOffset;
}
template <class T>
void Visit(T& var)
{
var.Visit(*this);
}
private:
std::string input;
int offset = 0;
std::string quoted(const char* name)
{
return std::string("\"" + std::string(name) + "\"");
}
void stripWhiteSpace()
{
while (isspace(input.c_str()[offset]))
{
offset++;
}
}
};
struct Person
{
Boolean m_bool;
Byte m_uint8;
Char m_int8;
Unsigned32 m_uint32;
Unsigned64 m_uint64;
Integer32 m_int32;
Integer64 m_int64;
Float32 m_flt32;
Float64 m_flt64;
Integer32 id;
String name;
Array<String> email;
Array<String> phoneNumbers;
// This can do both the serializing and de-serializing
// This might be good to have generated, but possibly being more explicit could be better too
// Also note the members could be private and this will still work
template <class V>
void Visit(V& v)
{
v.Enter("person");
v.Visit("id",id);
v.Visit("name",name);
v.Visit("email",email);
v.Visit("numbers",phoneNumbers);
v.Exit("person");
}
};
/*
struct Employer
{
String name;
};
*/
/*
struct Property
{
Property() : m_type(PT_Invalid) {}
~Property() {}
explicit Property(Boolean a_bool ) : m_type(PT_Boolean ) { m_basicType.m_bool = a_bool ; }
explicit Property(Byte a_uint8 ) : m_type(PT_Byte ) { m_basicType.m_uint8 = a_uint8 ; }
explicit Property(Char a_int8 ) : m_type(PT_Char ) { m_basicType.m_int8 = a_int8 ; }
explicit Property(Unsigned32 a_uint32) : m_type(PT_Unsigned32) { m_basicType.m_uint32 = a_uint32; }
explicit Property(Unsigned64 a_uint64) : m_type(PT_Unsigned64) { m_basicType.m_uint64 = a_uint64; }
explicit Property(Integer32 a_int32 ) : m_type(PT_Integer32 ) { m_basicType.m_int32 = a_int32 ; }
explicit Property(Integer64 a_int64 ) : m_type(PT_Integer64 ) { m_basicType.m_int64 = a_int64 ; }
explicit Property(Float32 a_flt32 ) : m_type(PT_Float32 ) { m_basicType.m_flt32 = a_flt32 ; }
explicit Property(Float64 a_flt64 ) : m_type(PT_Float64 ) { m_basicType.m_flt64 = a_flt64 ; }
explicit Property(const Array<Property>& a_array ) : m_type(PT_Array ) { m_array = a_array ; }
explicit Property(const String& a_string) : m_type(PT_String) { m_string = a_string; }
enum PropertyType
{
// Uninitialize state
PT_Invalid = 0,
// Basic types
PT_Boolean = 1,
PT_Byte = 2,
PT_Char = 3,
PT_Unsigned32 = 4,
PT_Unsigned64 = 5,
PT_Integer32 = 6,
PT_Integer64 = 7,
PT_Float32 = 8,
PT_Float64 = 9,
// Complex types
PT_Array = 10,
PT_String = 11,
};
PropertyType m_type;
union {
Boolean m_bool;
Byte m_uint8;
Char m_int8;
Unsigned32 m_uint32;
Unsigned64 m_uint64;
Integer32 m_int32;
Integer64 m_int64;
Float32 m_flt32;
Float64 m_flt64;
} m_basicType;
Array<Property> m_array;
String m_string;
// This can do both the serializing and de-serializing
// This might be good to have generated, but possibly being more explicit could be better too
// Also note the members could be private and this will still work
template <class V>
void Visit(V& v)
{
v.Enter("Property");
Integer32 t = m_type;
v.Visit("type", t);
switch (m_type)
{
case PT_Boolean:
v.Visit("value", m_basicType.m_bool); break;
case PT_Byte:
v.Visit("value", m_basicType.m_uint8); break;
case PT_Char:
v.Visit("value", m_basicType.m_int8); break;
case PT_Unsigned32:
v.Visit("value", m_basicType.m_uint32); break;
case PT_Unsigned64:
v.Visit("value", m_basicType.m_uint64); break;
case PT_Integer32:
v.Visit("value", m_basicType.m_int32); break;
case PT_Integer64:
v.Visit("value", m_basicType.m_int64); break;
case PT_Float32:
v.Visit("value", m_basicType.m_flt32); break;
case PT_Float64:
v.Visit("value", m_basicType.m_flt64); break;
case PT_Array:
v.Visit("value", m_array); break;
case PT_String:
v.Visit("value", m_string); break;
}
v.Exit("Property");
}
};
*/
int main(int argc, char* argv[])
{
std::string input;
{
Person a;
a.id = 10;
a.name = "blah";
a.email.push_back("em@ail.com");
a.email.push_back("other@add.com");
a.phoneNumbers.push_back("123555");
XMLSerializerVisitor serializer;
serializer.Visit(a);
printf("%s", serializer.Output().c_str());
input = serializer.Output();
}
{
Person b;
XMLDeserializerVisitor deserializer(input);
deserializer.Visit(b);
{
XMLSerializerVisitor serializer;
serializer.Visit(b);
printf("%s", serializer.Output().c_str());
}
}
{
Person a;
a.id = 10;
a.name = "blah";
a.email.push_back("em@ail.com");
a.email.push_back("other@add.com");
a.phoneNumbers.push_back("123555");
JsonSerializerVisitor serializer;
serializer.Visit(a);
printf("%s", serializer.Output().c_str());
input = serializer.Output();
}
{
Person b;
JsonDeserializerVisitor deserializer(input);
deserializer.Visit(b);
{
JsonSerializerVisitor serializer;
serializer.Visit(b);
printf("%s", serializer.Output().c_str());
}
}
/*
{
Property p("hello world");
Property i(100);
Array<Property> vals;
vals.push_back(p);
vals.push_back(i);
Property a(vals);
JsonSerializerVisitor serializer;
serializer.Visit(a);
printf("%s", serializer.Output().c_str());
input = serializer.Output();
}
*/
}