    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/">
     <channel>
        <title>ACCU  :: Automatic Object Versioning for Forward and Backward File
Format Compatibility</title>
        <link>https://members.accu.org/index.php/journals/502</link>
        <description>Professionalism in Programming</description>
        <dc:language>en-us</dc:language> 
        <dc:creator>Administrator</dc:creator> 
        <admin:generatorAgent rdf:resource="http://www.xaraya.org" /> 
        <admin:errorReportsTo rdf:resource="mailto:webeditor@accu.org" />
       <sy:updatePeriod>hourly</sy:updatePeriod>
       <sy:updateFrequency>1</sy:updateFrequency>
       <docs>http://backend.userland.com/rss</docs>


        <h2>Journal Articles</h2>


<div class="xar-mod-head"><span class="xar-mod-title">Overload Journal #35 - Jan 2000 + Programming Topics</span></div>

<table border="0" cellpadding="1" cellspacing="0">
    <tbody>
    <tr>
        <td valign="top">
            Browse in :
       </td>
       <td valign="top">

                                            <a href="https://members.accu.org/index.php/journals/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c76/">Journals</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c78/">Overload</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c169/">35</a>
                    (8)
<br />

                                            <a href="https://members.accu.org/index.php/journals/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c13/">Topics</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c65/">Programming</a>
                    (877)
<br />

                                            <a href="https://members.accu.org/index.php/journals/c169-65/">Any of these categories</a>

                    -                        <a href="https://members.accu.org/index.php/journals/c169+65/">All of these categories</a>
<br />
</td>
   </tr>
   </tbody>
</table>




<div class="xar-error">
   <p>
 <strong>Note:</strong> when you create a new publication type,
the articles module will automatically use the templates
<em>user-display-[publicationtype].xt</em>
and <em>user-summary-[publicationtype].xt</em>.
If those templates do not exist when you try to preview or display a new article,
you'll get this warning :-)  Please place your own templates in themes/<em>yourtheme</em>/modules/articles . The templates will get the extension .xt there. </p>
</div>
<div class="xar-norm xar-standard-box-padding">
   <h1><strong>Title:</strong>&nbsp;Automatic Object Versioning for Forward and Backward File
