C++ 九陰真經之序列化與反序列化

C++中將對象進行序列化的經常使用方法有三種:protobuf、 Boost.Serializatio、MFC Serialization。編程

根據使用場景,各有優點網絡

  • protobuf :須要單獨定義格式,通常用於網絡通訊,用於數據包的傳輸。
  • Boost.Serializatio : 可以建立或重建程序中的等效結構,並保存爲二進制數據、文本數據、XML或者實用戶本身定義的其它文件
  • MFC Serialization:  MFC 對 CObject 類中的序列化提供內置支持

    對於如下小型項目,若是須要保持程序中的等效結構,但又不想引入boost庫的話,其實也能夠自行實現,基於二進制的序列化與反序列化並不難,下面將詳細介紹實現過程,支持C++基礎類類型及stl容器類型。測試

    測試效果:spa

void print(const std::string& msg)
{
    //反序列化
    std::istringstream iss(msg);
    std::vector<std::string> d;
    Deserialize(iss, d);

    for(auto item : d)
    {
        std::cout << item << std::endl;
    }
    int val;
    Deserialize(iss, val);
    std::cout << "-->" << val << std::endl;

	std::string str;
	Deserialize(iss, str);
	std::cout << "-->" << str << std::endl;

	std::list<int> s;
	Deserialize(iss, s);
	for (auto item : s)
	{
		std::cout << item << std::endl;
	}

	std::map<std::string, int> m;
	Deserialize(iss, m);
	for (auto item : m)
	{
		std::cout << item.first << "-->" << item.second << std::endl;
	}

	std::tuple<int, std::string, double> tp;
	Deserialize(iss, tp);
	std::cout << std::get<0>(tp) << "-->" << std::get<1>(tp) << "-->" << std::get<2>(tp) << std::endl;

	std::set<int> st;
	Deserialize(iss, st);
	for (auto item : st)
	{
		std::cout << item << std::endl;
	}
    
    std::vector<MyTest> vecVal;
    Deserialize(iss, vecVal);

    for(auto item : vecVal)
    {
        std::cout << "age:" << *item.p << "  name:" << item.name << std::endl;
    }
}


int main()
{
    //序列化
	std::vector<std::string> d = { "10","20","50","100","1000" };
    std::ostringstream oss;
    Serialize(oss, d);

    Serialize(oss, 10);

	std::string str("hello");
	Serialize(oss, str);


	std::list<int> s = { 10,20,50,100,1000 }; 
	Serialize(oss, s);

	std::map<std::string, int> val = { { "1",3 }, { "3",4 }, {"6",8} };
	Serialize(oss, val);


	std::tuple<int, std::string, double> tp{ 0, "test", 1.3 };
	Serialize(oss, tp);

    std::vector<MyTest> vecVal = {MyTest(35, "hello"), MyTest(40, "world")}; 

	std::set<int> st = { 100,30,70 };
	Serialize(oss, st);

    Serialize(oss, vecVal);

    print(oss.str());
    return 0;
}

    輸出:code

實現過程對象

利用C++模板能夠大大簡化處理過程,第一步咱們須要對C++經常使用類型進行分類,而後結合C++泛型編程分步實現。blog

    經常使用類型分類:內存

    一、可平凡複製類型 (C++ POD概念,支持按位複製的類型)get

    二、std::string  最經常使用類型,雖然string是能夠自動分配內存的,但能夠針對連續內存的特性進行特化處理。string

    三、容器類型,須要支持容器嵌套處理

    四、std::pair類型,多值結構須要特殊處理

    五、std::tuple 元組不一樣於其它C++容器,須要特殊處理。

如下位序列化代碼:

//可平凡複製 
template <typename T, typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type N = 0>
void Serialize(std::ostream & os, const T & val) 
{ 
    os.write((const char *)&val,sizeof(T)); 
} 

//pair
template <typename K, typename V>
void Serialize(std::ostream & os, const std::pair<K, V> & val)
{
	Serialize(os, val.first);
	Serialize(os, val.second);
}

// std::string
void Serialize(std::ostream & os, const std::string &val)
{
	size_t size = val.size();
	os.write((const char *)&size, sizeof(size));
	os.write((const char *)val.data(), size * sizeof(typename std::string::value_type));
}

//容器 
template <typename T, typename std::enable_if< std::is_same<typename std::iterator_traits<typename T::iterator>::value_type, typename T::value_type>::value
    , int>::type N = 0> 
void Serialize(std::ostream & os, const T & val) 
{
	size_t size = val.size();
    os.write((const char *)&size, sizeof(size_t));
    for (auto & v : val) 
    { 
        Serialize(os, v); 
    } 
}


//tuple
template<typename T>
int _Serialize(std::ostream & os, T& val)
{
	Serialize(os, val);
	return 0;
}

template<typename Tuple, std::size_t... I>
void _Serialize(std::ostream & os, Tuple& tup, std::index_sequence<I...>)
{
	std::initializer_list<int>{_Serialize(os, std::get<I>(tup))...};
}


template <typename...Args>
void Serialize(std::ostream & os, const std::tuple<Args...>& val)
{
	_Serialize(os, val, std::make_index_sequence<sizeof...(Args)>{});
}

反序列化

// 可平凡複製 
template <typename T,typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type N = 0>
void Deserialize(std::istream & is, T & val)
 { 
     is.read((char *)&val, sizeof(T)); 
}

// std::string
void Deserialize(std::istream & is, std::string &val)
{
	size_t size = 0; 
	is.read((char*)&size, sizeof(size_t));
	val.resize(size);
	auto count = size * sizeof(typename std::string::value_type);
	is.read((char *)val.data(), count);
}


//pair
template <typename V>
struct ImpDeserialize
{
	template<typename T>
	static void get(std::istream & is, T & val)
	{
		size_t size = 0;
		is.read((char *)&size, sizeof(size_t));
		for (size_t i = 0; i < size; i++)
		{
			V v;
			Deserialize(is, v);
			val.insert(val.end(), v);
		}
	}
};

template <typename K, typename V>
struct ImpDeserialize<std::pair<K, V>>
{
	template<typename T>
	static void get(std::istream & is, T & val)
	{
		size_t size = 0;
		is.read((char *)&size, sizeof(size_t));
		for (size_t i = 0; i < size; i++)
		{
			K k;
			V v;
			Deserialize(is, k);
			
			Deserialize(is, v);
			val.emplace(k, v);
		}
		
	}
};

// 容器 
template <typename T, typename std::enable_if< std::is_same<typename std::iterator_traits<typename T::iterator>::value_type, typename T::value_type>::value
	, int>::type N = 0>
void Deserialize(std::istream & is, T & val) 
{

	ImpDeserialize<typename  T::value_type>::get(is, val);
}


//tuple
template<typename T>
int _Deserialize(std::istream & is, T& val)
{
	Deserialize(is, val);
	return 0;
}

template<typename Tuple, std::size_t... I>
void _Deserialize(std::istream & is, Tuple& tup, std::index_sequence<I...>)
{
	std::initializer_list<int>{_Deserialize(is, std::get<I>(tup))...};
}


template <typename...Args>
void Deserialize(std::istream & is, std::tuple<Args...>& val)
{
	_Deserialize(is, val, std::make_index_sequence<sizeof...(Args)>{});
}
相關文章
相關標籤/搜索