tutorial.html 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  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 - Tutorial</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">Tutorial</h2>
  24. </td>
  25. </tr>
  26. </table>
  27. <hr>
  28. <dl class="page-index">
  29. <dt><a href="#simplecase">A Very Simple Case</a>
  30. <dt><a href="#nonintrusiveversion">Non Intrusive Version</a>
  31. <dt><a href="#serializablemembers">Serializable Members</a>
  32. <dt><a href="#derivedclasses">Derived Classes</a>
  33. <dt><a href="#pointers">Pointers</a>
  34. <dt><a href="#arrays">Arrays</a>
  35. <dt><a href="#stl">STL Collections</a>
  36. <dt><a href="#versioning">Class Versioning</a>
  37. <dt><a href="#splitting">Splitting <code style="white-space: normal">serialize</code> into <code style="white-space: normal">save/load</code></a>
  38. <dt><a href="#archives">Archives</a>
  39. <dt><a href="#examples">List of examples</a>
  40. </dl>
  41. An output archive is similar to an output data stream. Data can be saved to the archive
  42. with either the &lt;&lt; or the &amp; operator:
  43. <pre><code>
  44. ar &lt;&lt; data;
  45. ar &amp; data;
  46. </code></pre>
  47. An input archive is similar to an input datastream. Data can be loaded from the archive
  48. with either the &gt;&gt; or the &amp; operator.
  49. <pre><code>
  50. ar &gt;&gt; data;
  51. ar &amp; data;
  52. </code></pre>
  53. <p>
  54. When these operators are invoked for primitive data types, the data is simply saved/loaded
  55. to/from the archive. When invoked for class data types, the class
  56. <code style="white-space: normal">serialize</code> function is invoked. Each
  57. <code style="white-space: normal">serialize</code> function is uses the above operators
  58. to save/load its data members. This process will continue in a recursive manner until
  59. all the data contained in the class is saved/loaded.
  60. <h3><a name="simplecase">A Very Simple Case</a></h3>
  61. These operators are used inside the <code style="white-space: normal">serialize</code>
  62. function to save and load class data members.
  63. <p>
  64. Included in this library is a program called
  65. <a href="../example/demo.cpp" target="demo_cpp">demo.cpp</a> which illustrates how
  66. to use this system. Below we excerpt code from this program to
  67. illustrate with the simplest possible case how this library is
  68. intended to be used.
  69. <pre>
  70. <code>
  71. #include &lt;fstream&gt;
  72. // include headers that implement a archive in simple text format
  73. #include &lt;boost/archive/text_oarchive.hpp&gt;
  74. #include &lt;boost/archive/text_iarchive.hpp&gt;
  75. /////////////////////////////////////////////////////////////
  76. // gps coordinate
  77. //
  78. // illustrates serialization for a simple type
  79. //
  80. class gps_position
  81. {
  82. private:
  83. friend class boost::serialization::access;
  84. // When the class Archive corresponds to an output archive, the
  85. // &amp; operator is defined similar to &lt;&lt;. Likewise, when the class Archive
  86. // is a type of input archive the &amp; operator is defined similar to &gt;&gt;.
  87. template&lt;class Archive&gt;
  88. void serialize(Archive &amp; ar, const unsigned int version)
  89. {
  90. ar &amp; degrees;
  91. ar &amp; minutes;
  92. ar &amp; seconds;
  93. }
  94. int degrees;
  95. int minutes;
  96. float seconds;
  97. public:
  98. gps_position(){};
  99. gps_position(int d, int m, float s) :
  100. degrees(d), minutes(m), seconds(s)
  101. {}
  102. };
  103. int main() {
  104. // create and open a character archive for output
  105. std::ofstream ofs("filename");
  106. // create class instance
  107. const gps_position g(35, 59, 24.567f);
  108. // save data to archive
  109. {
  110. boost::archive::text_oarchive oa(ofs);
  111. // write class instance to archive
  112. oa &lt;&lt; g;
  113. // archive and stream closed when destructors are called
  114. }
  115. // ... some time later restore the class instance to its orginal state
  116. gps_position newg;
  117. {
  118. // create and open an archive for input
  119. std::ifstream ifs("filename");
  120. boost::archive::text_iarchive ia(ifs);
  121. // read class state from archive
  122. ia &gt;&gt; newg;
  123. // archive and stream closed when destructors are called
  124. }
  125. return 0;
  126. }
  127. </code>
  128. </pre>
  129. <p>For each class to be saved via serialization, there must exist a function to
  130. save all the class members which define the state of the class.
  131. For each class to be loaded via serialization, there must exist a function to
  132. load theese class members in the same sequence as they were saved.
  133. In the above example, these functions are generated by the
  134. template member function <code style="white-space: normal">serialize</code>.
  135. <h3><a name="nonintrusiveversion">Non Intrusive Version</a></h3>
  136. <p>The above formulation is intrusive. That is, it requires
  137. that classes whose instances are to be serialized be
  138. altered. This can be inconvenient in some cases.
  139. An equivalent alternative formulation permitted by the
  140. system would be:
  141. <pre><code>
  142. #include &lt;boost/archive/text_oarchive.hpp&gt;
  143. #include &lt;boost/archive/text_iarchive.hpp&gt;
  144. class gps_position
  145. {
  146. public:
  147. int degrees;
  148. int minutes;
  149. float seconds;
  150. gps_position(){};
  151. gps_position(int d, int m, float s) :
  152. degrees(d), minutes(m), seconds(s)
  153. {}
  154. };
  155. namespace boost {
  156. namespace serialization {
  157. template&lt;class Archive&gt;
  158. void serialize(Archive &amp; ar, gps_position &amp; g, const unsigned int version)
  159. {
  160. ar &amp; g.degrees;
  161. ar &amp; g.minutes;
  162. ar &amp; g.seconds;
  163. }
  164. } // namespace serialization
  165. } // namespace boost
  166. </code></pre>
  167. <p>
  168. In this case the generated serialize functions are not members of the
  169. <code style="white-space: normal">gps_position</code> class. The two formulations function
  170. in exactly the same way.
  171. <p>
  172. The main application of non-intrusive serialization is to permit serialization
  173. to be implemented for classes without changing the class definition.
  174. In order for this to be possible, the class must expose enough information
  175. to reconstruct the class state. In this example, we presumed that the
  176. class had <code style="white-space: normal">public</code> members - not a common occurence. Only
  177. classes which expose enough information to save and restore the class
  178. state will be serializable without changing the class definition.
  179. <h3><a name="serializablemembers">Serializable Members</a></h3>
  180. <p>
  181. A serializable class with serializable members would look like this:
  182. <pre><code>
  183. class bus_stop
  184. {
  185. friend class boost::serialization::access;
  186. template&lt;class Archive&gt;
  187. void serialize(Archive &amp; ar, const unsigned int version)
  188. {
  189. ar &amp; latitude;
  190. ar &amp; longitude;
  191. }
  192. gps_position latitude;
  193. gps_position longitude;
  194. protected:
  195. bus_stop(const gps_position &amp; lat_, const gps_position &amp; long_) :
  196. latitude(lat_), longitude(long_)
  197. {}
  198. public:
  199. bus_stop(){}
  200. // See item # 14 in Effective C++ by Scott Meyers.
  201. // re non-virtual destructors in base classes.
  202. virtual ~bus_stop(){}
  203. };
  204. </code></pre>
  205. <p>That is, members of class type are serialized just as
  206. members of primitive types are.
  207. <p>
  208. Note that saving an instance of the class <code style="white-space: normal">bus_stop</code>
  209. with one of the archive operators will invoke the
  210. <code style="white-space: normal">serialize</code> function which saves
  211. <code style="white-space: normal">latitude</code> and
  212. <code style="white-space: normal">longitude</code>. Each of these in turn will be saved by invoking
  213. <code style="white-space: normal">serialize</code> in the definition of
  214. <code style="white-space: normal">gps_position</code>. In this manner the whole
  215. data structure is saved by the application of an archive operator to
  216. just its root item.
  217. <h3><a name="derivedclasses">Derived Classes</a></h3>
  218. <p>Derived classes should include serializations of their base classes.
  219. <pre><code>
  220. #include &lt;boost/serialization/base_object.hpp&gt;
  221. class bus_stop_corner : public bus_stop
  222. {
  223. friend class boost::serialization::access;
  224. template&lt;class Archive&gt;
  225. void serialize(Archive &amp; ar, const unsigned int version)
  226. {
  227. // serialize base class information
  228. ar &amp; boost::serialization::base_object&lt;bus_stop&gt;(*this);
  229. ar &amp; street1;
  230. ar &amp; street2;
  231. }
  232. std::string street1;
  233. std::string street2;
  234. virtual std::string description() const
  235. {
  236. return street1 + " and " + street2;
  237. }
  238. public:
  239. bus_stop_corner(){}
  240. bus_stop_corner(const gps_position &amp; lat_, const gps_position &amp; long_,
  241. const std::string &amp; s1_, const std::string &amp; s2_
  242. ) :
  243. bus_stop(lat_, long_), street1(s1_), street2(s2_)
  244. {}
  245. };
  246. </code>
  247. </pre>
  248. <p>
  249. Note the serialization of the base classes from the derived
  250. class. Do <b>NOT</b> directly call the base class serialize
  251. functions. Doing so might seem to work but will bypass the code
  252. that tracks instances written to storage to eliminate redundancies.
  253. It will also bypass the writing of class version information into
  254. the archive. For this reason, it is advisable to always make member
  255. <code style="white-space: normal">serialize</code> functions private. The declaration
  256. <code style="white-space: normal">friend boost::serialization::access</code> will grant to the
  257. serialization library access to private member variables and functions.
  258. <p>
  259. <h3><a name="pointers">Pointers</a></h3>
  260. Suppose we define a bus route as an array of bus stops. Given that
  261. <ol>
  262. <li>we might have several types of bus stops (remember bus_stop is
  263. a base class)
  264. <li>a given bus_stop might appear in more than one route.
  265. </ol>
  266. it's convenient to represent a bus route with an array of pointers
  267. to <code style="white-space: normal">bus_stop</code>.
  268. <pre>
  269. <code>
  270. class bus_route
  271. {
  272. friend class boost::serialization::access;
  273. bus_stop * stops[10];
  274. template&lt;class Archive&gt;
  275. void serialize(Archive &amp; ar, const unsigned int version)
  276. {
  277. int i;
  278. for(i = 0; i &lt 10; ++i)
  279. ar &amp; stops[i];
  280. }
  281. public:
  282. bus_route(){}
  283. };
  284. </code>
  285. </pre>
  286. Each member of the array <code style="white-space: normal">stops</code> will be serialized.
  287. But remember each member is a pointer - so what can this really
  288. mean? The whole object of this serialization is to permit
  289. reconstruction of the original data structures at another place
  290. and time. In order to accomplish this with a pointer, it is
  291. not sufficient to save the value of the pointer, rather the
  292. object it points to must be saved. When the member is later
  293. loaded, a new object has to be created and a new pointer has
  294. to be loaded into the class member.
  295. <p>
  296. If the same pointer is serialized more than once, only one instance
  297. is be added to the archive. When read back, no data is read back in.
  298. The only operation that occurs is for the second pointer is set equal to the first
  299. <p>
  300. Note that, in this example, the array consists of polymorphic pointers.
  301. That is, each array element point to one of several possible
  302. kinds of bus stops. So when the pointer is saved, some sort of class
  303. identifier must be saved. When the pointer is loaded, the class
  304. identifier must be read and and instance of the corresponding class
  305. must be constructed. Finally the data can be loaded to newly created
  306. instance of the correct type.
  307. As can be seen in
  308. <a href="../example/demo.cpp" target="demo_cpp">demo.cpp</a>,
  309. serialization of pointers to derived classes through a base
  310. clas pointer may require explicit enumeration of the derived
  311. classes to be serialized. This is referred to as "registration" or "export"
  312. of derived classes. This requirement and the methods of
  313. satisfying it are explained in detail
  314. <a href="serialization.html#derivedpointers">here</a>.
  315. <p>
  316. All this is accomplished automatically by the serialization
  317. library. The above code is all that is necessary to accomplish
  318. the saving and loading of objects accessed through pointers.
  319. <p>
  320. <h3><a name="arrays">Arrays</a></h3>
  321. The above formulation is in fact more complex than necessary.
  322. The serialization library detects when the object being
  323. serialized is an array and emits code equivalent to the above.
  324. So the above can be shortened to:
  325. <pre>
  326. <code>
  327. class bus_route
  328. {
  329. friend class boost::serialization::access;
  330. bus_stop * stops[10];
  331. template&lt;class Archive&gt;
  332. void serialize(Archive &amp; ar, const unsigned int version)
  333. {
  334. ar &amp; stops;
  335. }
  336. public:
  337. bus_route(){}
  338. };
  339. </code>
  340. </pre>
  341. <h3><a name="stl">STL Collections</a></h3>
  342. The above example uses an array of members. More likely such
  343. an application would use an STL collection for such a purpose.
  344. The serialization library contains code for serialization
  345. of all STL classes. Hence, the reformulation below will
  346. also work as one would expect.
  347. <pre>
  348. <code>
  349. #include &lt;boost/serialization/list.hpp&gt;
  350. class bus_route
  351. {
  352. friend class boost::serialization::access;
  353. std::list&lt;bus_stop *&gt; stops;
  354. template&lt;class Archive&gt;
  355. void serialize(Archive &amp; ar, const unsigned int version)
  356. {
  357. ar &amp; stops;
  358. }
  359. public:
  360. bus_route(){}
  361. };
  362. </code>
  363. </pre>
  364. <h3><a name="versioning">Class Versioning</a></h3>
  365. <p>
  366. Suppose we're satisfied with our <code style="white-space: normal">bus_route</code> class, build a program
  367. that uses it and ship the product. Some time later, it's decided
  368. that the program needs enhancement and the <code style="white-space: normal">bus_route</code> class is
  369. altered to include the name of the driver of the route. So the
  370. new version looks like:
  371. <pre>
  372. <code>
  373. #include &lt;boost/serialization/list.hpp&gt;
  374. #include &lt;boost/serialization/string.hpp&gt;
  375. class bus_route
  376. {
  377. friend class boost::serialization::access;
  378. std::list&lt;bus_stop *&gt; stops;
  379. std::string driver_name;
  380. template&lt;class Archive&gt;
  381. void serialize(Archive &amp; ar, const unsigned int version)
  382. {
  383. ar &amp; driver_name;
  384. ar &amp; stops;
  385. }
  386. public:
  387. bus_route(){}
  388. };
  389. </code>
  390. </pre>
  391. Great, we're all done. Except... what about people using our application
  392. who now have a bunch of files created under the previous program.
  393. How can these be used with our new program version?
  394. <p>
  395. In general, the serialization library stores a version number in the
  396. archive for each class serialized. By default this version number is 0.
  397. When the archive is loaded, the version number under which it was saved
  398. is read. The above code can be altered to exploit this
  399. <pre>
  400. <code>
  401. #include &lt;boost/serialization/list.hpp&gt;
  402. #include &lt;boost/serialization/string.hpp&gt;
  403. #include &lt;boost/serialization/version.hpp&gt;
  404. class bus_route
  405. {
  406. friend class boost::serialization::access;
  407. std::list&lt;bus_stop *&gt; stops;
  408. std::string driver_name;
  409. template&lt;class Archive&gt;
  410. void serialize(Archive &amp; ar, const unsigned int version)
  411. {
  412. // only save/load driver_name for newer archives
  413. if(version &gt; 0)
  414. ar &amp; driver_name;
  415. ar &amp; stops;
  416. }
  417. public:
  418. bus_route(){}
  419. };
  420. BOOST_CLASS_VERSION(bus_route, 1)
  421. </code>
  422. </pre>
  423. By application of versioning to each class, there is no need to
  424. try to maintain a versioning of files. That is, a file version
  425. is the combination of the versions of all its constituent classes.
  426. This system permits programs to be always compatible with archives
  427. created by all previous versions of a program with no more
  428. effort than required by this example.
  429. <h3><a name="splitting">Splitting <code style="white-space: normal">serialize</code>
  430. into <code style="white-space: normal">save/load</code></a></h3>
  431. The <code style="white-space: normal">serialize</code> function is simple, concise, and guarantees
  432. that class members are saved and loaded in the same sequence
  433. - the key to the serialization system. However, there are cases
  434. where the load and save operations are not as similar as the examples
  435. used here. For example, this could occur with a class that has evolved through
  436. multiple versions. The above class can be reformulated as:
  437. <pre>
  438. <code>
  439. #include &lt;boost/serialization/list.hpp&gt;
  440. #include &lt;boost/serialization/string.hpp&gt;
  441. #include &lt;boost/serialization/version.hpp&gt;
  442. #include &lt;boost/serialization/split_member.hpp&gt;
  443. class bus_route
  444. {
  445. friend class boost::serialization::access;
  446. std::list&lt;bus_stop *&gt; stops;
  447. std::string driver_name;
  448. template&lt;class Archive&gt;
  449. void save(Archive &amp; ar, const unsigned int version) const
  450. {
  451. // note, version is always the latest when saving
  452. ar &amp; driver_name;
  453. ar &amp; stops;
  454. }
  455. template&lt;class Archive&gt;
  456. void load(Archive &amp; ar, const unsigned int version)
  457. {
  458. if(version &gt; 0)
  459. ar &amp; driver_name;
  460. ar &amp; stops;
  461. }
  462. BOOST_SERIALIZATION_SPLIT_MEMBER()
  463. public:
  464. bus_route(){}
  465. };
  466. BOOST_CLASS_VERSION(bus_route, 1)
  467. </code>
  468. </pre>
  469. The macro <code style="white-space: normal">BOOST_SERIALIZATION_SPLIT_MEMBER()</code> generates
  470. code which invokes the <code style="white-space: normal">save</code>
  471. or <code style="white-space: normal">load</code>
  472. depending on whether the archive is used for saving or loading.
  473. <h3><a name="archives">Archives</a></h3>
  474. Our discussion here has focused on adding serialization
  475. capability to classes. The actual rendering of the data to be serialized
  476. is implemented in the archive class. Thus the stream of serialized
  477. data is a product of the serialization of the class and the
  478. archive selected. It is a key design decision that these two
  479. components be independent. This permits any serialization specification
  480. to be usable with any archive.
  481. <p>
  482. In this tutorial, we have used a particular
  483. archive class - <code style="white-space: normal">text_oarchive</code> for saving and
  484. <code style="white-space: normal">text_iarchive</code> for loading.
  485. text archives render data as text and are portable across platforms. In addition
  486. to text archives, the library includes archive class for native binary data
  487. and xml formatted data. Interfaces to all archive classes are all identical.
  488. Once serialization has been defined for a class, that class can be serialized to
  489. any type of archive.
  490. <p>
  491. If the current set of archive classes doesn't provide the
  492. attributes, format, or behavior needed for a particular application,
  493. one can either make a new archive class or derive from an existing one.
  494. This is described later in the manual.
  495. <h3><a name="examples">List of Examples</h3>
  496. <dl>
  497. <dt><a href="../example/demo.cpp" target="demo_cpp">demo.cpp</a>
  498. <dd>This is the completed example used in this tutorial.
  499. It does the following:
  500. <ol>
  501. <li>Creates a structure of differing kinds of stops, routes and schedules
  502. <li>Displays it
  503. <li>Serializes it to a file named "testfile.txt" with one
  504. statement
  505. <li>Restores to another structure
  506. <li>Displays the restored structure
  507. </ol>
  508. <a href="../example/demo_output.txt" target="demo_output">Output of
  509. this program</a> is sufficient to verify that all the
  510. originally stated requirements for a serialization system
  511. are met with this system. The <a href="../example/demofile.txt"
  512. target="test_file">contents of the archive file</a> can
  513. also be displayed as serialization files are ASCII text.
  514. <dt><a href="../example/demo_xml.cpp" target="demo_xml_cpp">demo_xml.cpp</a>
  515. <dd>This is a variation the original demo which supports xml archives in addition
  516. to the others. The extra wrapping macro, BOOST_SERIALIZATION_NVP(name), is
  517. needed to associate a data item name with the corresponding xml
  518. tag. It is importanted that 'name' be a valid xml tag, else it
  519. will be impossible to restore the archive.
  520. For more information see
  521. <a target="detail" href="wrappers.html#nvp">Name-Value Pairs</a>.
  522. <a href="../example/demo_save.xml" target="demo_save_xml">Here</a>
  523. is what an xml archive looks like.
  524. <dt><a href="../example/demo_xml_save.cpp" target="demo_xml_save_cpp">demo_xml_save.cpp</a>
  525. and <a href="../example/demo_xml_load.cpp" target="demo_xml_load_cpp">demo_xml_load.cpp</a>
  526. <dd>Note also that though our examples save and load the program data
  527. to an archive within the same program, this merely a convenience
  528. for purposes of illustration. In general, the archive may or may
  529. not be loaded by the same program that created it.
  530. </dl>
  531. <p>
  532. The astute reader might notice that these examples contain a subtle but important flaw.
  533. They leak memory. The bus stops are created in the <code style="white-space: normal">
  534. main</code> function. The bus schedules may refer to these bus stops
  535. any number of times. At the end of the main function after the bus schedules are destroyed,
  536. the bus stops are destroyed. This seems fine. But what about the structure
  537. <code style="white-space: normal">new_schedule</code> data item created by the
  538. process of loading from an archive? This contains its own separate set of bus stops
  539. that are not referenced outside of the bus schedule. These won't be destroyed
  540. anywhere in the program - a memory leak.
  541. <p>
  542. There are couple of ways of fixing this. One way is to explicitly manage the bus stops.
  543. However, a more robust and transparent is to use
  544. <code style="white-space: normal">shared_ptr</code> rather than raw pointers. Along
  545. with serialization implementations for the Standard Library, the serialization library
  546. includes implementation of serialization for
  547. <code style="white-space: normal">boost::shared ptr</code>. Given this, it should be
  548. easy to alter any of these examples to eliminate the memory leak. This is left
  549. as an excercise for the reader.
  550. <hr>
  551. <p><i>&copy; Copyright <a href="http://www.rrsd.com">Robert Ramey</a> 2002-2004.
  552. Distributed under the Boost Software License, Version 1.0. (See
  553. accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  554. </i></p>
  555. </body>
  556. </html>