C++98序列化库,C++类与JSON、XML、Google protobuf等快速超高效互转库。快速、简洁的 API 设计,大大降低直接使用JSON、XML、Google protobuf解析库来实现此类功能的代码复杂度。也没有外部依赖。
注: 该库旨在以C++ Class为核心,和json、xml、protobuf等协议的互转。
支持结构体类型:
- 支持
bool、uint32_t、int32_t、uint64_t、int64_t、float、double、enum、std::string、std::vector和std::map等。(不支持std::list) std::map的key只能是基础类型,且不能是float、double。- 不支持指针和固定数组。(以后也不考虑支持指针和数组)
- 数组内需要统一类型,不支持
[1,2,{"key":"value"}]。 - 解析时支持has功能。
-------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------
Encode 42850 ns 42812 ns 15775
Decode 13109 ns 13085 ns 56355
cJSON Encode 130396 ns 130237 ns 5382
cJSON Decode 78503 ns 78451 ns 8670
$ git clone https://github.com/yksten/serialflex.git
$ cd serialflex
$ mkdir -p cmake/build
$ pushd cmake/build
$ cmake -DEXAMPLE=ON ../..
$ make -j
$ popd
/* ------头文件 h、hpp------ */classData {
int32_t a;
bool has_a;
uint32_t b;
bool has_b;
float c;
bool has_c;
double d;
bool has_d;
StructItem e; // StructItem里需要实现serialize模板函数bool has_e;
public:Data() : a(0), has_a(false), b(0), has_b(false), c(0.0f), has_c(false), d(0.0f), has_d(false),, has_e(false) {}
voidset_a(constint32_t& value) { a = value; has_a = true; }
constint32_t& get_a() const { return a; }
voidset_b(constuint32_t& value) { b = value; has_b = true; }
constint32_t& get_b() const { return b; }
voidset_c(constfloat& value) { c = value; has_c = true; }
constint32_t& get_c() const { return c; }
voidset_d(constdouble& value) { d = value; has_d = true; }
constint32_t& get_d() const { return d; }
voidset_e(const StructItem& value) { e = value; has_e = true; }
constint32_t& get_e() const { return e; }
template <typename Archive>
voidserialize(Archive& ar) {
ar.convert("a", a, &has_a).convert("b", b, &has_b).convert("c", c, &has_c);
ar.convert("d", d, &has_d).convert("e", e, , &has_e);
}
};- SERIALIZE宏暂时没有has功能。
/* ------头文件 h、hpp------ */
#include<serialflex/serialflex.h>classData {
int32_t a;
bool has_a;
uint32_t b;
bool has_b;
float c;
bool has_c;
double d;
bool has_d;
StructItem e; // StructItem里需要实现serialize模板函数bool has_e;
public:Data() : a(0), has_a(false), b(0), has_b(false), c(0.0f), has_c(false), d(0.0f), has_d(false),, has_e(false) {}
voidset_a(constint32_t& value) { a = value; has_a = true; }
constint32_t& get_a() const { return a; }
voidset_b(constuint32_t& value) { b = value; has_b = true; }
constint32_t& get_b() const { return b; }
voidset_c(constfloat& value) { c = value; has_c = true; }
constint32_t& get_c() const { return c; }
voidset_d(constdouble& value) { d = value; has_d = true; }
constint32_t& get_d() const { return d; }
voidset_e(const StructItem& value) { e = value; has_e = true; }
constint32_t& get_e() const { return e; }
template <typename Archive>
voidserialize(Archive& ar) {
// SERIALIZE参数限制63个SERIALIZE(ar, a, b, c, d, e);
// SERIALIZE(ar, f); /* 超过63个参数后建议换行后继续使用SERIALIZE */
}
};/* ------源文件 cpp、cc------ */// 编码
Data data; /* 假设data已赋值 */
std::string json;
bool result = serialflex::JSONEncoder(json) << data;
// 解码
Data data;
std::string json; /* 假设json已被赋值 */bool result = serialflex::JSONDecoder(json.c_str()) >> data;原理:C++模板 SFINAE
适用范围:非侵入式serialize函数,仅在极端情况下使用,如外部的结构体。
非侵入式serialize函数声明定义在cpp/cc文件里时,那么也仅在当前cpp/cc文件里有效。部分情况需要考虑命名空间问题,实现困难。
/* ------源文件 cpp、cc------ */// 非侵入式序列化,和侵入式序列化同时存在时,会优先有这里template <typename Archive>
voidserialize(Archive& ar, Data& data) {
ar.convert("a", data.a).convert("b", data.b).convert("c", data.c);
ar.convert("d", data.d).convert("e", data.e);
// 如果包含了 #include <serialflex/serialflex.h> 头文件可以这样实现serialize函数:// NISERIALIZE(ar, data, a, b, c, d, e); /* NISERIALIZE也是限制63个参数 */
}
// 编码
Data data; /* 假设data已赋值 */
std::string json;
bool result = serialflex::JSONEncoder(json) << data;
// 解码
Data data;
std::string json; /* 假设json已被赋值 */bool result = serialflex::JSONDecoder(json.c_str()) >> data;- 缺点:新增成员变量需要在两个函数实现,容易遗漏。
/* ------头文件 h、hpp------ */
#include<serialflex/json/encoder.h>
#include<serialflex/json/decoder.h>classData {
public:int32_t a;
uint32_t b;
float c;
double d;
StructItem e;
// #include <serialflex/json/encoder.h>voidserialize(serialflex::JSONEncoder& ar) {
if (/*老版本*/) {
a = 666;
}
ar.convert("a", a).convert("b", b).convert("c", c);
ar.convert("d", d).convert("e", e);
}
// #include <serialflex/json/decoder.h>voidserialize(serialflex::JSONDecoder& ar) {
ar.convert("a", a).convert("b", b).convert("c", c);
ar.convert("d", d).convert("e", e);
}
};- 为了达到最小依赖、降低产物大小,源码里未实现这些功能:
#include<serialflex/json/encoder.h>
#include<serialflex/json/decoder.h>template <typename Derived>
classConvertWrapper {
public:explicitConvertWrapper(Derived& d) : _d(d) {
}
inlinebooloperator>>(std::string& json) {
returnserialflex::JSONEncoder().operator<<(*((Derived*)this)).toString(json);
}
inlinebooloperator<<(const std::string& json) {
returnserialflex::JSONDecoder(json.c_str()) >> *((Derived*)this);
} };// 编码
Data data; /* 假设data已赋值 */
std::string json;
bool result = ConvertWrapper(data) >> json;
// 解码
Data data;
std::string json; /* 假设json已被赋值 */bool result = ConvertWrapper(data) << json;/* ------可使用宏代替------ */
#defineENCODE_TO_JSON(data, json) serialflex::JSONEncoder().operator<<(data).toString(json);
#defineDECODE_FROM_JSON(data, json) serialflex::JSONDecoder(json.c_str()) >> data;原理:模板类型萃取。
缺点:不能判断json里的值是否在枚举范围内。
为什么不使用std::is_enum?
- std::is_enum是C++11提供的,serialflex实现之初是为了支持C++98语法。
template <classT> structisIntegral { staticconstbool value = false; };
template <> structisIntegral<bool> { staticconstbool value = true; };
template <> structisIntegral<int8_t> { staticconstbool value = true; };
template <> structisIntegral<uint8_t> { staticconstbool value = true; };
template <> structisIntegral<int16_t> { staticconstbool value = true; };
template <> structisIntegral<uint16_t> { staticconstbool value = true; };
template <> structisIntegral<int32_t> { staticconstbool value = true; };
template <> structisIntegral<uint32_t> { staticconstbool value = true; };
template <> structisIntegral<int64_t> { staticconstbool value = true; };
template <> structisIntegral<uint64_t> { staticconstbool value = true; };
template <> structisIntegral<float> { staticconstbool value = true; };
template <> structisIntegral<double> { staticconstbool value = true; };
template <typename From, typename To> structis_convertible { staticconstbool value = sizeof(ConvertHelper::Test<To>(ConvertHelper::Create<From>())) == sizeof(ConvertHelper::YesType); };
// 判断类型是否是枚举enumtemplate <classT> structis_enum { staticconstbool value = is_convertible<T, int>::value && !isIntegral<T>::value; };
// 是枚举enum的情况下萃取类型为inttemplate <typename T, bool flag = is_enum<T>::value> structUnwrapTraits { typedef T Type; };
template <typename T> structUnwrapTraits<T, true> { typedefint32_t Type; };/* ---编码---*/template <typename T>
JSONEncoder& convert(constchar* name, const T& value, bool* has_value = NULL) {
setValue(name, *(typename internal::UnwrapTraits<T>::Type*)(&value));
return *this;
}
/* ---解码---*/template <typename T>
JSONDecoder& convert(constchar* name, T& value, bool* has_value = NULL) {
getValue(name, *(typename internal::UnwrapTraits<T>::Type*)(&value), has_value);
return *this;
}- json字符串:
{
"a": 1,
"b": 1.5,
"c": {
"d": [
1,
2,
3
]
}
}- 生成的c++源码:
// Generated according to JSON, for reference only!
#include<stdint.h>
#include<vector>classCXX_C {
std::vector<int32_t> d;
bool has_d;
public:CXX_C() {}
voidset_d(const std::vector<int32_t>& value) { d = value; has_d = true; }
const std::vector<int32_t>& get_d() const { return d; }
template<typename Archive>
voidserialize(Archive& ar) {
ar.convert("d", d, &has_d);
}
};
classCXX_Result {
int32_t a;
bool has_a;
float b;
bool has_b;
CXX_C c;
bool has_c;
public:CXX_Result() : a(0), b(0) {}
voidset_a(constint32_t& value) { a = value; has_a = true; }
constint32_t& get_a() const { return a; }
voidset_b(constfloat& value) { b = value; has_b = true; }
constfloat& get_b() const { return b; }
voidset_c(constCXX_C& value) { c = value; has_c = true; }
constCXX_C& get_c() const { return c; }
template<typename Archive>
voidserialize(Archive& ar) {
ar.convert("a", a, &has_a).convert("b", b, &has_b).convert("c", c, &has_c);
}
};