對于核心數據相對比較集中的應用程序來說,serialization機制可以直接充當文檔保存與打開功能的實現工具,這也是很多成熟的應用程序framework都提供serialization支持的原因
但是個人認為,serialization最精彩的用處在于保存現場,比如在探索性的科研應用程序開發過程中,很可能一部分算法已經固定下來,其余的有待進一步探索,而確定下來的部分有可能十分time consuming,如果每次改一下算法都要從頭計算,就會很費時間,長期下去對工作情緒會有很大影響,這時候可以用serialization把每一步的結果存成文件,下次啟動時任選一個開始新的計算。
Boost的Serialization庫是一個十分強大的工具,它文檔中提到的11個開發目標列舉如下:
1.代碼移植性,只依賴于ANSI C++標準
2.代碼簡潔性,的確運用Boost::Serialization所需要的代碼量很小
3.每個類有自己獨立的版本控制,以保證舊的save結果可以被新的程序load回來
4.深度指針save與load,不僅serialize指針本身,而且包括它指向的對象數據
5.多個指針指向同一個對象不會被serialize多次
6.對常用STL容器的支持
7.串行數據的平臺移植性
8.類如何被串行化與串行數據按何種格式存儲相互無關
9.非侵入性,這對于數據中使用了第三方類庫的情形很有效
最后兩個沒有理解,希望高人指點
10.The archive interface must be simple enough to easily permit creation of a new type of archive.
11.The archive interface must be rich enough to permit the creation of an archive that presents serialized data as XML in a useful manner.
下面是我試用Boost::Serialization的記錄
第一個例子
假設有這樣一個類需要串行化
?
class
?Data

{
public
:
????
int
?mInt;
}
;
?
這里為了簡化起見,成員變量都設成public了,串行化的代碼如下
?
?1
#include?
<
fstream
>
?2
?3
//
?fewest?include?headers
?4
#include?
<
boost
/
archive
/
text_iarchive.hpp
>
?5
#include?
<
boost
/
archive
/
text_oarchive.hpp
>
?6
?7
//
?use?this?to?ease?the?archive?selection
?8
typedef?boost::archive::text_iarchive?iarchive;
?9
typedef?boost::archive::text_oarchive?oarchive;
10
11
class
?Data
12
{
13
????friend?
class
?boost::serialization::access;
14
15
protected
:
16
17
????template
<
class
?Archive
>
18
????
void
?serialize(Archive?
&
?ar,?
const
?unsigned?
int
?
/**/
/*
?file_version?
*/
)
19
????
{
20
????????ar?
&
?mInt;
21
????}
22
23
public
:
24
25
????
int
?mInt;
26
27
????
static
?
void
?saveData(
const
?Data
&
?obj,?std::
string
?fileName)
28
????
{
29
????????std::ofstream?ofs(fileName.c_str());
30
????????oarchive?oa(ofs);
31
????????oa?
<<
?obj;
32
????}
33
34
????
static
?
void
?loadData(Data
&
?obj,?std::
string
?fileName)
35
????
{
36
????????std::ifstream?ifs(fileName.c_str());
37
????????iarchive?ia(ifs);
38
????????ia?
>>
?obj;
39
????}
40
}
;
41
42
void
?main()
43
{
44
????Data?d1;
45
????d1.mInt?
=
?
3
;
46
47
????Data::saveData(d1,?
"
output.txt
"
);
48
49
????Data?d2;
50
????Data::loadData(d2,?
"
output.txt
"
);
51
52
????
//
?results?should?be?the?same.
53
????ToolLib::LOG(TOSTR(d2.mInt));
54
}
?
成員serialize函數是定義類如何被串行化之規則的核心。
由于使用了RTTI機制,serialize函數不需要為virtual,永遠只要是void就可以,在串行化指針的時候能夠被正確調用。
serialize函數中的 & 運算符在load時調用 >>,而在save時調用 <<,這樣save和load只要一個函數就可以。
saveData和loadData函數必不可少,由于serialization庫強大的編譯檢查機制,如果不用這樣的方式來save一個對象,往往會因為這個對象不是const而編譯失敗。
所用的archive類型可以任選,如text或binary,上例中的typedef即是為了封裝這一變化
非侵入性
下面一例演示了串行化不可侵入的類型。這里用的是 WildMagicLib2.5中的 2-Vector
?
#include?
<
WildMagic2p5
/
Include
/
WmlVector2.h
>
class
?Data

