serialization.html
39.4 KB
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<!--
(C) Copyright 2002-4 Robert Ramey - http://www.rrsd.com .
Use, modification and distribution is subject to the Boost Software
License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
-->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="../../../boost.css">
<link rel="stylesheet" type="text/css" href="style.css">
<title>Serialization - Serialization of Classes</title>
</head>
<body link="#0000ff" vlink="#800080">
<table border="0" cellpadding="7" cellspacing="0" width="100%" summary="header">
<tr>
<td valign="top" width="300">
<h3><a href="../../../index.htm"><img height="86" width="277" alt="C++ Boost" src="../../../boost.png" border="0"></a></h3>
</td>
<td valign="top">
<h1 align="center">Serialization</h1>
<h2 align="center">Serializable Concept</h2>
</td>
</tr>
</table>
<hr>
<dl class="page-index">
<dt><a href="#primitiveoperators">Primitive Types</a>
<dt><a href="#classoperators">Class Types</a>
<dl class="page-index">
<dt><a href="#member">Member Function</a>
<dt><a href="#free">Free Function</a>
<dl class="page-index">
<dt><a href="#namespaces">Namespaces for Free Function Overrides</a>
</dl>
<dt><a href="#classmembers">Class Members</a>
<dl class="page-index">
<dt><a href="#base">Base Classes</a>
<dt><a href="#const"><code style="white-space: normal">const</code> Members</a>
<dt><a href="#templates">Templates</a>
</dl>
<dt><a href="#versioning">Versioning</a>
<dt><a href="#splitting">Splitting <code style="white-space: normal">serialize</code> into
<code style="white-space: normal">save/load</code></a>
<dl class="page-index">
<dt><a href="#splittingmemberfunctions">Member Functions</a>
<dt><a href="#splittingfreefunctions">Free Functions</a>
</dl>
</dl>
<dt><a href="#pointeroperators">Pointers</a>
<dl class="page-index">
<dt><a href="#constructors">Non-Default Constructors</a>
<dt><a href="#derivedpointers">Pointers to Objects of Derived Classes</a>
<dl class="page-index">
<dt><a href="#registration">Registration</a>
<dt><a href="#export">Export</a>
<dt><a href="#instantiation">Instantiation</a>
<dt><a href="#selectivetracking">Selective Tracking</a>
<dt><a href="#runtimecasting">Runtime Casting</a>
</dl>
</dl>
<dt><a href="#references">References</a>
<dt><a href="#arrays">Arrays</a>
<dt><a href="traits.html">Class Serialization Traits</a>
<dt><a href="wrappers.html">Serialization Wrappers</a>
<dt><a href="#models">Models - Serialization Implementations Included in the Library</a>
</dl>
A type <code style="white-space: normal">T</code> is <strong>Serializable</strong>
if and only if one of the following is true:
<ul>
<li>it is a primitive type.<br>
By <i>primitive type</i> we mean a C++ built-in type and <i>ONLY</i>
a C++ built-in type. Arithmetic (including characters), bool, enum are primitive types.
Below in <a target="detail" href="traits.html#Traits">serialization traits</a>,
we define a "primitive" implementation level in a different way for a
different purpose. This can be a source of confusion.
<li>It is a class type and one of the following has been declared according
to the prototypes detailed below:
<ul>
<li>a class member function <code style="white-space: normal">serialize</code>
<li>a global function <code style="white-space: normal">serialize</code>
</ul>
<li>it is a pointer to a <strong>Serializable</strong> type.
<li>it is a reference to a <strong>Serializable</strong> type.
<li>it is a native C++ Array of <strong>Serializable</strong> type.
</ul>
<h2><a name="primitiveoperators">Primitive Types</a></h2>
The template operators &, <<, and >> of the archive classes
described above will generate code to save/load all primitive types
to/from an archive. This code will usually just add the
data to the archive according to the archive format.
For example, a four byte integer is appended to a binary archive
as 4 binary bytes while a to a text archive it would be
rendered as a space followed by a string representation.
<h2><a name="classoperators">Class Types</a></h2>
For class/struct types, the template operators &, <<, and >>
will generate code that invokes the programmer's serialization code for the
particular data type. There is no default. An attempt to serialize a
class/struct for which no serialization has been explicitly specified
will result in a compile time error. The serialiation of a class can
be specified via either a class member function or a free funcation which
takes a reference to an instance of the class as an argument.
<h3><a name="member">Member Function</a></h3>
The serialization library invokes the following code to save or load a class instance
to/from and archive.
<pre><code>
template<class Archive, class T>
inline void serialize(
Archive & ar,
T & t,
const unsigned int file_version
){
// invoke member function for class T
t.serialize(ar, file_version);
}
</code></pre>
That is, the default definition of template <code style="white-space: normal">serialize</code>
presumes the existence of a class member function template of the following
signature:
<pre><code>
template<class Archive>
void serialize(Archive &ar, const unsigned int version){
...
}
</code></pre>
If such a member function is not declared, a compile time error will occur. In order
that the member function generated by this template can be called to
append the data to an archive, it either must be public or the class must
be made accessible to the serialization library by including:
<pre><code>
friend class boost::serialization::access;
</code></pre>
in the class definition. This latter method should be preferred over the option
of making the member function public. This will prevent serialization functions from
being called from outside the library. This is almost certainly an error. Unfortunately,
it may appear to function but fail in a way that is very difficult to find.
<p>
It may not be immediately obvious how this one template serves for both
saving data to an archive as well as loading data from the archive.
The key is that the <code style="white-space: normal">&</code> operator is
defined as <code style="white-space: normal"><<</code>
for output archives and as <code style="white-space: normal">>></code> input archives. The
"polymorphic" behavior of the <code style="white-space: normal">&</code> permits the same template
to be used for both save and load operations. This is very convenient in that it
saves a lot of typing and guarantees that the saving and loading of class
data members are always in sync. This is the key to the whole serialization
system.
<h3><a name="free">Free Function</a></h3>
Of course we're not restricted to using the default implementation described
above. We can override the default one with our own. Doing this will
permit us to implement serialization of a class without altering
the class definition itself. We call this <strong>non-intrusive</strong>
serialization. Suppose our class is named <code style="white-space: normal">my_class</code>, the
override would be specified as:
<pre><code>
// namespace selection
template<class Archive>
inline void serialize(
Archive & ar,
my_class & t,
const unsigned int file_version
){
...
}
</code></pre>
Note that we have called this override "non-intrusive". This is slightly
inaccurate. It does not require that the class have special functions, that
it be derived from some common base class or any other fundamental design changes.
However, it will require access to the class members that are to
be saved and loaded. If these members are <code style="white-space: normal">private</code>, it won't be
possible to serialize them. So in some instances, minor modifications to the
class to be serialized will be necessary even when using this "non-intrusive"
method. In practice this may not be such a problem as many libraries
(E.G. STL) expose enough information to permit implementation of non-intrusive
serialization with absolutly no changes to the library.
<h4><a name="namespaces">Namespaces for Free Function Overrides</a></h4>
For maximum portability, include any free functions templates and definitions in the
namespace <code style="white-space: normal">boost::serialization</code>. If portability is not a concern and the
compiler being used supports ADL (Argument Dependent Lookup) the free functions and
templates can be in any of the following namespaces:
<ul>
<li><code style="white-space: normal">boost::serialization</code>
<li>namespace of the archive class
<li>namespace of the type being serialized
</ul>
<p>
Note that, at first glance, this suggestion may seem to be wrong for compilers which implement
two phase lookup. In fact, the serialization library used a perhaps overly clever
method to support this rule even for such compilers. Those with an interest in studying
this further will find more information in
<a target=serialization_hpp href="../../../boost/serialization/serialization.hpp">serialization.hpp</a>
<h3><a name="classmembers">Serialization of Class Members</a></h3>
Regardless of which of the above methods is used, the body of the serialize function must
specify the data to be saved/loaded by sequential application of the archive
<code style="white-space: normal">operator &</code> to all the data members of the class.
<pre><code>
{
// save/load class member variables
ar & member1;
ar & member2;
}
</code></pre>
<h4><a name="base">Base Classes</a></h4>
The header file
<a href="../../../boost/serialization/base_object.hpp" target="base_object_hpp">
base_object.hpp
</a>
includes the template:
<pre><code>
template<class Base, class Derived>
Base & base_object(Derived &d);
</code></pre>
which should be used to create a reference to an object of the base
which can be used as an argument to the archive serialization operators.
So for a class of <strong>Serializable</strong> type
<code style="white-space: normal">T</code> the base class state should be
serialized like this:
<pre><code>
{
// invoke serialization of the base class
ar & boost::serialization::base_object<base_class_of_T>(*this);
// save/load class member variables
ar & member1;
ar & member2;
}
</code></pre>
Resist the temptation to just cast <code style="white-space: normal">*this</code> to the base class.
This might seem to work but may fail to invoke code necessary for
proper serialization.
<p>
Note that this is <strong>NOT</strong> the same as calling the <code style="white-space: normal">serialize</code>
function of the base class. This might seem to work but will circumvent
certain code used for tracking of objects, and registering base-derived
relationships and other bookkeeping that is required for the serialization
system to function as designed. For this reason, all <code style="white-space: normal">serialize</code>
member functions should be <code style="white-space: normal">private</code>.
<h4><a name="const"><code style="white-space: normal">const</code> Members</a></h4>
Saving <code style="white-space: normal">const</code> members to an archive
requires no special considerations.
Loading <code style="white-space: normal">const</code> members can be addressed by using a
<code style="white-space: normal">const_cast</code>:
<pre><code>
ar & const_cast<T &>(t);
</code></pre>
Note that this violates the spirit and intention of the <code style="white-space: normal">const</code>
keyword. <code style="white-space: normal">const</code> members are intialized when a class instance
is constructed and not changed thereafter. However, this may
be most appropriate in many cases. Ultimately, it comes down to
the question about what <code style="white-space: normal">const</code> means in the context
of serialization.
<h4><a name="templates"></a>Templates</h4>
Implementation of serialization for templates is exactly the same process
as for normal classes and requires no additional considerations. Among
other things, this implies that serialization of compositions of templates
are automatically generated when required if serialization of the
component templates is defined. For example, this library includes
definition of serialization for <code style="white-space: normal">boost::shared_ptr<T></code> and for
<code style="white-space: normal">std::list<T></code>. If I have defined serialization for my own
class <code style="white-space: normal">my_t</code>, then serialization for
<code style="white-space: normal">std::list< boost::shared_ptr< my_t> ></code> is already available
for use.
<p>
For an example that shows how this idea might be implemented for your own
class templates, see
<a href="../example/demo_auto_ptr.cpp" target="demo_auto_ptr.cpp">
demo_auto_ptr.cpp</a>.
This shows how non-intrusive serialization
for the template <code style="white-space: normal">auto_ptr</code> from the standard library
can be implemented.
<p>
A somewhat trickier addition of serialization to a standard template
can be found in the example
<a href="../../../boost/serialization/shared_ptr.hpp" target="shared_ptr_hpp">
shared_ptr.hpp
</a>
<!--
Only the most minimal change to
<code>shared_count.hpp</code>
(to gain access to some private members) was necessary to achieve this.
This should demonstrate how easy it is to non-intrusively
implement serialization to any data type or template.
-->
<p>
In the specification of serialization for templates, its common
to split <code style="white-space: normal">serialize</code>
into a <code style="white-space: normal">load/save</code> pair.
Note that the convenience macro described
<a href="#BOOST_SERIALIZATION_SPLIT_FREE">above</a>
isn't helpful in these cases as the number and kind of
template class arguments won't match those used when splitting
<code style="white-space: normal">serialize</code> for a simple class. Use the override
syntax instead.
<h3><a name="versioning">Versioning</a></h3>
It will eventually occur that class definitions change after archives have
been created. When a class instance is saved, the current version
in included in the class information stored in the archive. When the class instance
is loaded from the archive, the original version number is passed as an
argument to the loading function. This permits the load function to include
logic to accommodate older definitions for the class and reconcile them
with latest version. Save functions always save the current version. So this
results in automatically converting older format archives to the newest versions.
Version numbers are maintained independently for each class. This results in
a simple system for permitting access to older files and conversion of same.
The current version of the class is assigned as a
<a href="traits.html">Class Serialization Trait</a> described later in this manual.
<pre><code>
{
// invoke serialization of the base class
ar & boost::serialization::base_object<base_class_of_T>(*this);
// save/load class member variables
ar & member1;
ar & member2;
// if its a recent version of the class
if(1 < file_version)
// save load recently added class members
ar & member3;
}
</code></pre>
<h3><a name="splitting">Splitting <code style="white-space: normal">serialize</code> into Save/Load</a></h3>
There are times when it is inconvenient to use the same
template for both save and load functions. For example, this might occur if versioning
gets complex.
<h4><a name="splittingmemberfunctions">Splitting Member Functions</a></h4>
For member functions this can be addressed by including
the header file <a href="../../../boost/serialization/split_member.hpp" target="split_member_hpp">
boost/serialization/split_member.hpp</a> including code like this in the class:
<pre><code>
template<class Archive>
void save(Archive & ar, const unsigned int version) const
{
// invoke serialization of the base class
ar << boost::serialization::base_object<const base_class_of_T>(*this);
ar << member1;
ar << member2;
ar << member3;
}
template<class Archive>
void load(Archive & ar, const unsigned int version)
{
// invoke serialization of the base class
ar >> boost::serialization::base_object<base_class_of_T>(*this);
ar >> member1;
ar >> member2;
if(version > 0)
ar >> member3;
}
template<class Archive>
void serialize(
Archive & ar,
const unsigned int file_version
){
boost::serialization::split_member(ar, *this, file_version);
}
</code></pre>
This splits the serialization into two separate functions <code style="white-space: normal">save</code>
and <code style="white-space: normal">load</code>. Since the new <code style="white-space: normal">serialize</code> template
is always the same it can be generated by invoking the macro
BOOST_SERIALIZATION_SPLIT_MEMBER() defined in the header file
<a href="../../../boost/serialization/split_member.hpp" target="split_member_hpp">
boost/serialization/split_member.hpp
</a>.
So the entire <code style="white-space: normal">serialize</code> function above can be replaced with:
<pre><code>
BOOST_SERIALIZATION_SPLIT_MEMBER()
</code></pre>
<h4><a name="splittingfreefunctions">Splitting Free Functions</a></h4>
The situation is same for non-intrusive serialization with the free
<code style="white-space: normal">serialize</code> function template.
<a name="BOOST_SERIALIZATION_SPLIT_FREE">
To use <code style="white-space: normal">save</code> and
<code style="white-space: normal">load</code> function templates rather than
<code style="white-space: normal">serialize</code>:
<pre><code>
namespace boost { namespace serialization {
template<class Archive>
void save(Archive & ar, const my_class & t, unsigned int version)
{
...
}
template<class Archive>
void load(Archive & ar, my_class & t, unsigned int version)
{
...
}
}}
</code></pre>
include the header file
<a href="../../../boost/serialization/split_free.hpp" target="split_free_hpp">
boost/serialization/split_free.hpp
</a>.
and override the free <code style="white-space: normal">serialize</code> function template:
<pre><code>
namespace boost { namespace serialization {
template<class Archive>
inline void serialize(
Archive & ar,
my_class & t,
const unsigned int file_version
){
split_free(ar, t, file_version);
}
}}
</code></pre>
To shorten typing, the above template can be replaced with
the macro:
<pre><code>
BOOST_SERIALIZATION_SPLIT_FREE(my_class)
</code></pre>
Note that although the functionality to split the <code style="white-space: normal">
serialize</code> function into <code style="white-space: normal">save/load</code>
has been provided, the usage of the <code style="white-space: normal">serialize</code>
function with the corresponding <code style="white-space: normal">&</code> operator
is preferred. The key to the serialization implementation is that objects are saved
and loaded in exactly the same sequence. Using the <code style="white-space: normal">&</code>
operator and <code style="white-space: normal">serialize</code>
function guarantees that this is always the case and will minimize the
occurrence of hard to find errors related to synchronization of
<code style="white-space: normal">save</code> and <code style="white-space: normal">load</code>
functions.
<p>
Also note that <code style="white-space: normal">BOOST_SERIALIZATION_SPLIT_FREE</code>
must be used outside of any namespace.
<h2><a name="pointeroperators">Pointers</a></h2>
A pointer to any class instance can be serialized with any of the archive
save/load operators.
<p>
To properly save and restore an object through a pointer the
following situations must be addressed:
<ol>
<li>If the same object is saved multiple times through different
pointers, only one copy of the object need be saved.
<li>If an object is loaded multiple times through different pointers,
only one new object should be created and all returned pointers
should point to it.
<li>The system must detect the case where an object is first
saved through a pointer then the object itself is saved.
Without taking extra precautions, loading would result in the
creation of multiple copies of the original object. This system detects
this case when saving and throws an exception - see below.
<li>An object of a derived class may be stored through a
pointer to the base class. The true type of the object must
be determined and saved. Upon restoration the correct type
must be created and its address correctly cast to the base
class. That is, polymorphic pointers have to be considered.
<li>NULL pointers must be dectected when saved and restored
to NULL when deserialized.
</ol>
This serialization library addresses all of the above
considerations. The process of saving and loading an object
through a pointer is non-trivial. It can be summarized as
follows:
<p>Saving a pointer:
<ol>
<li>determine the true type of the object being pointed to.
<li>write a special tag to the archive
<li>if the object pointed to has not already been written
to the archive, do so now
</ol>
Loading a pointer:
<ol>
<li>read a tag from the archive.
<li>determine the type of object to be created
<li>if the object has already been loaded, return its address.
<li>otherwise, create a new instance of the object
<li>read the data back in using the operators described above
<li>return the address of the newly created object.
</ol>
Given that class instances are saved/loaded to/from the archive
only once, regardless of how many times they are serialized with
the <code style="white-space: normal"><<</code>
and <code style="white-space: normal">>></code> operators
<ul>
<li>Loading the same pointer object multiple times
results in only one object being created, thereby replicating
the original pointer configuration.
<li>Structures, such as collections of polymorphic pointers,
are handled with no special effort on the part of users of this library.
</ul>
Serialization of pointers of derived types through a pointer to the
base class may require a little extra "help". Also, the programmer
may desire to modify the process described above for his own reasons.
For example, it might be desired to suppress the tracking of objects
as it is known a priori that the application in question can never
create duplicate objects. Serialization of pointers can be "fine tuned"
via the specification of <a target="detail" href="traits.html#Traits">Class Serialization Traits</a>
as described in
<a target="detail" href="special.html#derivedpointers">
another section of this manual
</a>
<h3><a name="constructors">Non-Default Constructors</a></h3>
Serialization of pointers is implemented in the library with code
similar to the following:
<pre><code>
// load data required for construction and invoke constructor in place
template<class Archive, class T>
inline void load_construct_data(
Archive & ar, T * t, const unsigned int file_version
){
// default just uses the default constructor to initialize
// previously allocated memory.
::new(t)T();
}
</code></pre>
The default <code style="white-space: normal">load_construct_data</code> invokes the
default constructor "in-place" to initialize the memory.
<p>
If there is no such default constructor, the function templates
<code style="white-space: normal">load_construct_data</code> and
perhaps <code style="white-space: normal">save_construct_data</code>
will have to be overridden. Here is a simple example:
<pre><code>
class my_class {
private:
friend class boost::serialization::access;
const int m_attribute; // some immutable aspect of the instance
int m_state; // mutable state of this instance
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version){
ar & m_state;
}
public:
// no default construct guarentees that no invalid object
// ever exists
my_class(int attribute) :
m_attribute(attribute),
m_state(0)
{}
};
</code></pre>
the overrides would be:
<pre><code>
namespace boost { namespace serialization {
template<class Archive>
inline void save_construct_data(
Archive & ar, const my_class * t, const unsigned int file_version
){
// save data required to construct instance
ar << t->m_attribute;
}
template<class Archive>
inline void load_construct_data(
Archive & ar, my_class * t, const unsigned int file_version
){
// retrieve data from archive required to construct new instance
int attribute;
ar >> attribute;
// invoke inplace constructor to initialize instance of my_class
::new(t)my_class(attribute);
}
}} // namespace ...
</code></pre>
In addition to the deserialization of pointers, these overrides are used
in the deserialization of STL containers whose element type has no default
constructor.
<h3><a name="derivedpointers">Pointers to Objects of Derived Classes</a></h3>
<h4><a name="registration">Registration</a></h4>
Consider the following:
<pre><code>
class base {
...
};
class derived_one : public base {
...
};
class derived_two : public base {
...
};
main(){
...
base *b;
...
ar & b;
}
</code></pre>
When saving <code style="white-space: normal">b</code> what kind of object should be saved?
When loading <code style="white-space: normal">b</code> what kind of object should be created?
Should it be an object of class <code style="white-space: normal">derived_one</code>,
<code style="white-space: normal">derived_two</code>, or maybe <code style="white-space: normal">base</code>?
<p>
It turns out that the kind of object serialized depends upon whether the base class
(<code style="white-space: normal">base</code> in this case) is polymophic or not.
If <code style="white-space: normal">base</code> is not polymorphic, that is if it has no
virtual functions, then an object of the type <code style="white-space: normal">base</code>
will be serialized. Information in any derived classes will be lost. If this is what is desired
(it usually isn't) then no other effort is required.
<p>
If the base class is polymorphic, an object of the most derived type
(<code style="white-space: normal">derived_one</code>
or <code style="white-space: normal">derived_two</code>
in this case) will be serialized. The question of which type of object is to be
serialized is (almost) automatically handled by the library.
<p>
The system "registers" each class in an archive the first time an object of that
class it is serialized and assigns a sequential number to it. Next time an
object of that class is serialized in that same archive, this number is written
in the archive. So every class is identified uniquely within the archive.
When the archive is read back in, each new sequence number is re-associated with
the class being read. Note that this implies that "registration" has to occur
during both save and load so that the class-integer table built on load
is identical to the class-integer table built on save. In fact, the key to
whole serialization system is that things are always saved and loaded in
the same sequence. This includes "registration".
<p>
Expanding our previous example:
<pre><code>
main(){
derived_one d1;
derived_two d2:
...
ar & d1;
ar & d2;
// A side effect of serialization of objects d1 and d2 is that
// the classes derived_one and derived_two become known to the archive.
// So subsequent serialization of those classes by base pointer works
// without any special considerations.
base *b;
...
ar & b;
}
</code></pre>
When <code style="white-space: normal">b</code> is read it is
preceded by a unique (to the archive) class identifier which
has previously been related to class <code style="white-space: normal">derived_one</code> or
<code style="white-space: normal">derived_two</code>.
<p>
If a derived class has NOT been automatically "registered" as described
above, an <a target="detail" href="exceptions.html#unregistered_class">
<code style="white-space: normal">unregistered_class</code></a> exception
will be thrown when serialization is invoked.
<p>
This can be addressed by registering the derived class explicitly. All archives are
derived from a base class which implements the following template:
<pre><code>
template<class T>
register_type();
</code></pre>
So our problem could just as well be addressed by writing:
<pre><code>
main(){
...
ar.template register_type<derived_one>();
ar.template register_type<derived_two>();
base *b;
...
ar & b;
}
</code></pre>
Note that if the serialization function is split between save and load, both
functions must include the registration. This is required to keep the save
and corresponding load in syncronization.
<h4><a name="export">Export</a></h4>
The above will work but may be inconvenient. We don't always know which derived
classes we are going to serialize when we write the code to serialize through
a base class pointer. Every time a new derived class is written we have to
go back to all the places where the base class is serialized and update the
code.
<p>
So we have another method:
<pre><code>
#include <boost/serialization/export.hpp>
...
BOOST_CLASS_EXPORT_GUID(derived_one, "derived_one")
BOOST_CLASS_EXPORT_GUID(derived_two, "derived_two")
main(){
...
base *b;
...
ar & b;
}
</code></pre>
The macro <code style="white-space: normal">BOOST_CLASS_EXPORT_GUID</code> associates a string literal
with a class. In the above example we've used a string rendering
of the class name. If a object of such an "exported" class is serialized
through a pointer and is otherwise unregistered, the "export" string is
included in the archive. When the archive
is later read, the string literal is used to find the class which
should be created by the serialization library.
This permits each class to be in a separate header file along with its
string identifier. There is no need to maintain a separate "pre-registration"
of derived classes that might be serialized. This method of
registration is referred to as "key export". More information on this
topic is found in the section Class Traits -
<a target="detail" href="traits.html#export">Export Key</a>.
<p>
<h4><a name="instantiation">Instantiation</a></h4>
Registration by means of any of the above methods fulfill another role
whose importance might not be obvious. This system relies on templated
functions of the form <code style="white-space: normal">template<class Archive, class T></code>.
This means that serialization code must be instantiated for each
combination of archive and data type that is serialized in the program.
<p>
Polymorphic pointers of derived classes may never be referred to
explictly by the program so normally code to serialize such classes
would never be instantiated. So in addition to including export key
strings in an archive, <code style="white-space: normal">BOOST_CLASS_EXPORT_GUID</code> explicitly
instantiates the class serialization code for all archive classes used
by the program.
<h4><a name="selectivetracking">Selective Tracking</a></h4>
Whether or not an object is tracked is determined by its
<a target="detail" href="traits.html#tracking">object tracking trait</a>.
The default setting for user defined types is <code style="white-space: normal">track_selectively</code>.
That is, track objects if and only if they are serialized through pointers anywhere
in the program. Any objects that are "registered" by any of the above means are presumed
to be serialized through pointers somewhere in the program and therefore
would be tracked. In certain situations this could lead to an inefficiency.
Suppose we have a class module used by multiple programs. Because
some programs serializes polymorphic pointers to objects of this class, we
<a target="detail" href="traits.html#export">export</a> a class
identifier by specifying <code style="white-space: normal">BOOST_CLASS_EXPORT</code> in the
class header. When this module is included by another program,
objects of this class will always be tracked even though it
may not be necessary. This situation could be addressed by using
<a target="detail" href="traits.html#tracking"><code style="white-space: normal">track_never</code></a>
in those programs.
<p>
It could also occur that even though a program serializes through
a pointer, we are more concerned with efficiency than avoiding the
the possibility of creating duplicate objects. It could be
that we happen to know that there will be no duplicates. It could
also be that the creation of a few duplicates is benign and not
worth avoiding given the runtime cost of tracking duplicates.
Again, <a target="detail" href="traits.html#tracking"><code style="white-space: normal">track_never</code></a>
can be used.
<h4><a name="runtimecasting">Runtime Casting</a></h4>
In order to properly translate between base and derived pointers
at runtime, the system requires each base/derived pair be found
in a table. A side effect of serializing a base object with
<code style="white-space: normal">boost::serialization::base_object<Base>(Derived &)</code>
is to ensure that the base/derived pair is added to the table
before the <code style="white-space: normal">main</code> function is entered.
This is very convenient and results in a clean syntax. The only
problem is that it can occur where a derived class serialized
through a pointer has no need to invoke the serialization of
its base class. In such a case, there are two choices. The obvious
one is to invoke the base class serialization with <code style="white-space: normal">base_object</code>
and specify an empty function for the base class serialization.
The alternative is to "register" the Base/Derived relationship
explicitly by invoking the template
<code style="white-space: normal">void_cast_register<Derived, Base>();</code>.
Note that this usage of the term "register" is not related
to its usage in the previous section. Here is an example of how this is done:
<pre><code>
#include <sstream>
#include <boost/serialization/serialization.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
class base {
friend class boost::serialization::access;
//...
// only required when using method 1 below
// no real serialization required - specify a vestigial one
template<class Archive>
void serialize(Archive & ar, const unsigned int file_version){}
};
class derived : public base {
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int file_version){
// method 1 : invoke base class serialization
ar & boost::serialization::base_object<base>(*this);
// method 2 : explicitly register base/derived relationship
boost::serialization::void_cast_register<derived, base>(
static_cast<derived *>(NULL),
static_cast<base *>(NULL)
)
}
};
BOOST_CLASS_EXPORT_GUID(derived, "derived")
main(){
//...
std::stringstream ss;
boost::archive::text_iarchive ar(ss);
base *b;
ar >> b;
}
</code></pre>
<p>
In order for this template to be invoked in code compiled by non-conforming
compilers, the following syntax may be used:
<pre><code>
boost::serialization::void_cast_register(
static_cast<Derived *>(NULL),
static_cast<Base *>(NULL)
);
</code></pre>
For more information, see <a target="detail" href="implementation.html#tempatesyntax">Template Invocation syntax</a>
<h3><a name="references"></a>References</h3>
Classes that contain reference members will generally require
non-default constructors as references can only be set when
an instance is constructed. The example of the previous section
is slightly more complex if the class has reference members.
This raises the question of how and where the objects being
referred to are stored and how are they created. Also there is the question about
references to polymorphic base classes. Basically, these
are the same questions that arise regarding pointers. This is
no surprise as references are really a special kind of pointer.
We address these questions by serializing references as though
they were pointers.
<pre><code>
class object;
class my_class {
private:
friend class boost::serialization::access;
int member1;
object & member2;
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version);
public:
my_class(int m, object & o) :
member1(m),
member2(o)
{}
};
</code></pre>
the overrides would be:
<pre><code>
namespace boost { namespace serialization {
template<class Archive>
inline void save_construct_data(
Archive & ar, const my_class * t, const unsigned int file_version
){
// save data required to construct instance
ar << t.member1;
// serialize reference to object as a pointer
ar << & t.member2;
}
template<class Archive>
inline void load_construct_data(
Archive & ar, my_class * t, const unsigned int file_version
){
// retrieve data from archive required to construct new instance
int m;
ar >> m;
// create and load data through pointer to object
// tracking handles issues of duplicates.
object * optr;
ar >> optr;
// invoke inplace constructor to initialize instance of my_class
::new(t)my_class(m, *optr);
}
}} // namespace ...
</code></pre>
<h3><a name="arrays"></a>Arrays</h3>
If <code style="white-space: normal">T</code> is a serializable type,
then any native C++ array of type T is a serializable type.
That is, if <code style="white-space: normal">T</code>
is a serializable type, then the following
is automatically available and will function as expected:
<pre><code>
T t[4];
ar << t;
...
ar >> t;
</code></pre>
<h2><a href="traits.html">Class Serialization Traits</a></h2>
<h2><a href="wrappers.html">Serialization Wrappers</a></h2>
<h2><a name="models"></a>Models - Serialization Implementations Included in the Library</h2>
The facilities described above are sufficient to implement
serialization for all STL containers. In fact, this has been done
and has been included in the library. For example, in order to use
the included serialization code for <code style="white-space: normal">std::list</code>, use:
<pre><code>
#include <boost/serialization/list.hpp>
</code></pre>
rather than
<pre><code>
#include <list>
</code></pre>
Since the former includes the latter, this is all that is necessary.
The same holds true for all STL collections as well as templates
required to support them (e.g. <code style="white-space: normal">std::pair</code>).
<p>
As of this writing, the library contains serialization of the following boost classes:
<ul>
<li>optional
<li>variant
<li>scoped_ptr
<li>shared_ptr
<li>auto_ptr (demo)
</ul>
Others are being added to the list so check the boost files section and headers for
new implementations!
<hr>
<p><i>© Copyright <a href="http://www.rrsd.com">Robert Ramey</a> 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</i></p>
</body>
</html>