Format Compatibility</h1>
<p><strong>Author:</strong>&nbsp;</p>
<p>
<strong>Date:</strong> 26 January 2000 16:50:55 +00:00 or Wed, 26 January 2000 16:50:55 +00:00</p>
<p><strong>Summary:</strong>&nbsp;</p>
<p><strong>Body:</strong>&nbsp;<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e18" id="d0e18"></a></h2>
</div>
<p>In this article I attempt to document some problems encountered
when evolving objects are serialised to and from persistent
storage. I also describe a class that allows backward and some
degree of forward compatibility of file formats to be attained
automatically.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e22" id="d0e22"></a>Object
evolution and file formats</h2>
</div>
<p>Systems and objects evolve. Most projects will have to face the
problems arising when object persistence (or serialisation) and
object evolution meet. Whether you are writing and reading a system
to a file, or streaming objects across process boundaries, you need
to know how to write out your objects in a binary representation,
and you then need to know how to read them in again. When writing,
this is usually an easy call: just write out all you know about the
object's state - its attributes. When reading, you need to be able
to determine the schema used when the object was saved, and take
appropriate action if this differs from the current schema. In this
article I shall discuss serialising objects to files, but similar
arguments can be applied to other forms of object
packaging/unpackaging, such as preparing objects to be squirted
across a network.</p>
<p>Objects are typically composed of attributes that are either
built-in types or other objects (or references to
these)<sup>[<a name="d0e29" href="#ftn.d0e29" id=
"d0e29">1</a>]</sup>. As objects evolve, they may gain new
attributes, or existing attributes may become obsolete (changes of
use of attributes can be considered as making the old one obsolete
and adding the new).</p>
<p>In some evolving systems there may be no requirement to be able
to read files written with a different version of the software. In
other cases, one-off batch upgrade processes might be invoked to
upgrade all data from the previous format to the new format. In
many systems, however, it if often necessary, or at least
desirable, to provide some form of backward and possibly forward
compatibility in each version of the system. This provides users
with the greatest flexibility, at the expense of often considerable
work for the developers.</p>
<p>In this article, by backward compatibility I mean that the
current version of the software will be able to read old files
stored by some or all earlier versions of the same software using
previous object schemas. By forward compatibility I mean that in a
heterogeneous environment where different versions of the software
are being used concurrently, files saved by a later version can be
read by earlier versions, as long as it makes sense to do so. If
files are resaved using the earlier version then future additions
will be lost and the file will be saved in the earlier file format.
Note that I am not considering the ability to save a file in an
earlier format. In fact, this becomes unnecessary if forward
compatibility is supported.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e37" id="d0e37"></a>The traditional
solution</h2>
</div>
<p>A common technique used to address schema evolution is to have a
&quot;reserved block&quot; at the end of an object, typically of the
form:</p>
<pre class="programlisting">
class person {
  shoe_size ss;
  char reserved[20];
};
</pre>
<p>To allow for some future expansion, a predetermined amount of
unused space is added at the end of each object. If we decide that
our person class could do with some extension, new attributes can
be added. The reserved space shrinks to keep the size of the object
the same, which in turn simplifies object serialisation.</p>
<p>There are a number of problems with this technique.</p>
<p>A lot of space can be wasted, depending on how much room you
allow for future growth.</p>
<p>If you want to add more attributes than you left space for,
you're stuck.</p>
<p>If you inadvertently forgot to initialise your reserved space,
then you might not be able to use it anyway if you can't tell
between a new attribute and garbage in the reserved block.</p>
<p>Even if you remembered to initialise it, you may need to encode
future attributes if the values you initialised the block to are
valid non-default attribute values. (E.g., if you want to store a
new integer attribute, with -1 denoting the default or missing
value, and your block was initialised to zero, you'll have to add
one to the attribute values when storing).</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e56" id="d0e56"></a>Other
solutions</h2>
</div>
<p>Other possibilities include adding version numbers to the file.
Earlier software versions can then simply refuse to open the file,
and later software versions can determine which parts of each
object should be present in the file based on the stored version
number. For finer grained control, some or all objects can
additionally use their own version stamp.</p>
<p>The main drawback with this approach is that large amounts of
conditional code can build up in the serialisation methods to take
each generation of file format into account. This leads to
spaghetti code and major problems with system maintainability and
extensibility. Errors can often be introduced into such complex
legacy code quite easily, and testing is complicated because normal
use of the software with current file formats does not exercise
much of this compatibility code.</p>
<p>Furthermore, if attributes must be removed from objects, dummy
attribute values are often left in the file in perpetuity because
of the extra burden of further checks, etc., in the already
complicated serialisation code, leading to more wasted space and
more potential errors.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e65" id="d0e65"></a>Self-describing
file formats</h2>
</div>
<p>One solution to these problems of schema evolution is to store
the schema in the file alongside the data. If the system metadata
is stored in the file in addition to its data, then the file can be
read in exactly as intended. This is because you can always tell
what data values are present in the file.</p>
<p>XML allows this to be taken one step further. XML defines a
standard file format for data and metadata (ASCII, tags, etc.). It
also defines a format for the meta-metadata (data describing the
metadata) in the Document Type Definition. An XML parser can
therefore read any XML file, even if it has no comprehension of the
business domain and objects that the file describes. Even though
XML is becoming more widespread and easier to use through
public-domain parsers, standard binary files or homegrown file
formats are ubiquitous<sup>[<a name="d0e72" href="#ftn.d0e72" id=
"d0e72">2</a>]</sup>. A simple method for tackling schema evolution
problems in these files is required.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e76" id="d0e76"></a>Object sizes as
a discriminator of object content</h2>
</div>
<p>In many cases (although not all), the size of an object gives a
good idea of its version, and hence its contents. As bits get added
to an object, the increase in size can be used to determine whether
we have an earlier or later version of the object on file. If we
reach the end of the data on file before we have finished
initialising our object, we can stop reading, assign default values
for any attributes that have not yet been read, and continue with
the next object. Similarly, if we have read all the attributes we
know about, and there is still more data on file, we know we must
be reading a later version of the file, and we can skip over this
additional data. For simple fixed size objects, this is quite a
common technique - simple structs are sometimes written to storage
preceded by their binary size. This size is used as a primitive
version stamp.</p>
<p>One problem with this technique is that it is often difficult to
calculate the size of complex objects in advance. Complex objects
often contain sub-objects, so any size computation needs to iterate
around all attributes and sub-objects. An additional size() method
can be added to all objects, to enable the filing subsystem to make
records of object sizes in the file. This is somewhat intrusive,
however. It can also be error-prone. If the size calculation in any
one class has errors, or if the size methods are not updated when
persistence methods are modified, then the resultant mismatch can
produce filing errors that may not surface for some time.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e83" id="d0e83"></a>A class to
automatically calculate object sizes</h2>
</div>
<p>What we would like is a class that can automatically calculate
object sizes without any knowledge of the object itself. Such a
class would make object versioning really easy to implement. There
would be no need for intrusive <tt class="methodname">size()</tt>
methods in every object, and calculations cannot get out of step
when object schemas change.</p>
<p>The technique I used to do this was to utilise the file stream
itself to calculate any given object's size. This way, the size is
guaranteed to be right - as long as you can measure how much data
you have written to the stream. It does not matter if strings are
stored preceded by their length, or are null-terminated, or whether
an integer is stored as binary data or as ASCII text. By measuring
our progress reading through the file we can determine
automatically whether there is more to read or if we have hit the
end of the object's data.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e93" id="d0e93"></a>Strategy</h2>
</div>
<p>Details of the actual algorithms used are given with the
implementation in the next section. Basically I precede each
object's data in the file with its size as in figure 1.</p>
<p>I call this grouping of size and state on file an object block.
For compound objects, each nested object has its own object block,
complete with its size, so that it can be independently extended as
it evolves, and the parent object block has a size that takes into
account the sizes of all the nested blocks.</p>
<div class="figure"><a name="d0e100" id="d0e100"></a>
<div class="mediaobject c2"><img src="/var/uploads/journals/resources/Blundell%201.png"
align="middle" alt=
"How an object&rsquo;s attributes are stored, preceded by their size, in an object block in a file."></div>
<p class="title c3">Figure 1. How an object's attributes are
stored, preceded by their size, in an object block in a file.</p>
</div>
<div class="figure"><a name="d0e106" id="d0e106"></a>
<div class="mediaobject c2"><img src="/var/uploads/journals/resources/Blundell%202.png"
align="middle" alt=
"How a compound object block is stored in a file. The first size is that for the entire compound object; the second is that for just the sub-object."></div>
<p class="title c3">Figure 2. How a compound object block is stored
in a file. The first size is that for the entire compound object;
the second is that for just the sub-object.</p>
</div>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e112" id="d0e112"></a>Abstracting
the file system</h2>
</div>
<p>Not everyone uses <tt class="classname">CArchive</tt>/<tt class=
"classname">CFile</tt>, or any other single file type, for
persistence. Furthermore, developers are often reluctant to change
the classes that they currently use for filing. To provide some
degree of portability, I abstracted the filing system away behind a
primitive file interface. This interface requires only basic filing
facilities - reading and writing an object size, determining the
current file position, and seeking to a given file position. An
adapter class can thus be written for any filing classes that
support these basic facilities. An example of such an adapter class
is given later.</p>
<p>I have used typedefs for the basic concepts of a file position
(<tt class="type">file_ptr</tt>) and an object size (<tt class=
"type">file_diff</tt> - the difference between two file positions).
This allows you to change these to suit your current system if
necessary. In the example below I have used <tt class=
"literal">size_t</tt> for both of these.</p>
<p>Note that the interface requires seeking to calculated positions
(and not just positions that have previously been queried). This
may prove problematic if your system does not allow true byte-level
random access.</p>
<pre class="programlisting">
typedef size_t file_ptr;
typedef size_t file_diff;