{
????friend?
class
?boost::serialization::access;

protected
:

????template
<
class
?Archive
>
????
void
?serialize(Archive?
&
?ar,?
const
?unsigned?
int
?
/**/
/*
?file_version?
*/
)

????
{
????????ar?
&
?mVec2d;
????}
public
:

????Wml::Vector2d?mVec2d;

????
static
?
void
?saveData(
const
?Data
&
?obj,?std::
string
?fileName);
????
static
?
void
?loadData(Data
&
?obj,?std::
string
?fileName);
}
;


namespace
?boost?
{?

namespace
?serialization?
{
????template
<
class
?Archive,?
class
?Real
>
????
void
?serialize(Archive
&
?ar,?Wml::Vector2
<
Real
>&
?g,?
const
?unsigned?
int
?version)

????
{
????????ar?
&
?g.X();
????????ar?
&
?g.Y();
????}
}
?
//
?namespace?serialization
}
?
//
?namespace?boost
?
這種情況下,需要這個全局serialize在能夠訪問到那個類里需要串行化的數據,常常load和save的方法不一樣,如load時調用setVar,save時調用getVar,這時Archive::is_loading和Archive::is_saving常數就有用了。一種等價但是更直觀的方法是使用BOOST_SERIALIZATION_SPLIT_MEMBER或者BOOST_SERIALIZATION_SPLIT_FREE宏,兩者分別生成調用load/save成員函數和load/save全局函數的代碼。
在serialization內部,是通過定義全局serialize函數模板,并在里面調用成員serialize函數來實現的,如下所示,需要非侵入的對象只要特化這個全局函數就可以了。
?
//
?default?implemenation?-?call?the?member?function?"serialize"
template
<
class
?Archive,?
class
?T
>
inline?
void
?serialize(
????Archive?
&
?ar,?T?
&
?t,?
const
?BOOST_PFTO?unsigned?
int
?file_version

)
{
????access::serialize(ar,?t,?static_cast
<
unsigned?
int
>
(file_version));
}
?
STL容器支持
通過包含一些serialization提供的頭文件,對STL容器可以像普通變量一樣支持
?
//
?STL?support?headers
#include?
<
boost
/
serialization
/
vector.hpp
>
#include?
<
boost
/
serialization
/
string
.hpp
>
class
?Data

{
????friend?
class
?boost::serialization::access;

protected
:

????template
<
class
?Archive
>
????
void
?serialize(Archive?
&
?ar,?
const
?unsigned?
int
?
/**/
/*
?file_version?
*/
)

????
{
????????ar?
&
?mStr;
????????ar?
&
?mVecInt;
????????ar?
&
?mVecStr;
????}
public
:

????std::
string
?mStr;
????std::vector
<
int
>
?mVecInt;
????std::vector
<
std::
string
>
?mVecStr;

????
static
?
void
?saveData(
const
?Data
&
?obj,?std::
string
?fileName);
????
static
?
void
?loadData(Data
&
?obj,?std::
string
?fileName);
}
;
?
指針與數組
?
class
?ClassA

{
public
:
????
int
?mInt;
}
;

class
?Data

{
????friend?
class
?boost::serialization::access;

protected
:

????template
<
class
?Archive
>
????
void
?serialize(Archive?
&
?ar,?
const
?unsigned?
int
?
/**/
/*
?file_version?
*/
)

????
{
????????ar?
&
?mPtrInt;
????????ar?
&
?mArrInt;
????????ar?
&
?mPtrData;
????????ar?
&
?mPtrA;
????}
public
:


????Data():mPtrData(NULL),?mPtrInt(NULL),?mPtrA(NULL)
{}
????Data
*
?mPtrData;
????
int
?mArrInt[
10
];
????
int
*
?mPtrInt;
????ClassA
*
?mPtrA;

????
static
?
void
?saveData(
const
?Data
&
?obj,?std::
string
?fileName);
????
static
?
void
?loadData(Data
&
?obj,?std::
string
?fileName);
}
;


