Brian Silverman | 8867871 | 2018-08-04 23:56:48 -0700 | [diff] [blame^] | 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 . |
| 5 | Use, modification and distribution is subject to the Boost Software |
| 6 | License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
| 7 | http://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> |
| 40 | Serialization of data depends on the type of the data. For example, for |
| 41 | primitive types such as <code style="white-space: normal">int</code>, it wouldn't make sense to save |
| 42 | a version number in the archive. Likewise, for a data type that is never |
| 43 | serialized through a pointer, it would (almost) never make sense to track |
| 44 | the address of objects saved to/loaded from the archive as it will never |
| 45 | be saved/loaded more than once in any case. Details of |
| 46 | serialization for a particular data type will vary depending on the |
| 47 | type, the way it is used and specifications of the programmer. |
| 48 | <p> |
| 49 | One can alter the manner in which a particular data type is serialized |
| 50 | by specifying one or more <strong>class serialization traits</strong>. |
| 51 | It is not generally necessary for the programmer to explictly assign |
| 52 | traits to his classes as there are default values for all traits. |
| 53 | If the default values are not appropriate they can be assigned by the programmer. |
| 54 | A template is used to associate a typename with a constant. For example |
| 55 | see <a href="../../../boost/serialization/version.hpp" target="version_hpp"> |
| 56 | version.hpp</a>. |
| 57 | <h3><a name="version">Version</a></h3> |
| 58 | This header file includes the following code: |
| 59 | |
| 60 | <pre><code> |
| 61 | namespace boost { |
| 62 | namespace serialization { |
| 63 | template<class T> |
| 64 | struct version |
| 65 | { |
| 66 | BOOST_STATIC_CONSTANT(unsigned int, value = 0); |
| 67 | }; |
| 68 | } // namespace serialization |
| 69 | } // namespace boost |
| 70 | </code></pre> |
| 71 | |
| 72 | For any class <code style="white-space: normal">T</code>, The default definition |
| 73 | of <code style="white-space: normal">boost::serialization::version<T>::value</code> is 0. |
| 74 | If we want to assign a value of 2 as the version for class <code style="white-space: normal">my_class</code> |
| 75 | we specialize the version template: |
| 76 | <pre><code> |
| 77 | namespace boost { |
| 78 | namespace serialization { |
| 79 | struct version<my_class> |
| 80 | { |
| 81 | BOOST_STATIC_CONSTANT(unsigned int, value = 2); |
| 82 | }; |
| 83 | } // namespace serialization |
| 84 | } // namespace boost |
| 85 | </code></pre> |
| 86 | Now whenever the version number for class <code style="white-space: normal">my_class</code> is required, |
| 87 | the value 2 will be returned rather than the default value of 0. |
| 88 | <p> |
| 89 | To diminish typing and enhance readability, a macro is defined |
| 90 | so that instead of the above, we could write: |
| 91 | <pre><code> |
| 92 | BOOST_CLASS_VERSION(my_class, 2) |
| 93 | </code></pre> |
| 94 | which expands to the code above. |
| 95 | |
| 96 | <h3><a name="level">Implementation Level</a></h3> |
| 97 | In the same manner as the above, the "level" of implementation of serialization is |
| 98 | specified. The header file <a href="../../../boost/serialization/level.hpp" |
| 99 | target="level_hpp">level.hpp</a> defines the following. |
| 100 | <pre><code> |
| 101 | // names for each level |
| 102 | enum 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> |
| 128 | Using a macro defined in <code style="white-space: normal">level.hpp</code> we can specify |
| 129 | that <code style="white-space: normal">my_class</code> should be serialized along with its version number: |
| 130 | <pre><code> |
| 131 | BOOST_CLASS_IMPLEMENTATION(my_class, boost::serialization::object_class_info) |
| 132 | </code></pre> |
| 133 | If implementation level is not explicitly assigned, the system uses |
| 134 | a default according to the following rules. |
| 135 | <ul> |
| 136 | <li>if the data type is <code style="white-space: normal">volatile</code> |
| 137 | assign <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> |
| 141 | That is, for most user defined types, objects will be serialized along with |
| 142 | class version information. This will permit one to maintain backward |
| 143 | compatibility with archives which contain previous versions. However, with this |
| 144 | ability comes a small runtime cost. For types whose definition will "never" |
| 145 | change, efficiency can be gained by specifying <code style="white-space: normal">object_serializable</code> |
| 146 | to override the default setting of <code style="white-space: normal">object_class_info</code>. |
| 147 | For example, |
| 148 | this has been done for the |
| 149 | <a href="../../../boost/serialization/binary_object.hpp" target="binary_object_hpp"> |
| 150 | binary_object wrapper</a> |
| 151 | |
| 152 | <h3><a name="tracking">Object Tracking</a></h3> |
| 153 | Depending on the way a type is used, it may be necessary or convenient to |
| 154 | track the address of objects saved and loaded. For example, this is generally |
| 155 | necessary while serializing objects through a pointer in order to be sure |
| 156 | that multiple identical objects are not created when an archive is loaded. |
| 157 | This "tracking behavior" is controlled by the type trait defined in the header |
| 158 | file <a href="../../../boost/serialization/tracking.hpp" target="tracking_hpp">tracking.hpp</a> |
| 159 | which defines the following: |
| 160 | <pre><code> |
| 161 | // names for each tracking level |
| 162 | enum 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> |
| 173 | A corresponding macro is defined so that we can use: |
| 174 | <pre><code> |
| 175 | BOOST_CLASS_TRACKING(my_class, boost::serialization::track_never) |
| 176 | </code></pre> |
| 177 | Default 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> |
| 196 | The default behavior is almost always the most convenient one. However, |
| 197 | there a few cases where it would be desirable to override the |
| 198 | default. One case is that of a virtual base class. In a diamond |
| 199 | heritance structure with a virtual base class, object tracking |
| 200 | will prevent redundant save/load invocations. So here is one |
| 201 | case where it might be convenient to override the default tracking |
| 202 | trait. <i>(Note: in a future version the default will be reimplemented |
| 203 | to automatically track classes used as virtual bases).</i> This |
| 204 | situation is demonstrated by |
| 205 | <a href="../test/test_diamond.cpp" target="test_diamond_cpp">test_diamond.cpp</a> |
| 206 | included with the library. |
| 207 | |
| 208 | <h3><a name="export">Export Key</a></h3> |
| 209 | |
| 210 | When serializing a derived class through a virtual base class pointer, |
| 211 | two issues may arise. |
| 212 | <ul> |
| 213 | <li> The code in the derived class might never be explicitly |
| 214 | referred to. Such code will never be instantiated. |
| 215 | <p> |
| 216 | This is addressed by invoking |
| 217 | <code style="white-space: normal">BOOST_CLASS_EXPORT_IMPLEMENT(T)</code> |
| 218 | in the file which defines (implements) the class T. |
| 219 | This ensures that code for the derived class T will |
| 220 | be explicity instantiated. |
| 221 | <li> There needs to be some sort of identifier which can |
| 222 | be used to select the code to be invoked when the object |
| 223 | is loaded. |
| 224 | Standard C++ does implement <code style="white-space: normal">typeid()</code> which can be |
| 225 | used to return a unique string for the class. This is not entirely |
| 226 | statisfactory 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> |
| 238 | So 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> |
| 240 | in the header file which declares the class. |
| 241 | In a large majority of applications, the class name works just fine |
| 242 | for the external identifier string so the following short cut is |
| 243 | defined - |
| 244 | <code style="white-space: normal">BOOST_CLASS_EXPORT_KEY(my_class)</code>. |
| 245 | </ul> |
| 246 | For programs which consist of only one module - that is |
| 247 | programs which do not use DLLS, one can specify |
| 248 | <code style="white-space: normal">BOOST_CLASS_EXPORT(my_class)</code> |
| 249 | or |
| 250 | <code style="white-space: normal">BOOST_CLASS_EXPORT_GUID(my_class, "my_class_external_identifier")</code> |
| 251 | in either the declaration header or definition. These macros |
| 252 | expand 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> |
| 256 | in this manual, the serialization of derived classes is addressed in detail.)</i> |
| 257 | <p> |
| 258 | The header file |
| 259 | <a href="../../../boost/serialization/export.hpp" target="export_hpp">export.hpp</a> |
| 260 | contains all macro definitions described here. |
| 261 | The 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 |
| 265 | in more than one module (or DLL). |
| 266 | </ul> |
| 267 | |
| 268 | <h3><a name="abstract">Abstract</a></h3> |
| 269 | When serializing an object through a pointer to its base class, |
| 270 | the library needs to determine whether or not the base is abstract |
| 271 | (i.e. has at least one virtual function). The library uses the |
| 272 | type trait macro <code style="white-space: normal">BOOST_IS_ABSTRACT(T)</code> |
| 273 | to do this. Not all compilers support this type trait and corresponding |
| 274 | macro. To address this, the macro <code style="white-space: normal"> |
| 275 | BOOST_SERIALIZATION_ASSUME_ABSTRACT(T)</code> has been |
| 276 | implemented to permit one to explicitly indicate that a specified |
| 277 | type is in fact abstract. This will guarentee that |
| 278 | <code style="white-space: normal">BOOST_IS_ABSTRACT</code> |
| 279 | will return the correct value for all compilers. |
| 280 | |
| 281 | <h3><a name="typeinfo">Type Information Implementation</a></h3> |
| 282 | This last trait is also related to the serialization of objects |
| 283 | through a base class pointer. The implementation of this facility |
| 284 | requires the ability to determine at run time the true type of the |
| 285 | object that a base class pointer points to. Different serialization |
| 286 | systems do this in different ways. In our system, the default method |
| 287 | is to use the function <code style="white-space: normal">typeid(...)</code> which is available |
| 288 | in systems which support <b>RTTI</b> (<b>R</b>un <b>T</b>ime |
| 289 | <b>T</b>ype <b>I</b>nformation). |
| 290 | This will be satisfactory in almost all cases and most users of this |
| 291 | library will lose nothing in skipping this section of the manual. |
| 292 | <p> |
| 293 | However, there are some cases where the default type determination |
| 294 | system is not convenient. Some platforms might not support |
| 295 | RTTI or it may have been disabled in order to speed execution |
| 296 | or for some other reason. Some applications, E.G. runtime linking |
| 297 | of plug-in modules, can't depend on C++ RTTI to determine the |
| 298 | true derived class. RTTI only returns the correct type for polymorphic |
| 299 | classes - classes with at least one virtual function. If any of these |
| 300 | situations applies, one may substitute his own implementation of |
| 301 | <code style="white-space: normal">extended_type_info</code> |
| 302 | <p> |
| 303 | The interface to facilities required to implement serialization is defined in |
| 304 | <a href="../../../boost/serialization/extended_type_info.hpp" |
| 305 | target="extended_type_info_hpp">extended_type_info.hpp</a>. |
| 306 | |
| 307 | Default implementation of these facilities based on <code style="white-space: normal">typeid(...)</code> |
| 308 | is defined in |
| 309 | |
| 310 | <a href="../../../boost/serialization/extended_type_info_typeid.hpp" |
| 311 | target="extended_type_info_typeid_hpp">extended_type_info_typeid.hpp</a>. |
| 312 | |
| 313 | An alternative implementation based on exported class identifiers |
| 314 | is defined in |
| 315 | <a href="../../../boost/serialization/extended_type_info_no_rtti.hpp" |
| 316 | target="extended_type_info_rtti_hpp">extended_type_info_no_rtti.hpp</a>. |
| 317 | <p> |
| 318 | By invoking the macro: |
| 319 | <pre><code> |
| 320 | BOOST_CLASS_TYPE_INFO( |
| 321 | my_class, |
| 322 | extended_type_info_no_rtti<my_class> |
| 323 | ) |
| 324 | </code></pre> |
| 325 | we can assign the type information implementation to each class on a case by |
| 326 | case basis. There is no requirement that all classes in a program use the same |
| 327 | implementation of <code style="white-space: normal">extended_type_info</code>. This supports the concept |
| 328 | that serialization of each class is specified "once and for all" in a header |
| 329 | file that can be included in any project without change. |
| 330 | <p> |
| 331 | This is illustrated by the test program |
| 332 | <a href="../test/test_no_rtti.cpp" target="test_no_rtti_cpp">test_no_rtti.cpp</a>. |
| 333 | Other implementations are possible and might be necessary for |
| 334 | certain special cases. |
| 335 | |
| 336 | <h3><a name="wrappers">Wrappers</a></h3> |
| 337 | Archives need to treat wrappers differently from other types since, for example, |
| 338 | they usually are non-const objects while output archives require that any |
| 339 | serialized object (with the exception of a wrapper) be const. |
| 340 | |
| 341 | This header file <a href="../../../boost/serialization/wrapper.hpp">wrapper.hpp</a> |
| 342 | includes the following code: |
| 343 | |
| 344 | <pre><code> |
| 345 | namespace boost { |
| 346 | namespace serialization { |
| 347 | template<class T> |
| 348 | struct is_wrapper |
| 349 | : public mpl::false_ |
| 350 | {}; |
| 351 | } // namespace serialization |
| 352 | } // namespace boost |
| 353 | </code></pre> |
| 354 | |
| 355 | For any class <code style="white-space: normal">T</code>, The default definition |
| 356 | of <code style="white-space: normal">boost::serialization::is_wrapper<T>::value</code> is thus false. |
| 357 | |
| 358 | If we want to declare that a class <code style="white-space: normal">my_class</code> |
| 359 | is a wrapper we specialize the version template: |
| 360 | <pre><code> |
| 361 | namespace boost { |
| 362 | namespace serialization { |
| 363 | struct is_wrapper<my_class> |
| 364 | : mpl::true_ |
| 365 | {}; |
| 366 | } // namespace serialization |
| 367 | } // namespace boost |
| 368 | </code></pre> |
| 369 | <p> |
| 370 | To diminish typing and enhance readability, a macro is defined |
| 371 | so that instead of the above, we could write: |
| 372 | <pre><code> |
| 373 | BOOST_CLASS_IS_WRAPPER(my_class) |
| 374 | </code></pre> |
| 375 | which expands to the code above. |
| 376 | |
| 377 | <h3><a name="bitwise">Bitwise Serialization</a></h3> |
| 378 | Some simple classes could be serialized just by directly copying all bits |
| 379 | of the class. This is, in particular, the case for POD data types containing |
| 380 | no pointer members, and which are neither versioned nor tracked. Some archives, |
| 381 | such as non-portable binary archives can make us of this information to |
| 382 | substantially speed up serialization. |
| 383 | |
| 384 | To indicate the possibility of bitwise serialization the type trait defined |
| 385 | in the header |
| 386 | file <a href="../../../boost/serialization/is_bitwise_serializable.hpp" target="is_bitwise_serializable">is_bitwise_serializable.hpp</a> |
| 387 | is used: |
| 388 | <pre><code> |
| 389 | namespace boost { namespace serialization { |
| 390 | template<class T> |
| 391 | struct is_bitwise_serializable |
| 392 | : public is_arithmetic<T> |
| 393 | {}; |
| 394 | } } |
| 395 | </code></pre> |
| 396 | is used, and can be specialized for other classes. The specialization |
| 397 | is made easy by the corresponding macro: |
| 398 | <pre><code> |
| 399 | BOOST_IS_BITWISE_SERIALIZABLE(my_class) |
| 400 | </code></pre> |
| 401 | |
| 402 | <h3><a name="templates">Template Serialization Traits</a></h3> |
| 403 | In some instances it might be convenient to assign serialization traits |
| 404 | to a whole group of classes at once. Consider, the name-value pair |
| 405 | wrapper |
| 406 | <pre><code> |
| 407 | template<class T> |
| 408 | struct nvp : public std::pair<const char *, T *> |
| 409 | { |
| 410 | ... |
| 411 | }; |
| 412 | </code></pre> |
| 413 | used by XML archives to associate a name with a data variable of type T. |
| 414 | These data types are never tracked and never versioned. So one might |
| 415 | want to specify: |
| 416 | <pre><code> |
| 417 | BOOST_CLASS_IMPLEMENTATION(nvp<T>, boost::serialization::level_type::object_serializable) |
| 418 | BOOST_CLASS_TRACKING(nvp<T>, boost::serialization::track_never) |
| 419 | </code></pre> |
| 420 | Examination of the definition of these macros reveals that they won't expand |
| 421 | to sensible code when used with a template argument. So rather than using the |
| 422 | convenience macros, use the original definitions |
| 423 | <pre><code> |
| 424 | template<class T> |
| 425 | struct 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 |
| 436 | template<class T> |
| 437 | struct 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> |
| 447 | to assign serialization traits to all classes generated by the template |
| 448 | <code style="white-space: normal">nvp<T></code> |
| 449 | <p> |
| 450 | |
| 451 | Note that it is only possible to use the above method to assign traits to |
| 452 | templates when using compilers which correctly support Partial Template Specialization. |
| 453 | |
| 454 | One's first impulse might be to do something like: |
| 455 | |
| 456 | <pre><code> |
| 457 | #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION |
| 458 | template<class T> |
| 459 | struct implementation_level<nvp<T> > |
| 460 | { |
| 461 | ... // see above |
| 462 | }; |
| 463 | |
| 464 | // nvp objects are generally created on the stack and are never tracked |
| 465 | template<class T> |
| 466 | struct tracking_level<nvp<T> > |
| 467 | { |
| 468 | ... // see above |
| 469 | }; |
| 470 | #endif |
| 471 | </code></pre> |
| 472 | This can be problematic when one wants to make his code <strong>and archives</strong> |
| 473 | portable to other platforms. It means the objects will be serialized differently |
| 474 | depending on the platform used. This implies that objects saved from one platform |
| 475 | won't be loaded properly on another. In other words, archives won't be portable. |
| 476 | <p> |
| 477 | This problem is addressed by creating another method of assigning serialization traits |
| 478 | to 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> |
| 481 | Specifically, 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 |
| 484 | combination of serialization traits. |
| 485 | When looking up the serialization traits, the library first checks to see if this class has been |
| 486 | used as a base class. If so, the corresponding traits are used. Otherwise, the standard defaults |
| 487 | are used. By deriving from a serialization traits class rather than relying upon Partial Template |
| 488 | Specializaton, one can a apply serialization traits to a template and those traits will be |
| 489 | the same across all known platforms. |
| 490 | <p> |
| 491 | The signature for the traits template is: |
| 492 | <pre><code> |
| 493 | template< |
| 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 | > |
| 501 | struct traits |
| 502 | </code></pre> |
| 503 | and 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> |
| 516 | Some serialization traits can conflict with other ones. Sometimes these conflicts |
| 517 | will result in erroneous behavior (E.G. creating of archives which could not be read) |
| 518 | and other times they represent a probable misconception on the part of the |
| 519 | library user which could result in suprising behavior. To the extent possible, |
| 520 | these conflicts are detected at compile time and errors (BOOST_STATIC_ASSERT) |
| 521 | or warnings (BOOST_STATIC_WARNING) are generated. They are generated in a |
| 522 | compiler dependent manner which should show a chain of instantiation |
| 523 | to the point where the error/warning is detected. Without this capability, |
| 524 | it would be very hard to track down errors or unexpected behavior in library |
| 525 | usage. 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> |
| 530 | This error traps attempts to serialize types whose |
| 531 | implentation level is set to non_serializable. |
| 532 | </dd> |
| 533 | <dt><h2><a name="object_versioning">object_versioning</a> - error</h2></dt> |
| 534 | <dd> |
| 535 | It's possible that for efficiency reasons, a type can be |
| 536 | assigned a serialization level which doesn't include type information |
| 537 | in the archive. This would preclude the assignment |
| 538 | of a new version number to the type. This error |
| 539 | traps attempts to assign a version number in this case. |
| 540 | This has to be a user error. |
| 541 | </dd> |
| 542 | |
| 543 | <dt><h2><a name="object_tracking">object_tracking</a> - warning</h2></dt> |
| 544 | <dd> |
| 545 | The following code will display a message when compiled: |
| 546 | |
| 547 | <code style="white-space: normal"><pre> |
| 548 | T t; |
| 549 | ar << t; |
| 550 | </pre></code> |
| 551 | |
| 552 | unless the tracking_level serialization trait is set to "track_never". The following |
| 553 | will compile without problem: |
| 554 | |
| 555 | <code style="white-space: normal"><pre> |
| 556 | const T t |
| 557 | ar << t; |
| 558 | </pre></code> |
| 559 | |
| 560 | Likewise, the following code will trap at compile time: |
| 561 | |
| 562 | <code style="white-space: normal"><pre> |
| 563 | T * t; |
| 564 | ar >> t; |
| 565 | </pre></code> |
| 566 | |
| 567 | if the tracking_level serialization trait is set to "track_never". |
| 568 | <p> |
| 569 | |
| 570 | The following case illustrates the function of this message. |
| 571 | It was originally used as an example in the |
| 572 | mailing list by Peter Dimov. |
| 573 | |
| 574 | <code style="white-space: normal"><pre> |
| 575 | class construct_from |
| 576 | { |
| 577 | ... |
| 578 | }; |
| 579 | |
| 580 | void main(){ |
| 581 | ... |
| 582 | Y y; |
| 583 | construct_from x(y); |
| 584 | ar << x; |
| 585 | } |
| 586 | </pre></code> |
| 587 | |
| 588 | Suppose 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> |
| 596 | void 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> |
| 617 | class 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, |
| 634 | it'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> |
| 651 | BOOST_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> |
| 656 | is 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 |
| 658 | have its own distinct raw pointer. This will break |
| 659 | <code style="white-space: normal">shared_ptr</code> and cause a memory leak. Again, |
| 660 | The cause of this problem is very far removed from the point of discovery. It could |
| 661 | well be that the problem is not even discovered until after the archives are loaded. |
| 662 | Now we not only have a difficult to find and fix program bug, but we have a bunch of |
| 663 | invalid 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> |
| 672 | ar << 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> |
| 679 | Y y; |
| 680 | const construct_from x(y); |
| 681 | ar << 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> |
| 689 | Y y; |
| 690 | const construct_from x(y); |
| 691 | ... |
| 692 | x.f(); // change x in some way ; compile error f() is not const |
| 693 | ... |
| 694 | ar << 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> |
| 721 | BOOST_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> |
| 740 | Note 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 | |
| 748 | It's true that these messages may sometimes flag code that is currently correct and |
| 749 | that this may be annoying to some programmers. However, this example illustrates |
| 750 | my view that these messages are useful and that any such annoyance is a small price to |
| 751 | pay 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> |
| 757 | This trap addresses the following situaion when serializing |
| 758 | a pointer: |
| 759 | <ul> |
| 760 | <li>A type doesn't save class information in the |
| 761 | archive. That is, the serialization trait implementation |
| 762 | level <= object_serializable. |
| 763 | <li>Tracking for this type is set to "track selectively" |
| 764 | in this case, indication that an object is tracked is |
| 765 | not stored in the archive itself - see level == object_serializable. |
| 766 | Since class information is not saved in the archive, the existence |
| 767 | or absence of the operation ar << T * anywhere else in the |
| 768 | program is used to infer that an object of this type should be tracked. |
| 769 | <p> |
| 770 | A problem arises when a program which reads an archive |
| 771 | includes the operation ar >> T * so that tracking information |
| 772 | will be included in the archive. When a program which |
| 773 | creates the archive doesn't include ar << T it is presumed |
| 774 | that the archive doesn't include tracking information and |
| 775 | the archive will fail to load. Also the reverse situation could |
| 776 | trigger a similar problem. |
| 777 | <p> |
| 778 | Though this situation is unlikely for several reasones, |
| 779 | it is possible - hence this warning. |
| 780 | </ul> |
| 781 | So if your program traps here, consider changing the |
| 782 | tracking or implementation level traits - or not |
| 783 | serializing via a pointer. |
| 784 | </dd> |
| 785 | |
| 786 | <dt><h2><a name="pointer_tracking">pointer_tracking</a> - warning</h2></dt> |
| 787 | <dd> |
| 788 | Serializing an object of a type marked "track_never" through a pointer |
| 789 | could result in creating more objects than were saved! There are cases |
| 790 | in 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> |
| 795 | One cannot load data into a "const" object unless it's a |
| 796 | wrapper 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. |
| 802 | Distributed under the Boost Software License, Version 1.0. (See |
| 803 | accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| 804 | </i></p> |
| 805 | </body> |
| 806 | </html> |