共享Windows下C++库之序列化组件

简介:

什么是序列化?简单来说,序列化就是将对象实例的状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它根据流重构对象。这两个过程结合起来,可以轻松地存储和传输数据。

对象序列化反序列化通常用于:

1. 将对象存储于硬盘上

2. 在网络上传送对象的字节序列

这是一个简易的序列化组件,代码不足500行,支持对内置类型、用户自定义类型、STL容器。

这里是介绍序列化的Wiki。

这里是boost的序列化库。

这里是Protobuf,强烈建议没有学习过的同学研究下。

下载地址:

CSDN资源

特点:

    1. 接口简单,易于集成,方便维护。
    2. 基于Policy-based设计模式
    3. 采用C++流式方式方便易用
    4. 扩展性强(针对用户自定义类型,输出目标)
    5. 编译期对用户自定义类型侦错

目标:

    1. 客户端代码接口简洁,使用户代码短小便捷(满足)
    2. 指针的深度存储与恢复,保存指针所指内存或恢复数据到指针所指向区域,且能正确处理多个指针(满足)
    3. 对STL容器的支持,能正确处理嵌套容器(满足)
    4. 支持多种存储方式(目前仅支持文件与内存)
    5. 非入侵式实现,不需要从某个特定的类派生或实现特定的成员函数(需要友元函数或者另一个名字空间的自由函数)

内部原理:

未命名

Tutorial:

1. 基础数据测试,char类型数据内存缓冲区,写入任意类值类型数据,再重新冲读取,判断值是否一致

 

void TestBasic()
{
	char buf[1024] = {0};
	utility::serialize::Serialize os(buf);

	int b1 = 1;
	long b2 = 1L;
	long long b3 = 1LL;
	double b4 = 1.111;
	char b5 = 'a';
	char b6[] = "bc";
	wchar_t b7 = L'c';
	wchar_t b8[] = L"Yu";
	os << b1;
	os << b2;
	os << b3;
	os << b4;
	os << b5;
	os << b6;
	os << b7;
	os << b8;

	std::string b9("测试1");
	std::wstring b10(L"测试2");
	os << b9;
	os << b10;

	char *b11 = "asd";
	wchar_t *b12 = L"bbb";
	os << b11 << b12;

	utility::serialize::Serialize in(buf);
	int a1 = 0;
	long a2 = 0L;
	long long a3 = 0LL;
	double a4 = 0.0;
	char a5 = 0;
	char a6[1024] = {0};
	wchar_t a7 = L'';
	wchar_t a8[1024] = {0};

	std::string a9;
	std::wstring a10;

	in >> a1;
	in >> a2;
	in >> a3;
	in >> a4;
	in >> a5;
	in >> a6;
	in >> a7;
	in >> a8;
	in >> a9;
	in >> a10;

	char tmp1[4] = {0};
	wchar_t tmp2[4] = {0};
	char *a11 = tmp1;
	wchar_t *a12 = tmp2;
	in >> a11 >> a12;


	assert(b1 == a1);
	assert(b2 == a2);
	assert(b3 == a3);
	assert(b4 == a4);
	assert(b5 == a5);
	assert(strcmp(b6, a6) == 0);
	assert(b7 == a7);
	assert(wcscmp(b8, a8) == 0);
	assert(b9 == a9);
	assert(b10 == a10);
	assert(strcmp(b11, a11) == 0);
	assert(wcscmp(b12, a12) == 0);
}


2. 基础数据测试,wchar_t类型数据内存缓冲区,测试目的与测试样例1相同

 