namespace
?boost?
{?

namespace
?serialization?
{
????template
<
class
?Archive
>
????
void
?serialize(Archive
&
?ar,?
int
&
?g,?
const
?unsigned?
int
?version)

????
{
????????ar?
&
?g;
????}
}
?
//
?namespace?serialization
}
?
//
?namespace?boost
?
數組可以直接串行化,指針比須保證有效,所以必須保證在串行化之前經過初始化。
對于基本類型如int,可以直接串行化,但其指針int*,要當作不可侵入類型的指針來看待,所以需要一個全局serialize函數來說明int類型的串行化方式
對于有基類指針的串行化,代碼如下
?
class
?ClassA

{
public
:
????
int
?mIntA;

????
virtual
?
void
?someMethod()?
=
?NULL;
????
????template
<
class
?Archive
>
????
void
?serialize(Archive?
&
?ar,?
const
?unsigned?
int
?
/**/
/*
?file_version?
*/
)

????
{
????????ar?
&
?mIntB;
????}
}
;

BOOST_IS_ABSTRACT(ClassA)

class
?ClassB:?
public
?ClassA

{
public
:
????
int
?mIntB;
????

????
virtual
?
void
?someMethod()
{}
????template
<
class
?Archive
>
????
void
?serialize(Archive?
&
?ar,?
const
?unsigned?
int
?
/**/
/*
?file_version?
*/
)

????
{
????????ar?
&
?boost::serialization::base_object
<
ClassA
>
(
*
this
);
????????ar?
&
?mIntB;
????}
}
;

BOOST_CLASS_EXPORT(ClassB)
?
純虛類后加上BOOST_ISABSTRACT,而可能會被串行化到的子類用BOOST_CLASS_EXPORT,這樣就可以在任何地方串行化 ClassA* 的成員變量。
子類的serialize函數里必須要照顧到基類的成員。
版本控制
在serialize函數中的version參數就是用于版本控制的,所有類的版本號默認為0,新版本的類可以自己指定版本號以便與舊版本相區別。如下
BOOST_CLASS_VERSION(ClassA, 1)
對于save過程,版本號始終為新的,而load過程取決于文件中保存的值,對于新版本新增變量的情況可以這樣解決
?
//
?old?definition
class
?ClassA

{
public
:
????
int
?mInt;
}
;

//
?new?definition
class
?ClassA

{
public
:
????
int
?mInt;
????
int
?mIntNew;

????template
<
class
?Archive
>
????
void
?serialize(Archive?
&
?ar,?
const
?unsigned?
int
?ver)

????
{
????????ar?
&
?mInt;

????????
if
(ver?
==
?
1
)
????????????ar?
&
?mIntB;
????}
}
;

BOOST_CLASS_VERSION(ClassA,?
1
)
?
對于有改動的情形,稍微復雜一點,可以這樣
?
//
?old?definition
class
?ClassA

{
public
:
????TypeA?mVarA;
}
;

//
?new?definition
class
?ClassA

{
public
:

????
//
?suppose?now?we?use?B?and?C?instead?of?A
????TypeB?mVarB;
????TypeC?mVarC;

????template
<
class
?Archive
>
????
void
?serialize(Archive?
&
?ar,?
const
?unsigned?
int
?ver)

????
{
????????
if
(ver?
<
?
1
)

????????
{
????????????
//
?here?must?be?loading
????????????TypeA?varA;
????????????ar?
&
?varA;
????????????
????????????
//
?now?derive?mVarB?&?mVarC?from?varA;
????????}
????????
else
????????
{
????????????ar?
&
?mVarB;
????????????ar?
&
?mVarC;
????????}
????}
}
;

BOOST_CLASS_VERSION(ClassA,?
1
)
?