1<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 2<html> 3<!-- 4(C) Copyright 2002-4 Robert Ramey - http://www.rrsd.com . 5Use, modification and distribution is subject to the Boost Software 6License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 7http://www.boost.org/LICENSE_1_0.txt) 8--> 9<head> 10<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 11<link rel="stylesheet" type="text/css" href="../../../boost.css"> 12<link rel="stylesheet" type="text/css" href="style.css"> 13<title>Serialization - Class Serialization Traits</title> 14</head> 15<body link="#0000ff" vlink="#800080"> 16<table border="0" cellpadding="7" cellspacing="0" width="100%" summary="header"> 17 <tr> 18 <td valign="top" width="300"> 19 <h3><a href="../../../index.htm"><img height="86" width="277" alt="C++ Boost" src="../../../boost.png" border="0"></a></h3> 20 </td> 21 <td valign="top"> 22 <h1 align="center">Serialization</h1> 23 <h2 align="center">Class Serialization Traits</h2> 24 </td> 25 </tr> 26</table> 27<hr> 28<dl class="page-index"> 29 <dt><a href="#version">Version</a> 30 <dt><a href="#level">Implementation Level</a> 31 <dt><a href="#tracking">Object Tracking</a> 32 <dt><a href="#export">Export Key</a> 33 <dt><a href="#abstract">Abstract</a> 34 <dt><a href="#typeinfo">Type Information Implementation</a> 35 <dt><a href="#wrappers">Wrappers</a> 36 <dt><a href="#bitwise">Bitwise Serialization</a> 37 <dt><a href="#templates">Template Serialization Traits</a> 38 <dt><a href="#compiletime_messages">Compile Time Warnings and Errors</a> 39</dl> 40Serialization of data depends on the type of the data. For example, for 41primitive types such as <code style="white-space: normal">int</code>, it wouldn't make sense to save 42a version number in the archive. Likewise, for a data type that is never 43serialized through a pointer, it would (almost) never make sense to track 44the address of objects saved to/loaded from the archive as it will never 45be saved/loaded more than once in any case. Details of 46serialization for a particular data type will vary depending on the 47type, the way it is used and specifications of the programmer. 48<p> 49One can alter the manner in which a particular data type is serialized 50by specifying one or more <strong>class serialization traits</strong>. 51It is not generally necessary for the programmer to explictly assign 52traits to his classes as there are default values for all traits. 53If the default values are not appropriate they can be assigned by the programmer. 54A template is used to associate a typename with a constant. For example 55see <a href="../../../boost/serialization/version.hpp" target="version_hpp"> 56version.hpp</a>. 57<h3><a name="version">Version</a></h3> 58This header file includes the following code: 59 60<pre><code> 61namespace boost { 62namespace serialization { 63template<class T> 64struct version 65{ 66 BOOST_STATIC_CONSTANT(unsigned int, value = 0); 67}; 68} // namespace serialization 69} // namespace boost 70</code></pre> 71 72For any class <code style="white-space: normal">T</code>, The default definition 73of <code style="white-space: normal">boost::serialization::version<T>::value</code> is 0. 74If we want to assign a value of 2 as the version for class <code style="white-space: normal">my_class</code> 75we specialize the version template: 76<pre><code> 77namespace boost { 78namespace serialization { 79struct version<my_class> 80{ 81 BOOST_STATIC_CONSTANT(unsigned int, value = 2); 82}; 83} // namespace serialization 84} // namespace boost 85</code></pre> 86Now whenever the version number for class <code style="white-space: normal">my_class</code> is required, 87the value 2 will be returned rather than the default value of 0. 88<p> 89To diminish typing and enhance readability, a macro is defined 90so that instead of the above, we could write: 91<pre><code> 92BOOST_CLASS_VERSION(my_class, 2) 93</code></pre> 94which expands to the code above. 95 96<h3><a name="level">Implementation Level</a></h3> 97In the same manner as the above, the "level" of implementation of serialization is 98specified. The header file <a href="../../../boost/serialization/level.hpp" 99target="level_hpp">level.hpp</a> defines the following. 100<pre><code> 101// names for each level 102enum level_type 103{ 104 // Don't serialize this type. An attempt to do so should 105 // invoke a compile time assertion. 106 not_serializable = 0, 107 // write/read this type directly to the archive. In this case 108 // serialization code won't be called. This is the default 109 // case for fundamental types. It presumes a member function or 110 // template in the archive class that can handle this type. 111 // there is no runtime overhead associated reading/writing 112 // instances of this level 113 primitive_type = 1, 114 // Serialize the objects of this type using the objects "serialize" 115 // function or template. This permits values to be written/read 116 // to/from archives but includes no class or version information. 117 object_serializable = 2, 118 /////////////////////////////////////////////////////////////////// 119 // once an object is serialized at one of the above levels, the 120 // corresponding archives cannot be read if the implementation level 121 // for the archive object is changed. 122 /////////////////////////////////////////////////////////////////// 123 // Add class information to the archive. Class information includes 124 // implementation level, class version and class name if available. 125 object_class_info = 3, 126}; 127</code></pre> 128Using a macro defined in <code style="white-space: normal">level.hpp</code> we can specify 129that <code style="white-space: normal">my_class</code> should be serialized along with its version number: 130<pre><code> 131BOOST_CLASS_IMPLEMENTATION(my_class, boost::serialization::object_class_info) 132</code></pre> 133If implementation level is not explicitly assigned, the system uses 134a default according to the following rules. 135<ul> 136 <li>if the data type is <code style="white-space: normal">volatile</code> 137assign <code style="white-space: normal">not_serializable</code> 138 <li>else if it's an enum or fundamental type assign <code style="white-space: normal">primitive_type</code> 139 <li>else assign <code style="white-space: normal">object_class_info</code> 140</ul> 141That is, for most user defined types, objects will be serialized along with 142class version information. This will permit one to maintain backward 143compatibility with archives which contain previous versions. However, with this 144ability comes a small runtime cost. For types whose definition will "never" 145change, efficiency can be gained by specifying <code style="white-space: normal">object_serializable</code> 146to override the default setting of <code style="white-space: normal">object_class_info</code>. 147For example, 148this has been done for the 149<a href="../../../boost/serialization/binary_object.hpp" target="binary_object_hpp"> 150binary_object wrapper</a> 151 152<h3><a name="tracking">Object Tracking</a></h3> 153Depending on the way a type is used, it may be necessary or convenient to 154track the address of objects saved and loaded. For example, this is generally 155necessary while serializing objects through a pointer in order to be sure 156that multiple identical objects are not created when an archive is loaded. 157This "tracking behavior" is controlled by the type trait defined in the header 158file <a href="../../../boost/serialization/tracking.hpp" target="tracking_hpp">tracking.hpp</a> 159which defines the following: 160<pre><code> 161// names for each tracking level 162enum tracking_type 163{ 164 // never track this type 165 track_never = 0, 166 // track objects of this type if the object is serialized through a 167 // pointer. 168 track_selectively = 1, 169 // always track this type 170 track_always = 2 171}; 172</code></pre> 173A corresponding macro is defined so that we can use: 174<pre><code> 175BOOST_CLASS_TRACKING(my_class, boost::serialization::track_never) 176</code></pre> 177Default tracking traits are: 178<ul> 179 <li>For primitive, <code style="white-space: normal">track_never</code>. 180 <li>For pointers, <code style="white-space: normal">track_never</code>. 181 That is, addresses of addresses are not tracked by default. 182 <li>All current serialization wrappers such as <code style="white-space: normal">boost::serialization::nvp</code>, 183 <code style="white-space: normal">track_never</code>. 184 <li>For all other types, <code style="white-space: normal">track_selectively</code>. 185 That is addresses of serialized objects are tracked if and only if 186 one or more of the following is true: 187 <ul> 188 <li>an object of this type is anywhere in the program serialized 189 through a pointer. 190 <li>the class is explicitly "exported" - see below. 191 <li>the class is explicitly "registered" in the archive 192 </ul> 193</ul> 194 195<p> 196The default behavior is almost always the most convenient one. However, 197there a few cases where it would be desirable to override the 198default. One case is that of a virtual base class. In a diamond 199heritance structure with a virtual base class, object tracking 200will prevent redundant save/load invocations. So here is one 201case where it might be convenient to override the default tracking 202trait. <i>(Note: in a future version the default will be reimplemented 203to automatically track classes used as virtual bases).</i> This 204situation is demonstrated by 205<a href="../test/test_diamond.cpp" target="test_diamond_cpp">test_diamond.cpp</a> 206included with the library. 207 208<h3><a name="export">Export Key</a></h3> 209 210When serializing a derived class through a virtual base class pointer, 211two issues may arise. 212<ul> 213<li> The code in the derived class might never be explicitly 214referred to. Such code will never be instantiated. 215<p> 216This is addressed by invoking 217<code style="white-space: normal">BOOST_CLASS_EXPORT_IMPLEMENT(T)</code> 218in the file which defines (implements) the class T. 219This ensures that code for the derived class T will 220be explicity instantiated. 221<li> There needs to be some sort of identifier which can 222be used to select the code to be invoked when the object 223is loaded. 224Standard C++ does implement <code style="white-space: normal">typeid()</code> which can be 225used to return a unique string for the class. This is not entirely 226statisfactory for our purposes for the following reasons: 227<ul> 228 <li>There is no guarantee that the string is the same across platforms. 229 This would then fail to support portable archives. 230 <li>In using code modules from various sources, classes may have 231 to be wrapped in different namespaces in different programs. 232 <li>There might be classes locally defined in different code modules 233 that have the same name. 234 <li>There might be classes with different names that we want to 235 consider equivalent for purposes of serialization. 236</ul> 237<p> 238So in the serialization library, this is addressed by invoking 239<code style="white-space: normal">BOOST_CLASS_EXPORT_KEY2(my_class, "my_class_external_identifier")</code> 240in the header file which declares the class. 241In a large majority of applications, the class name works just fine 242for the external identifier string so the following short cut is 243defined - 244<code style="white-space: normal">BOOST_CLASS_EXPORT_KEY(my_class)</code>. 245</ul> 246For programs which consist of only one module - that is 247programs which do not use DLLS, one can specify 248<code style="white-space: normal">BOOST_CLASS_EXPORT(my_class)</code> 249or 250<code style="white-space: normal">BOOST_CLASS_EXPORT_GUID(my_class, "my_class_external_identifier")</code> 251in either the declaration header or definition. These macros 252expand to invocation of both of the macros described above. 253<i>(<b>GUID</b> stands for <b>G</b>lobally <b>U</b>nique <b>ID</b>entfier.)</i> 254<p> 255<i>(<a target="detail" href="special.html#export">Elsewhere</a> 256in this manual, the serialization of derived classes is addressed in detail.)</i> 257<p> 258The header file 259<a href="../../../boost/serialization/export.hpp" target="export_hpp">export.hpp</a> 260contains all macro definitions described here. 261The library will throw a runtime exception if 262<ul> 263<li> A type not explicitly referred to is not exported. 264<li> Serialization code for the same type is instantiated 265in more than one module (or DLL). 266</ul> 267 268<h3><a name="abstract">Abstract</a></h3> 269When serializing an object through a pointer to its base class, 270the library needs to determine whether or not the base is abstract 271(i.e. has at least one virtual function). The library uses the 272type trait macro <code style="white-space: normal">BOOST_IS_ABSTRACT(T)</code> 273to do this. Not all compilers support this type trait and corresponding 274macro. To address this, the macro <code style="white-space: normal"> 275BOOST_SERIALIZATION_ASSUME_ABSTRACT(T)</code> has been 276implemented to permit one to explicitly indicate that a specified 277type is in fact abstract. This will guarentee that 278<code style="white-space: normal">BOOST_IS_ABSTRACT</code> 279will return the correct value for all compilers. 280 281<h3><a name="typeinfo">Type Information Implementation</a></h3> 282This last trait is also related to the serialization of objects 283through a base class pointer. The implementation of this facility 284requires the ability to determine at run time the true type of the 285object that a base class pointer points to. Different serialization 286systems do this in different ways. In our system, the default method 287is to use the function <code style="white-space: normal">typeid(...)</code> which is available 288in systems which support <b>RTTI</b> (<b>R</b>un <b>T</b>ime 289<b>T</b>ype <b>I</b>nformation). 290This will be satisfactory in almost all cases and most users of this 291library will lose nothing in skipping this section of the manual. 292<p> 293However, there are some cases where the default type determination 294system is not convenient. Some platforms might not support 295RTTI or it may have been disabled in order to speed execution 296or for some other reason. Some applications, E.G. runtime linking 297of plug-in modules, can't depend on C++ RTTI to determine the 298true derived class. RTTI only returns the correct type for polymorphic 299classes - classes with at least one virtual function. If any of these 300situations applies, one may substitute his own implementation of 301<code style="white-space: normal">extended_type_info</code> 302<p> 303The interface to facilities required to implement serialization is defined in 304<a href="../../../boost/serialization/extended_type_info.hpp" 305target="extended_type_info_hpp">extended_type_info.hpp</a>. 306 307Default implementation of these facilities based on <code style="white-space: normal">typeid(...)</code> 308is defined in 309 310<a href="../../../boost/serialization/extended_type_info_typeid.hpp" 311target="extended_type_info_typeid_hpp">extended_type_info_typeid.hpp</a>. 312 313An alternative implementation based on exported class identifiers 314is defined in 315<a href="../../../boost/serialization/extended_type_info_no_rtti.hpp" 316target="extended_type_info_rtti_hpp">extended_type_info_no_rtti.hpp</a>. 317<p> 318By invoking the macro: 319<pre><code> 320BOOST_CLASS_TYPE_INFO( 321 my_class, 322 extended_type_info_no_rtti<my_class> 323) 324</code></pre> 325we can assign the type information implementation to each class on a case by 326case basis. There is no requirement that all classes in a program use the same 327implementation of <code style="white-space: normal">extended_type_info</code>. This supports the concept 328that serialization of each class is specified "once and for all" in a header 329file that can be included in any project without change. 330<p> 331This is illustrated by the test program 332<a href="../test/test_no_rtti.cpp" target="test_no_rtti_cpp">test_no_rtti.cpp</a>. 333Other implementations are possible and might be necessary for 334certain special cases. 335 336<h3><a name="wrappers">Wrappers</a></h3> 337Archives need to treat wrappers differently from other types since, for example, 338they usually are non-const objects while output archives require that any 339serialized object (with the exception of a wrapper) be const. 340 341This header file <a href="../../../boost/serialization/wrapper.hpp">wrapper.hpp</a> 342includes the following code: 343 344<pre><code> 345namespace boost { 346namespace serialization { 347template<class T> 348struct is_wrapper 349 : public mpl::false_ 350{}; 351} // namespace serialization 352} // namespace boost 353</code></pre> 354 355For any class <code style="white-space: normal">T</code>, The default definition 356of <code style="white-space: normal">boost::serialization::is_wrapper<T>::value</code> is thus false. 357 358If we want to declare that a class <code style="white-space: normal">my_class</code> 359is a wrapper we specialize the version template: 360<pre><code> 361namespace boost { 362namespace serialization { 363struct is_wrapper<my_class> 364 : mpl::true_ 365{}; 366} // namespace serialization 367} // namespace boost 368</code></pre> 369<p> 370To diminish typing and enhance readability, a macro is defined 371so that instead of the above, we could write: 372<pre><code> 373BOOST_CLASS_IS_WRAPPER(my_class) 374</code></pre> 375which expands to the code above. 376 377<h3><a name="bitwise">Bitwise Serialization</a></h3> 378Some simple classes could be serialized just by directly copying all bits 379of the class. This is, in particular, the case for POD data types containing 380no pointer members, and which are neither versioned nor tracked. Some archives, 381such as non-portable binary archives can make us of this information to 382substantially speed up serialization. 383 384To indicate the possibility of bitwise serialization the type trait defined 385in the header 386file <a href="../../../boost/serialization/is_bitwise_serializable.hpp" target="is_bitwise_serializable">is_bitwise_serializable.hpp</a> 387is used: 388<pre><code> 389namespace boost { namespace serialization { 390 template<class T> 391 struct is_bitwise_serializable 392 : public is_arithmetic<T> 393 {}; 394} } 395</code></pre> 396is used, and can be specialized for other classes. The specialization 397is made easy by the corresponding macro: 398<pre><code> 399BOOST_IS_BITWISE_SERIALIZABLE(my_class) 400</code></pre> 401 402<h3><a name="templates">Template Serialization Traits</a></h3> 403In some instances it might be convenient to assign serialization traits 404to a whole group of classes at once. Consider, the name-value pair 405wrapper 406<pre><code> 407template<class T> 408struct nvp : public std::pair<const char *, T *> 409{ 410 ... 411}; 412</code></pre> 413used by XML archives to associate a name with a data variable of type T. 414These data types are never tracked and never versioned. So one might 415want to specify: 416<pre><code> 417BOOST_CLASS_IMPLEMENTATION(nvp<T>, boost::serialization::level_type::object_serializable) 418BOOST_CLASS_TRACKING(nvp<T>, boost::serialization::track_never) 419</code></pre> 420Examination of the definition of these macros reveals that they won't expand 421to sensible code when used with a template argument. So rather than using the 422convenience macros, use the original definitions 423<pre><code> 424template<class T> 425struct implementation_level<nvp<T> > 426{ 427 typedef mpl::integral_c_tag tag; 428 typedef mpl::int_<object_serializable> type; 429 BOOST_STATIC_CONSTANT( 430 int, 431 value = implementation_level::type::value 432 ); 433}; 434 435// nvp objects are generally created on the stack and are never tracked 436template<class T> 437struct tracking_level<nvp<T> > 438{ 439 typedef mpl::integral_c_tag tag; 440 typedef mpl::int_<track_never> type; 441 BOOST_STATIC_CONSTANT( 442 int, 443 value = tracking_level::type::value 444 ); 445}; 446</code></pre> 447to assign serialization traits to all classes generated by the template 448<code style="white-space: normal">nvp<T></code> 449<p> 450 451Note that it is only possible to use the above method to assign traits to 452templates when using compilers which correctly support Partial Template Specialization. 453 454One's first impulse might be to do something like: 455 456<pre><code> 457#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION 458template<class T> 459struct implementation_level<nvp<T> > 460{ 461 ... // see above 462}; 463 464// nvp objects are generally created on the stack and are never tracked 465template<class T> 466struct tracking_level<nvp<T> > 467{ 468 ... // see above 469}; 470#endif 471</code></pre> 472This can be problematic when one wants to make his code <strong>and archives</strong> 473portable to other platforms. It means the objects will be serialized differently 474depending on the platform used. This implies that objects saved from one platform 475won't be loaded properly on another. In other words, archives won't be portable. 476<p> 477This problem is addressed by creating another method of assigning serialization traits 478to user classes. This is illustrated by the serialization for a 479<a target="nvp" href="../../../boost/serialization/nvp.hpp"><strong>name-value</strong> pair</a>. 480<p> 481Specifically, this entails deriving the template from a special class 482<a target="traits" href="../../../boost/serialization/traits.hpp"> 483<code style="white-space: normal">boost::serialization::traits</code></a> which is specialized for a specific 484combination of serialization traits. 485When looking up the serialization traits, the library first checks to see if this class has been 486used as a base class. If so, the corresponding traits are used. Otherwise, the standard defaults 487are used. By deriving from a serialization traits class rather than relying upon Partial Template 488Specializaton, one can a apply serialization traits to a template and those traits will be 489the same across all known platforms. 490<p> 491The signature for the traits template is: 492<pre><code> 493template< 494 class T, 495 int Level, 496 int Tracking, 497 unsigned int Version = 0, 498 class ETII = BOOST_SERIALIZATION_DEFAULT_TYPE_INFO(T), 499 class IsWrapper = mpl::false_ 500> 501struct traits 502</code></pre> 503and template parameters should be assigned according to the following table: 504<p> 505<table border> 506<tr><th align=left>parameter</th><th align=left>description</th><th align=left>permitted values</th><th align=left>default value</th></tr> 507<tr><td><code>T</code></td><td>target class</td><td>class name<T></td><td>none</td></tr> 508<tr><td><code>Level</code></td><td>implementation level</td><td><code>not_serializable<br>primitive_type<br>object_serializable<br>object_class_info</code></td><td>none</td></tr> 509<tr><td><code>Tracking</code></td><td>tracking level</td><td><code>track_never<br>track_selectivly<br>track_always</code></td><td>none</td></tr> 510<tr><td><code>Version</code></td><td><code>class version</td><td>unsigned integer</td><td><code>0</code></td></tr> 511<tr><td><code>ETTI</code></td><td><code>type_info</code> implementation</td><td><code>extended_type_info_typeid<br>extended_type_info_no_rtti</code></td><td>default <code>type_info implementation</code></td></tr> 512<tr><td><code>IsWrapper</code></td><td><code></code>is the type a wrapper?</td><td><code>mpl::false_<br>mpl::true_</code></td><td><code>mpl::false_</code></td></tr> 513</table> 514 515<h3><a name="compiletime_messages">Compile Time Warnings and Errors</a></h3> 516Some serialization traits can conflict with other ones. Sometimes these conflicts 517will result in erroneous behavior (E.G. creating of archives which could not be read) 518and other times they represent a probable misconception on the part of the 519library user which could result in suprising behavior. To the extent possible, 520these conflicts are detected at compile time and errors (BOOST_STATIC_ASSERT) 521or warnings (BOOST_STATIC_WARNING) are generated. They are generated in a 522compiler dependent manner which should show a chain of instantiation 523to the point where the error/warning is detected. Without this capability, 524it would be very hard to track down errors or unexpected behavior in library 525usage. Here is a list of the conflicts trapped: 526 527<dl> 528<dt><h2><a name="object_level">object_level</a> - error</h2></dt> 529<dd> 530This error traps attempts to serialize types whose 531implentation level is set to non_serializable. 532</dd> 533<dt><h2><a name="object_versioning">object_versioning</a> - error</h2></dt> 534<dd> 535It's possible that for efficiency reasons, a type can be 536assigned a serialization level which doesn't include type information 537in the archive. This would preclude the assignment 538of a new version number to the type. This error 539traps attempts to assign a version number in this case. 540This has to be a user error. 541</dd> 542 543<dt><h2><a name="object_tracking">object_tracking</a> - warning</h2></dt> 544<dd> 545The following code will display a message when compiled: 546 547<code style="white-space: normal"><pre> 548T t; 549ar << t; 550</pre></code> 551 552unless the tracking_level serialization trait is set to "track_never". The following 553will compile without problem: 554 555<code style="white-space: normal"><pre> 556const T t 557ar << t; 558</pre></code> 559 560Likewise, the following code will trap at compile time: 561 562<code style="white-space: normal"><pre> 563T * t; 564ar >> t; 565</pre></code> 566 567if the tracking_level serialization trait is set to "track_never". 568<p> 569 570The following case illustrates the function of this message. 571It was originally used as an example in the 572mailing list by Peter Dimov. 573 574<code style="white-space: normal"><pre> 575class construct_from 576{ 577 ... 578}; 579 580void main(){ 581 ... 582 Y y; 583 construct_from x(y); 584 ar << x; 585} 586</pre></code> 587 588Suppose that the above message is not displayed and the code is used as is. 589<ol> 590 <li>this example compiles and executes fine. No tracking is done because 591 construct_from has never been serialized through a pointer. Now some time 592 later, the next programmer(2) comes along and makes an enhancement. He 593 wants the archive to be sort of a log. 594 595<code style="white-space: normal"><pre> 596void main(){ 597 ... 598 Y y; 599 construct_from x(y); 600 ar << x; 601 ... 602 x.f(); // change x in some way 603 ... 604 ar << x 605} 606</pre></code> 607 <p> 608 Again no problem. He gets two different of copies in the archive, each one is different. 609 That is he gets exactly what he expects and is naturally delighted. 610 <p> 611 <li>Now sometime later, a third programmer(3) sees construct_from and says - 612 oh cool, just what I need. He writes a function in a totally disjoint 613 module. (The project is so big, he doesn't even realize the existence of 614 the original usage) and writes something like: 615 616<code style="white-space: normal"><pre> 617class K { 618 shared_ptr <construct_from> z; 619 template <class Archive> 620 void serialize(Archive & ar, const unsigned version){ 621 ar << z; 622 } 623}; 624</pre></code> 625 626 <p> 627 He builds and runs the program and tests his new functionality. It works 628 great and he's delighted. 629 <p> 630 <li>Things continue smoothly as before. A month goes by and it's 631 discovered that when loading the archives made in the last month (reading the 632 log). Things don't work. The second log entry is always the same as the 633 first. After a series of very long and increasingly acrimonius email exchanges, 634it's discovered 635 that programmer(3) accidently broke programmer(2)'s code .This is because by 636 serializing via a pointer, the "log" object is now being tracked. This is because 637 the default tracking behavior is "track_selectively". This means that class 638 instances are tracked only if they are serialized through pointers anywhere in 639 the program. Now multiple saves from the same address result in only the first one 640 being written to the archive. Subsequent saves only add the address - even though the 641 data might have been changed. When it comes time to load the data, all instances of the log record show the same data. 642 In this way, the behavior of a functioning piece of code is changed due the side 643 effect of a change in an otherwise disjoint module. 644 Worse yet, the data has been lost and cannot be recovered from the archives. 645 People are really upset and disappointed with boost (at least the serialization system). 646 <p> 647 <li> 648 After a lot of investigation, it's discovered what the source of the problem is 649 and class construct_from is marked "track_never" by including: 650<code style="white-space: normal"><pre> 651BOOST_CLASS_TRACKING(construct_from, track_never) 652</pre></code> 653 <li>Now everything works again. Or - so it seems. 654 <p> 655 <li><code style="white-space: normal">shared_ptr<construct_from></code> 656is not going to have a single raw pointer shared amongst the instances. Each loaded 657<code style="white-space: normal">shared_ptr<construct_from></code> is going to 658have its own distinct raw pointer. This will break 659<code style="white-space: normal">shared_ptr</code> and cause a memory leak. Again, 660The cause of this problem is very far removed from the point of discovery. It could 661well be that the problem is not even discovered until after the archives are loaded. 662Now we not only have a difficult to find and fix program bug, but we have a bunch of 663invalid archives and lost data. 664</ol> 665 666<p>Now consider what happens when the message is displayed: 667 668<ol> 669 <p> 670 <li>Right away, the program traps at 671<code style="white-space: normal"><pre> 672ar << x; 673</pre></code> 674 <p> 675 <li>The programmer curses (another %^&*&* hoop to jump through). He's in a 676 hurry (and who isn't) and would prefer not to <code style="white-space: normal">const_cast</code> 677 - because it looks bad. So he'll just make the following change an move on. 678<code style="white-space: normal"><pre> 679Y y; 680const construct_from x(y); 681ar << x; 682</pre></code> 683 <p> 684 Things work fine and he moves on. 685 <p> 686 <li>Now programer (2) wants to make his change - and again another 687 annoying const issue; 688<code style="white-space: normal"><pre> 689Y y; 690const construct_from x(y); 691... 692x.f(); // change x in some way ; compile error f() is not const 693... 694ar << x 695</pre></code> 696 <p> 697 He's mildly annoyed now he tries the following: 698 <ul> 699 <li>He considers making f() a const - but presumably that shifts the const 700 error to somewhere else. And he doesn't want to fiddle with "his" code to 701 work around a quirk in the serializaition system 702 <p> 703 <li>He removes the <code style="white-space: normal">const</code> 704 from <code style="white-space: normal">const construct_from</code> above - damn now he 705 gets the trap. If he looks at the comment code where the 706 <code style="white-space: normal">BOOST_STATIC_ASSERT</code> 707 occurs, he'll do one of two things 708 <ol> 709 <p> 710 <li>This is just crazy. Its making my life needlessly difficult and flagging 711 code that is just fine. So I'll fix this with a <code style="white-space: normal">const_cast</code> 712 and fire off a complaint to the list and mabe they will fix it. 713 In this case, the story branches off to the previous scenario. 714 <p> 715 <li>Oh, this trap is suggesting that the default serialization isn't really 716 what I want. Of course in this particular program it doesn't matter. But 717 then the code in the trap can't really evaluate code in other modules (which 718 might not even be written yet). OK, I'll add the following to my 719 construct_from.hpp to solve the problem. 720<code style="white-space: normal"><pre> 721BOOST_CLASS_TRACKING(construct_from, track_never) 722</pre></code> 723 </ol> 724 </ul> 725 <p> 726 <li>Now programmer (3) comes along and make his change. The behavior of the 727 original (and distant module) remains unchanged because the 728 <code style="white-space: normal">construct_from</code> trait has been set to 729 "track_never" so he should always get copies and the log should be what we expect. 730 <p> 731 <li>But now he gets another trap - trying to save an object of a 732 class marked "track_never" through a pointer. So he goes back to 733 construct_from.hpp and comments out the 734 <code style="white-space: normal">BOOST_CLASS_TRACKING</code> that 735 was inserted. Now the second trap is avoided, But damn - the first trap is 736 popping up again. Eventually, after some code restructuring, the differing 737 requirements of serializating <code style="white-space: normal">construct_from</code> 738 are reconciled. 739</ol> 740Note that in this second scenario 741<ul> 742 <li>all errors are trapped at compile time. 743 <li>no invalid archives are created. 744 <li>no data is lost. 745 <li>no runtime errors occur. 746</ul> 747 748It's true that these messages may sometimes flag code that is currently correct and 749that this may be annoying to some programmers. However, this example illustrates 750my view that these messages are useful and that any such annoyance is a small price to 751pay to avoid particularly vexing programming errors. 752 753</dd> 754 755<dt><h2><a name="pointer_level">pointer_level</a> - warning</h2></dt> 756<dd> 757This trap addresses the following situaion when serializing 758a pointer: 759<ul> 760<li>A type doesn't save class information in the 761archive. That is, the serialization trait implementation 762level <= object_serializable. 763<li>Tracking for this type is set to "track selectively" 764in this case, indication that an object is tracked is 765not stored in the archive itself - see level == object_serializable. 766Since class information is not saved in the archive, the existence 767or absence of the operation ar << T * anywhere else in the 768program is used to infer that an object of this type should be tracked. 769<p> 770A problem arises when a program which reads an archive 771includes the operation ar >> T * so that tracking information 772will be included in the archive. When a program which 773creates the archive doesn't include ar << T it is presumed 774that the archive doesn't include tracking information and 775the archive will fail to load. Also the reverse situation could 776trigger a similar problem. 777<p> 778Though this situation is unlikely for several reasones, 779it is possible - hence this warning. 780</ul> 781So if your program traps here, consider changing the 782tracking or implementation level traits - or not 783serializing via a pointer. 784</dd> 785 786<dt><h2><a name="pointer_tracking">pointer_tracking</a> - warning</h2></dt> 787<dd> 788Serializing an object of a type marked "track_never" through a pointer 789could result in creating more objects than were saved! There are cases 790in which a user might really want to do this so we leave it as a warning. 791</dd> 792 793<dt><h2><a name="const_loading">const_loading</a> - error</h2></dt> 794<dd> 795One cannot load data into a "const" object unless it's a 796wrapper around some other non-const object. 797</dd> 798</dl> 799 800<hr> 801<p><i>© Copyright <a href="http://www.rrsd.com">Robert Ramey</a> 2002-2004 and Matthias Troyer 2006. 802Distributed under the Boost Software License, Version 1.0. (See 803accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 804</i></p> 805</body> 806</html> 807