C++ 对象序列化

背景

最近做的项目需要对C++对象进行序列化和反序列化,最主要的目的是将JSON和C++对象互转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <iostream>
#include <tuple>
#include <map>

// sequence for
template <typename T, T... S, typename F>
constexpr void for_sequence(std::integer_sequence<T, S...>, F&& f) {
using unpack_t = int[];
(void)unpack_t{(static_cast<void>(f(std::integral_constant<T, S>{})), 0)..., 0};
}

// Sample implementation of a json-like data structure. It is only there for the example to compile and actually produce a testable output
namespace Json {
struct Value;

struct ValueData {
std::map<std::string, Value> subObject;
std::string string;
int number = 0;
};

struct Value {
ValueData data;

Value& operator[](std::string name) {
return data.subObject[std::move(name)];
}

const Value& operator[](std::string name) const {
auto it = data.subObject.find(std::move(name));

if (it != data.subObject.end()) {
return it->second;
}

throw;
}

Value& operator=(std::string value) {
data.string = value;
return *this;
}

Value& operator=(double value) {
data.number = value;
return *this;
}
};

template<typename T> T& asAny(Value&);
template<typename T> const T& asAny(const Value&);

template<>
int& asAny<int>(Value& value) {
return value.data.number;
}

template<>
const int& asAny<int>(const Value& value) {
return value.data.number;
}

template<>
const std::string& asAny<std::string>(const Value& value) {
return value.data.string;
}

template<>
std::string& asAny<std::string>(Value& value) {
return value.data.string;
}
}

template<typename Class, typename T>
struct PropertyImpl {
constexpr PropertyImpl(T Class::*aMember, const char* aName) : member{aMember}, name{aName} {}
using Type = T;
T Class::*member;
const char* name;
};

// One could overload this function to accept both a getter and a setter instead of a member.
template<typename Class, typename T>
constexpr auto property(T Class::*member, const char* name) {
return PropertyImpl<Class, T>{member, name};
}


// unserialize function
template<typename T>
T fromJson(const Json::Value& data) {
T object;

// We first get the number of properties
constexpr auto nbProperties = std::tuple_size<decltype(T::properties)>::value;

// We iterate on the index sequence of size `nbProperties`
for_sequence(std::make_index_sequence<nbProperties>{}, [&](auto i){
// get the property
constexpr auto property = std::get<i>(T::properties);

// get the type of the property
using Type = typename decltype(property)::Type;

// set the value to the member
object.*(property.member) = Json::asAny<Type>(data[property.name]);
});

return object;
}

template<typename T>
Json::Value toJson(const T& object) {
Json::Value data;

// We first get the number of properties
constexpr auto nbProperties = std::tuple_size<decltype(T::properties)>::value;

// We iterate on the index sequence of size `nbProperties`
for_sequence(std::make_index_sequence<nbProperties>{}, [&](auto i){
// get the property
constexpr auto property = std::get<i>(T::properties);

// set the value to the member
data[property.name] = object.*(property.member);
});

return data;
}

namespace animal {

struct Dog {
std::string barkType;
std::string color;
int weight = 0;

bool operator==(const Dog& rhs) const {
return barkType == rhs.barkType && color == rhs.color && weight == rhs.weight;
}

constexpr static auto properties = std::make_tuple(property(&Dog::barkType, "barkType"), property(&Dog::color, "color"), property(&Dog::weight, "weight"));
};

}

int main() {
animal::Dog dog;

dog.color = "green";
dog.barkType = "whaf";
dog.weight = 30;

Json::Value jsonDog = toJson(dog); // produces {"color":"green", "barkType":"whaf", "weight": 30}
auto dog2 = fromJson<animal::Dog>(jsonDog);

std::cout << std::boolalpha << (dog == dog2) << __FILE_NAME__ << std::endl; // pass the test, both dog are equal!

return 0;
}
作者

shouyi.www

发布于

2019-11-22

更新于

2025-01-30

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×