class file_T
{
public:
  virtual bool is_writing() = 0;

  virtual void seek(file_ptr pos) = 0;
  virtual file_ptr position() = 0;

  virtual void read(file_diff &amp;size) = 0;
  virtual void write(file_diff size) = 0;
};
</pre>
<p>The <tt class="methodname">is_writing()</tt> method returns true
if the file is being written to, or false if it is being read. I
tend to use a single method for object persistence. This aids
conciseness, gives me a single function to maintain, keeps related
code together, avoids code duplication, copy-and-paste errors, etc.
I wanted a single interface to be able to handle both writing and
reading, even if you decide to use save/load pairs of persistence
methods. The beauty of using an interface and adapter classes is
that the adapter classes can take care of and hide these
details.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e143" id=
"d0e143"></a>Implementation</h2>
</div>
<p>To avoid too much line wrapping, I have stripped comments from
the code and provided commentary in the text. Methods have also
been made inline to save column inches. All the usual disclaimers
apply!</p>
<p>The class interface is shown first. My class is called
<tt class="classname">object_size_handler</tt> - a bit of a
mouthful, but descriptive. The three important methods are as
follows. First, the ctor allows an object to be created and
attached to a given <tt class="classname">file_T</tt> file
interface. Second, the <tt class="methodname">more()</tt> method is
used to determine if there is more data when reading an object from
file. Finally, the dtor calculates object sizes, and repositions
the file pointer as necessary.</p>
<pre class="programlisting">
class object_size_handler
{
typedef object_size_handler my_T;
public:
  explicit object_size_handler(file_T &amp;f);
  ~object_size_handler();
  bool more() const;  
            // is there more to read?
  file_T &amp;file() const {return m_file;}

private:  // Not implemented - no copying!
  object_size_handler(const my_T &amp;copy);
  object_size_handler 
          &amp;operator=(const my_T &amp;rhs);

private:
  file_T      &amp;m_file;
  file_ptr    m_offset;
};
</pre>
<p>To help describe the implementation of these methods, let's
consider separately the cases of writing to, and reading from, the
file.</p>
<p>The ctor is shown below. On writing to the file, it stores a
dummy object size (since it has not calculated it yet), and
remembers the position of the start of the object's data. On
reading, it reads the stored object size, and from that and the
current file position it determines the file position where the
object ends.</p>
<pre class="programlisting">
object_size_handler::
    object_size_handler(file_T &amp;f)
  : m_file(f), m_offset(0){
  if (m_file.is_writing()) {
    m_file.write(file_diff(0));
    m_offset = m_file.position();
  } else {
    file_diff size;
    m_file.read(size);
    m_offset = m_file.position();
    m_offset += size;
  }
}
</pre>
<p>The dtor performs the end-of-object operations. On writing, it
determines the current file position (where the object has ended),
subtracts the start-of-object position that was stored by the ctor,
and so determines the object size automatically. It then seeks back
to just before the start of the object and writes the size there.
On reading, it simply seeks to the end of the current object,
skipping over unread data.</p>
<pre class="programlisting">
object_size_handler::~object_size_handler()
{
  if (m_file.is_writing()) {
    file_ptr end = m_file.position();
    file_diff size = end - m_offset;
    m_file.seek 
        (m_offset - sizeof(file_diff));
    m_file.write(size);
    m_file.seek(end);
  } else {
    // if (more()) set_skipped_flag();
    m_file.seek(m_offset);
  }
}
</pre>
<p>The commented out line allows a check to be made for data that
has not been read in. This data would be lost if the file were
later resaved. This is discussed in more detail in the &quot;A possible
extension&quot; section, below.</p>
<p>Finally, the <tt class="methodname">more()</tt> method checks to
see if we have reached the end of the object's data when reading.
It just returns true when writing (to allow symmetrical persistence
methods). I have used multiple returns in this short method - use a
local variable and a single return if you prefer.</p>
<pre class="programlisting">
bool object_size_handler::more() const
{
  if (m_file.is_writing())
    return true;
  else {
    file_diff remaining =
         m_offset - m_file.position();
    return remaining &gt; 0;
  }
}
</pre></div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e180" id="d0e180"></a>Example of
usage</h2>
</div>
<p>If this is all a bit confusing (or confused!), then an example
should clarify things. Consider a class, A, that we wish to
serialise. Let us assume that A originally had just one sub-object.
As the system evolved, however, another attribute was added, and in
the current version (V3) of the software it has three components.
We would like to be able to read files in any of the three
available formats, using any of the three versions of the
software.</p>
<p>The code below shows how this is achieved. It uses the Microsoft
Foundation Classes' persistence mechanism, in which a single
&quot;Serialize&quot; method receives an object of type <tt class=
"classname">CArchive</tt>. <tt class="methodname">Serialize()</tt>
methods are symmetrical, and either write or read data depending on
a flag internal to the <tt class="constant">CArchive</tt>
object.</p>
<p>If all files were written and read in the latest format, the
<tt class="methodname">Serialize()</tt> method would write or read
all three sub-objects like this:</p>
<pre class="programlisting">
void A::Serialize(CArchive &amp;ar)
{
  // Read and write version 3 file format
  m_original_data.Serialize(ar);    // always have this
  m_new_data.Serialize(ar);  // added in V2
  m_even_newer_data.Serialize(ar);  // added in V3
}
</pre>
<p>To allow full forward and backward file compatibility, all we
need to do is make the following changes. First of all we need to
create an adapter object (of class <tt class=
"classname">archive_adapter</tt>) to wrap up the <tt class=
"classname">CArchive</tt> object. This wraps <tt class=
"classname">CArchive</tt> and realises the standard <tt class=
"classname">file_T</tt> interface. Next we instantiate an
<tt class="classname">object_size_handler</tt> object to manage the
size and versioning of our A object. Finally, we just serialise out
attributes as normal, checking the presence of each one using the
<tt class="methodname">more()</tt> method.</p>
<pre class="programlisting">
void A::Serialize(CArchive &amp;ar) {
  archive_adapter f(ar);
  object_size_handler object_size(f);

  // All versions have this
  m_original_data.Serialize(ar);
  // Only later versions have this
  if (object_size.more())
    m_new_data.Serialize(ar);

  // Only really new versions have this
  if (object_size.more())
    m_even_newer_data.Serialize(ar);
}
</pre>
<p>The simplicity of the above code belies a lot that is going on
beneath the surface. Let's consider writing first, and then look at
reading.</p>
<p>When writing, the <tt class="classname">object_size_handler</tt>
ctor writes a dummy (zero) size onto the stream and records the
file position where the object starts. All the <tt class=
"methodname">more()</tt> methods return true during writing, so all
three sub-objects are serialised to the stream. Finally, the
<tt class="classname">object_size_handler</tt> object goes out of
scope. The dtor reads the current file position. From this end
position and the known start position, the object size can be
calculated and written at the start of the object block.</p>
<p>On reading, the <tt class="classname">object_size_handler</tt>
ctor reads the size of the stored data. A's sub-objects are
restored from file, but this time each call to <tt class=
"methodname">more()</tt> checks the file position against the known
end-of-object position. If there is no more data, <tt class=
"methodname">more()</tt> returns false, and no more attributes are
read from the file (because they are not there!). When the
<tt class="classname">object_size_handler</tt> goes out of scope,
the dtor fires, which ensures the file pointer is positioned
correctly at the end of the object's data, perhaps after a fourth
sub-object written by a future version of the software.</p>
<p>All these seeks, and rewriting of the object size should not
incur too much overhead, because in most filing systems i/o is
buffered. This actually means that rewriting the size at the start
of the object will most probably not require the physical disk to
rewrite the data, unless the object is so large that the buffer has
been flushed mid-object.</p>
<p>A peculiarity of the <tt class="classname">CArchive</tt> class
requires some special handling in the adapter class. <tt class=
"classname">CArchive</tt> contains a <tt class=
"classname">CFile</tt> object, which is responsible for writing
data to disk. However, not only is <tt class="classname">CFile</tt>
buffered, but also, so it seems, is <tt class=
"classname">CArchive</tt> - a double layering of buffers. This
means that you can check the file pointer of the underlying
<tt class="classname">CFile</tt> object, write something to the
<tt class="classname">CArchive</tt> object, check the file pointer
again and find that it has not changed! When direct access is
required to the underlying <tt class="classname">CFile</tt> object
(in this case for the <tt class="methodname">Seek()</tt> and
<tt class="methodname">GetPosition()</tt> calls) it is necessary to
synchronise the <tt class="classname">CArchive</tt> object and the
<tt class="classname">CFile</tt> object. This highlights another
advantage of interface programming and the use of an adapter class.
All these implementation details (i.e., mess) can be encapsulated
within the adapter class, with clients (in this case the <tt class=
"classname">object_size_handler</tt>) blissfully unaware of these
complications.</p>
<p>Here is the <tt class="classname">archive_adapter</tt>
class:</p>
<pre class="programlisting">
class archive_adapter : public file_T
{
public:
  archive_adapter(CArchive &amp;ar)
                      :m_ar(ar) {}
  virtual bool is_writing()
    {return m_ar.IsStoring() != FALSE;}
  virtual void seek(file_ptr pos)
  {
    sync();
    m_ar.GetFile()-&gt;Seek(pos,
                     CFile::begin);
    sync();
  }