void TestUnicode()
{
	wchar_t buf[1024] = {0};
	utility::serialize::SerializeW os(buf);

	int b1 = 1;
	long b2 = 1L;
	long long b3 = 1LL;
	double b4 = 1.111;
	char b5 = 'a';
	char b6[] = "bc";
	wchar_t b7 = L'c';
	wchar_t b8[] = L"Yu";
	os << b1;
	os << b2;
	os << b3;
	os << b4;
	os << b5;
	os << b6;
	os << b7;
	os << b8;

	std::string b9("测试1");
	std::wstring b10(L"测试2");
	os << b9;
	os << b10;

	char *b11 = "asd";
	wchar_t *b12 = L"bbb";
	os << b11 << b12;

	utility::serialize::SerializeW in(buf);
	int a1 = 0;
	long a2 = 0L;
	long long a3 = 0LL;
	double a4 = 0.0;
	char a5 = 0;
	char a6[1024] = {0};
	wchar_t a7 = L'';
	wchar_t a8[1024] = {0};

	std::string a9;
	std::wstring a10;

	in >> a1;
	in >> a2;
	in >> a3;
	in >> a4;
	in >> a5;
	in >> a6;
	in >> a7;
	in >> a8;
	in >> a9;
	in >> a10;

	char tmp1[4] = {0};
	wchar_t tmp2[4] = {0};
	char *a11 = tmp1;
	wchar_t *a12 = tmp2;
	in >> a11 >> a12;


	assert(b1 == a1);
	assert(b2 == a2);
	assert(b3 == a3);
	assert(b4 == a4);
	assert(b5 == a5);
	assert(strcmp(b6, a6) == 0);
	assert(b7 == a7);
	assert(wcscmp(b8, a8) == 0);
	assert(b9 == a9);
	assert(b10 == a10);
	assert(strcmp(b11, a11) == 0);
	assert(wcscmp(b12, a12) == 0);
}


3. 数组类型数据测试,针对数组提供单独测试用例

 

void TestArray()
{
	char buf[1024] = {0};
	utility::serialize::Serialize os(buf);

	char a1[] = "1234";
	unsigned char a2[] = "QWER";
	short a3[] = {1, 2, 3, 4};
	int a4[] = {5, 6, 7, 8};
	long a5[] = {10, 10, 10, 10};
	unsigned long long a6[] = {100LL, 100LL, 100LL};

	os << a1;
	os << a2;
	os << a3 << a4 << a5 << a6;

	char b1[5] = {0};
	unsigned char b2[5] = {0};
	short b3[4] = {0};
	int b4[4] = {0};
	long b5[4] = {0};
	unsigned long long b6[3] = {0};
	os >> b1 >> b2 >> b3 >> b4 >> b5 >> b6;

	assert(strcmp(b1, a1) == 0);
	assert(memcmp(b2, a2, _countof(a2)) == 0);
	assert(memcmp(b3, a3, _countof(a3)) == 0);
	assert(memcmp(b4, a4, _countof(a4)) == 0);
	assert(memcmp(b5, a5, _countof(a5)) == 0);
	assert(memcmp(b6, a6, _countof(a6)) == 0);
}


4.自定义数据类型测试,如果自定义数据(非POD类型数据)没有提供operator<<或operator>>,则由编译期错误

 

struct CustomType
{
	int age_;
	TCHAR class_[64];
	std::wstring name_;

	CustomType()
		: age_(0)
	{
		std::fill(class_, class_ + _countof(class_), 0);
	}
	CustomType(int age, LPCTSTR cla, LPCTSTR name)
		: age_(age)
		, name_(name)
	{
		_tcscpy(class_, cla);
	}
};

bool operator==(const CustomType &lhs, const CustomType &rhs)
{
	return lhs.age_ == rhs.age_ && 
		_tcscmp(lhs.class_, rhs.class_) == 0 &&
		lhs.name_ == rhs.name_;
}

utility::serialize::Serialize &operator<<(utility::serialize::Serialize &os, const CustomType &type)
{
	os << type.age_ << type.class_ << type.name_;
	return os;
}

utility::serialize::Serialize &operator>>(utility::serialize::Serialize &os, CustomType &type)
{
	os >> type.age_ >> type.class_ >> type.name_;
	return os;
}


void TestClass()
{
	char buf[1024];
	CustomType customType(10, _T("一年级一班"), _T("王小五"));
	utility::serialize::Serialize os(buf);
	os << customType;

	CustomType dst1, dst2;
	os >> dst1;

	os << &customType;
	os >> dst2;

	CustomType arr[2] = 
	{
		CustomType(10, _T("一年级一班"), _T("王小五")),
		CustomType(11, _T("2年级2班"), _T("王小2"))
	};
	os << arr;

	CustomType brr[2];
	os >> brr;

	assert(dst1 == customType); 
	assert(dst2 == customType);
	for(size_t i = 0; i != _countof(arr); ++i)
	{
		assert(arr[i] == brr[i]);
	}
};


5. STL容器测试,支持嵌套容器的序列化