  virtual file_ptr position()
    {sync(); return m_ar.GetFile()-&gt;GetPosition();}

  virtual void read(file_diff &amp;size)
    {m_ar.Read(&amp;size, sizeof(file_diff));}

  virtual void write(file_diff size)
    {m_ar.Write(&amp;size, sizeof(file_diff));}

private:
  void sync() {m_ar.Flush();}

private:
  CArchive  &amp;m_ar;
};
</pre></div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e302" id="d0e302"></a>A possible
extension</h2>
</div>
<p>By adding a flag that is reset when a file is initially opened,
and which is set whenever the dtor skips over some unread
attributes<sup>[<a name="d0e307" href="#ftn.d0e307" id=
"d0e307">3</a>]</sup>, a check can be made to see if any
attributes, stored in a later version, would be lost if the file
were rewritten. This allows the usual warning (&quot;some changes may be
lost&quot;) to be displayed when appropriate, but avoided when no
additional attributes were found (and hence can't be lost!).</p>
<p>Similarly, if <tt class="methodname">more()</tt> returns false
during a read operation, we may be able to determine that the file
was using an earlier schema. If the user attempts to resave the
file, we could in principle warn that the file format would be
updated in the process. This may well be unnecessary, however,
given that we are now forward compatible - the earlier version of
the software will be able to read the later file format anyway.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e316" id="d0e316"></a>Other
benefits</h2>
</div>
<p>An added benefit of the automatic size checking is that optional
attributes can easily be stored at the end of an object. When
reading, attributes present will be read, and the absence of
additional attributes can be checked for. When writing, attributes
that are defined but which have their default values need not be
stored in full, because the default values can be assigned when
their absence is noted when the file is loaded.</p>
<p>The only overhead for these facilities is the space for an
object size (typically four bytes, holding the value 'zero') in
place of any missing object or block of attributes (see figure
3).</p>
<div class="figure"><a name="d0e323" id="d0e323"></a>
<div class="mediaobject c2"><img src="/var/uploads/journals/resources/Blundell%203.png"
align="middle" alt=
"A missing attribute stored as a zero size"></div>
<p class="title c3">Figure 3. A missing attribute stored as a zero
size</p>
</div>
<p>An empty block containing an <tt class=
"classname">object_size_handler</tt> object will skip over an
obsolete object in the file (and will generate an empty object
block on writing). A helper class can be defined to allow such
obsolete attributes to be skipped easily:</p>
<pre class="programlisting">
class skip_object
{
public:
  skip_object(file_T &amp;f)
        {object_size_handler osh(f);}
};
</pre>
<p>The <tt class="classname">object_size_handler</tt> object is
created and goes out of scope again in the ctor. Its scope is
therefore just the period whilst the <tt class=
"classname">skip_object</tt> object is being constructed. It can be
used as follows:</p>
<pre class="programlisting">
// ...
m_attribute1.Serialize(ar);
skip_object skip_this_one(object_size.file());
m_attribute2.Serialize(ar);
// ...
</pre></div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e346" id=
"d0e346"></a>Conclusion</h2>
</div>
<p>If you are serialising a system of objects, if your objects tend
to grow over time as new attributes are added, and if it makes
sense for later versions of your software to be able to read files
saved by earlier versions (by calculating missing attribute values,
or using safe defaults, and ignoring obsolete attributes), then the
<tt class="classname">object_size_handler</tt> class offers an
extremely low-overhead solution to backward compatibility.</p>
<p>If, it makes sense for earlier versions of the software to read
files saved by later versions (it is acceptable to simply ignore
newer object attributes stored by the later version and assume
default values for attributes that have subsequently become
obsolete), then this class offers a zero-overhead solution to
forward compatibility. The class can also determine if information
would be lost when resaving a file.</p>
<p>Finally, the use of an abstracted file interface allows the
<tt class="classname">object_size_handler</tt> to be used with
virtually any random-access persistence mechanism - C files, C++
streams, file classes in third-party class libraries, etc. (with
the proviso about seeking to calculated file locations).</p>
</div>
<div class="footnotes"><br>
<hr class="c4" width="100">
<div class="footnote">
<p><sup>[<a name="ftn.d0e29" href="#d0e29" id=
"ftn.d0e29">1</a>]</sup> Less frequently they can contain no
attributes (for stateless objects), or can contain function
pointers, etc. Here I consider enums to be equivalent to built-in
integral types.</p>
</div>
<div class="footnote">
<p><sup>[<a name="ftn.d0e72" href="#d0e72" id=
"ftn.d0e72">2</a>]</sup> There are also times when a file format
that is not easily human-readable is advantageous.</p>
</div>
<div class="footnote">
<p><sup>[<a name="ftn.d0e307" href="#d0e307" id=
"ftn.d0e307">3</a>]</sup> see the commented-out line in the
implementation given.</p>
</div>
</div>
</p>
<p><strong>Notes:</strong>&nbsp;</p>
<p><em>More fields may be available via dynamicdata ..</em></p>
</div>
</channel>
</rss>