void TestContainer()
{
	char buf[1024] = {0};
	utility::serialize::Serialize os(buf);

	std::vector<int> a1(10);
	std::fill(a1.begin(), a1.end(), 10);
	os << a1;
	std::vector<int> b1;
	os >> b1;
	assert(std::equal(a1.begin(), a1.end(), b1.begin()));

	std::list<std::string> a2;
	a2.push_back("chenyu");
	a2.push_back("test");
	os << a2;
	std::list<std::string> b2;
	os >> b2;
	assert(std::equal(a2.begin(), a2.begin(), b2.begin()));

	std::deque<std::wstring> a3;
	a3.push_back(L"test1");
	a3.push_back(L"test2");
	os << a3;
	std::deque<std::wstring> b3;
	os >> b3;
	assert(std::equal(a3.begin(), a3.begin(), b3.begin()));

	std::set<long> a4;
	a4.insert(1L);
	a4.insert(10L);
	a4.insert(100L);
	os << a4;
	std::set<long> b4;
	os >> b4;
	assert(std::equal(a4.begin(), a4.begin(), b4.begin()));

	std::map<std::string, long> a5;
	a5.insert(std::make_pair("123", 1L));
	a5.insert(std::make_pair("1234", 10L));
	a5.insert(std::make_pair("12345", 100L));
	os << a5;
	std::map<std::string, long> b5;
	os >> b5;
	assert(std::equal(a5.begin(), a5.begin(), b5.begin()));


	std::multiset<long> a6;
	a6.insert(1L);
	a6.insert(10L);
	a6.insert(10L);
	a6.insert(10L);
	a6.insert(100L);
	a6.insert(100L);
	os << a6;
	std::set<long> b6;
	os >> b6;
	assert(std::equal(a6.begin(), a6.begin(), b6.begin()));


	std::multimap<std::string, long> a7;
	a7.insert(std::make_pair("123", 1L));
	a7.insert(std::make_pair("123", 1L));
	a7.insert(std::make_pair("1234", 10L));
	a7.insert(std::make_pair("1234", 100L));
	a7.insert(std::make_pair("1234", 1000L));
	a7.insert(std::make_pair("12345", 100L));
	os << a7;
	std::multimap<std::string, long> b7;
	os >> b7;
	assert(std::equal(a7.begin(), a7.begin(), b7.begin()));

	typedef std::map<std::pair<std::string, long>, std::vector<std::wstring>> VecMap;
	VecMap a8;
	std::vector<std::wstring> vec;
	vec.push_back(L"Test");
	vec.push_back(L"asd");
	a8.insert(std::make_pair(std::make_pair("10", 10), vec));
	a8.insert(std::make_pair(std::make_pair("11", 101), vec));
	a8.insert(std::make_pair(std::make_pair("11", 1011), vec));
	os << a8;
	VecMap b8;
	os >> b8;
	assert(std::equal(a8.begin(), a8.begin(), b8.begin()));

}

 

6. 基础数据测试,以File为缓冲区,类似测试样例1

void TestFile()
{
	utility::serialize::FileSerialize file("test.txt");
	int a1 = 10;
	long a2 = 10L;
	char a3 = 'c';
	char a4[] = "chenyu";
	wchar_t a5[] = L"chenyu";

	file << a1 << a2 << a3 << a4 << a5;

	int b1 = 0;
	long b2 = 0L;
	char b3 = 0;
	char b4[1024] = {0};
	wchar_t b5[1024] = {0};

	file >> b1 >> b2 >> b3 >> b4 >> b5;

	assert(a1 == b1);
	assert(a2 == b2);
	assert(a3 == b3);
	assert(strcmp(a4, b4) == 0);
	assert(wcscmp(a5, b5) == 0);
}


 

 

未来的版本变化:

  1. 缺乏多线程间同步支持,这个也是理念导致。窃以为,在序列化中,同步应该是用户层的事情,即需要用户指定是否需要同步。当然,也可以遵照policy-based的设计方式,提供模板参数,以达到编译器选择是否需要同步。
  2. 仅支持对象(包括内置类型)的序列化,不支持远程过程调用。以后也不考虑(复杂)
  3. 借鉴Protobuf与boost serialize的优势,本着简单易用小巧的原则扩展。

下载地址:

CSDN资源

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