1@chapsummary
2This section describes how to save and restore objects using a binary stream.
3It details how to add the appropriate functions to each class to make use
4of this facility.
5@endchapsummary
6
7All objects in VXL should be able to save themselves to a stream (eg a file)
8and restore (load) themselves from a stream (file). The main functions provided
9for this purpose are @code{vsl_b_write(os,object);} and @code{vsl_b_read(is,object&);}.
10
11The binary IO for the core libraries (vbl, vil, vgl and vnl) is implemented in `clip-on'
12libraries which live in the @code{io} subdirectories of each library (thus the
13declaration of the function @code{vsl_b_write(vsl_b_ostream&,vnl_vector const&);}
14lives in the file @file{vnl/io/vnl_io_vector.h}.
15
16However, it is recommended that I/O for other libraries be provided by writing
17@code{b_write(os);} and @code{b_read(is);} functions in each class.  See the
18`Design Notes' section below.
19
20@section Supported Platforms
21
22The binary I/O code is known to work across the following hardware/OS/compiler
23combinations, but probably also works on most other platform/compiler combinations:
24
25@enumerate
26@item  THIS CODE IS OUT OF DATE!
27@end enumerate
28
29Thus
30binary files produced by any of the above should be readable by any other of the above.
31There is of course a minor exception: large numbers (like integers larger than 4294967295)
32saved on a 64-bit platform cannot be read on a 32-bit platform.
33
34@subsection caveats
35
36The code has been designed to work on as many platforms as possible. However if your
37platform uses any of the following, then it will probably not work (as presently coded.)
38@enumerate
39@item  A middle endian word encoding scheme.
40@item  Chars of length other than 8 bits.
41@item  Non-IEEE format floats and doubles.
42@end enumerate
43
44@section Using Binary I/O
45
46To save an object to a file, simply do the following:
47@example
48  vxl_myclass my_object;
49
50  // Fill my_object
51
52  vsl_b_ofstream bfs("my_object.bvl");
53  if (!bfs)
54  @{
55    vcl_cerr<<"Failed to open my_object.bvl for output."<<vcl_endl;
56  @}
57  else
58  @{
59    vsl_b_write(bfs,my_object);
60    bfs.close();
61  @}
62@end example
63
64To load/restore the object from a file:
65
66@example
67  vxl_myclass my_new_object;
68
69  vsl_b_ifstream bfs("my_object.bvl");
70  if (!bfs)
71  @{
72    vcl_cerr<<"Failed to open my_object.bvl for input."<<vcl_endl;
73  @}
74  else
75  @{
76    vsl_b_read(bfs,my_object);
77    bfs.close();
78  @}
79@end example
80
81It is recommended that the default extension name for your binary files is @code{.bvl}.
82This extension does not appear to be used by any other program. In many cases however, you
83will want to pick a new extension to indicate the contents of a file. For example, we store
84active shape model objects with ending @code{.asm}.
85
86The classes @code{vsl_b_ifstream} and @code{vsl_b_ofstream} are simple wrappers around
87real @code{vcl_ifstream} and @code{vcl_ofstream} objects. These wrappers ensure that
88you open a file with CR/LF conversion turned off, and they should also allow lots of
89common misuses to be caught at compile time.
90
91The functions @code{vsl_b_write(os,X)} and @code{vsl_b_read(is,X)} are defined for all
92reasonable cases, including all inbuilt types, most classes in vcl and the classes in
93the core vxl libraries.
94
95When you write a new class, you should add the appropriate functions to allow easy
96use of binary I/O (see below).
97
98Or for simplicity we provide the utility functions which would allow you to write:
99
100@example
101  #include <vsl/vsl_quick_file.h>
102  vxl_myclass my_object,my_new_object;
103
104  vsl_quick_file_save("my_object.bvl",my_object);
105  vsl_quick_file_load("my_object.bvl",my_new_object);
106@end example
107
108
109@subsection Saving multiple objects
110
111One can use exactly the same approach to save a set of objects
112
113@example
114  vxl_myclass my_object;
115  vxl_my_other_class my_other_object;
116
117  // Fill objects
118  // ...
119
120  vsl_b_ofstream bfs("my_object.bvl");
121  if (!bfs)
122  @{
123    vcl_cerr<<"Failed to open my_object.bvl for output."<<vcl_endl;
124  @}
125  else
126  @{
127    vsl_b_write(bfs,my_object);
128    vsl_b_write(bfs,my_other_object);
129    bfs.close();
130  @}
131@end example
132
133(and similarly for loading them).
134
135A standard rule for ensuring trouble free I/O is
136@quotation
137Always write the input and output code in tandem - the output should
138precisely mirror the input.
139@end quotation
140
141
142@subsection Binary I/O by baseclass pointer
143
144When using polymorphism, there are frequently times when one needs to
145save and restore an object just using a base class pointer to it.
146@code{vsl} provides facilities to do this.
147
148Assuming class @code{my_derived} is derived from class @code{my_base}, the following
149will work.
150
151To save an object by baseclass:
152
153@example
154  my_derived d;
155
156  my_base *b = &d;
157
158  vsl_b_ofstream bfs("data.bvl");
159  vsl_b_write(bfs,b);
160  ...
161@end example
162
163To restore an object:
164@example
165  // Make application aware of possible classes that it might see in the file
166
167  vsl_add_to_binary_loader(my_derived());
168  vsl_add_to_binary_loader(my_derived2());
169  ...
170
171  my_base *b = 0;
172
173  vsl_b_ifstream bfs("data.bvl");
174  vsl_b_read(bfs,b);
175  // b now points to the correct class which has been created
176  // on the heap and filled with the data from bfs
177  ...
178@end example
179
180Note that the read function will only work if the application has been made
181aware of each of the possible derived classes that it might come across in the
182file.  This is done using calls to @code{vsl_add_to_binary_loader(my_derived())}
183(see appendix for details).
184
185To reduce the pain of doing this, many libraries have a function that adds all
186the relevant derived classes (eg @code{xxxx_add_all_binary_loaders()} where
187@code{xxxx} is the library name).
188
189@subsection Which files do I need to include/link?
190
191In general the vsl_b_read and vsl_b_write functions use Koenig Lookup - that is the location
192of their declaration depends on their parameters.
193
194The @code{vsl_b_stream} objects and @code{vsl_b_write} and @code{vsl_b_read} functions
195for fundamental data types are declared in @code{<vsl/vsl_binary_io.h>}. If you want to
196load or save a @code{vcl_vector}, the appropriate @code{vsl_b_write} and @code{vsl_b_read}
197functions will be in @code{<vsl/vsl_vector_io.h>}. Likewise for most of the other vcl classes.
198The @code{vsl} library contains the implementation of all of this.
199
200When reading/writing by baseclass pointer, you need to include @code{vsl/vsl_binary_loader.h}.
201
202If you want to load or save a @code{vgl_point_2d}, you will need to include
203@code{<vgl/io/vgl_io_point_2d.h>} and similarly for all other Level-1 VXL libraries. You
204will need to include the vgl_io library.
205For Level-2 libraries, the situation varies. If binary io has been defined at all for a
206level-2 library, it might be included in the library itself, e.g. the io functions for
207vpdfl_gaussian are declared in the same file as the Gaussian, @code{vpdfl/vpdfl_gaussian.h>}.
208Alternatively, it might be in a clip-on library in the same form as the Level-1 libraries
209above.
210
211@subsection How to save templated objects
212
213The situation for templated objects is the same as above, except that you need to ensure that
214the appropriate (templated) @code{vsl_b_read} and @code{vsl_b_write} functions are explicitly instantiated.
215This instantiation is achieved by placing a file in the relevant "Templates" folder.
216
217An example template file, is shown below. It enables saving of a 2d array of "@code{hjk_model}"s
218(a completely made up plain class).
219
220@example
221  // This is my_module/hjk/Templates/vbl_array_2d_io+hjk_model~-.cxx
222  #include <vbl/io/vbl_io_array_2d.hxx>
223  #include <hjk/hjk_model.h>
224  VBL_IO_ARRAY_2D_INSTANTIATE(hjk_model);
225@end example
226
227The @code{vbl_io_array_2d.txx} file contains the @code{VBL_IO_ARRAY_2D_INSTANTIATE} macro and the
228@code{hjk_model.h} file contains the io header declarations for a plain class.
229
230Another example template file, allowing the saving of a vector of @code{vgl_point_2d} objects,
231is shown below.
232
233@example
234  // file = my_module/hjk/Templates/vsl_vector_io+vgl_point_2d~-.cxx
235  #include <vsl/vsl_vector_io.hxx>
236  #include <vgl/io/vgl_io_point_2d.h>
237  VSL_VECTOR_IO_INSTANTIATE(vgl_point_2d<double>);
238@end example
239
240The @code{vsl_vector_io.txx} file contains the @code{VSL_VECTOR_IO_INSTANTIATE} macro and the
241@code{vgl_io_point_2d.h} file contains the io header declarations for @code{vgl_point_2d<double>}.
242
243You should now be able to load and save templated objects with lines such as:-
244
245@example
246  vcl_vector<hjk_model> hjk_model_vec;
247  vsl_b_ofstream bfs("hjk_model_vec.bvl");
248  if (!bfs)
249  @{
250    vcl_cerr<<"Failed to open hjk_model_vec.bvl for output."<<vcl_endl;
251  @}
252  else
253  @{
254    vsl_b_write(bfs,hjk_model_vec);
255    bfs.close();
256  @}
257@end example
258
259NB, the template instantiation files should be placed in your own libraries (ie here "hjk") to
260avoid creating unnecessary and unused versions of a given templated function.
261
262
263@section Tidy Printing with @code{vsl_indent}
264
265The utility functions and class in @code{vsl_indent} give a way of putting
266indentation into output streams to give more legible printed output.
267
268If each class implements it's printing (print(os) or print_summary(os))
269in such a way that at the beginning of each new line one inserts an
270indentation using
271@example
272  os<<vsl_indent()<<"Rest of stuff.."<<vcl_endl;
273@end example
274and increases and decreases the current indentation for the stream with
275@code{vsl_indent_inc(os)} and @code{vsl_indent_dec(os)},
276then one can easily generate readable output for complex nested sets
277of classes.
278
279It's use is best described by example:
280
281@example
282  vcl_cout<<vsl_indent()<<"No Indent"<<vcl_endl;
283  vsl_indent_inc(vcl_cout);
284  vcl_cout<<vsl_indent()<<"1 Indent"<<vcl_endl;
285  vsl_indent_inc(vcl_cout);
286  vcl_cout<<vsl_indent()<<"2 Indent"<<vcl_endl;
287  vsl_indent_dec(vcl_cout);
288  vcl_cout<<vsl_indent()<<"1 Indent"<<vcl_endl;
289  vsl_indent_dec(vcl_cout);
290  vcl_cout<<vsl_indent()<<"No Indent"<<vcl_endl;
291@end example
292
293This produces output of the form
294@example
295No Indent
296  1 Indent
297    2 Indent
298  1 Indent
299No Indent
300@end example
301
302Example of use in class output:
303@example
304  class Fred
305  @{
306   public:
307    void print(vcl_ostream& os) const @{ os<<vsl_indent(os)<<"Fred's data"; @}
308  @};
309
310  vcl_ostream& operator<<(vcl_ostream& os, Fred const& fred)
311  @{
312    os<<"Fred: "<<vcl_endl;
313    vsl_indent_inc(os);
314    fred.print(os);
315    vsl_indent_dec(os);
316    return os;
317  @}
318
319  class Jim
320  @{
321   private:
322    Fred fred_;
323   public:
324    void print(vcl_ostream& os) const
325    @{
326       os<<vsl_indent()<<fred_<<vcl_endl
327           vsl_indent()<<"Jim's other data";
328    @}
329  @};
330
331  vcl_ostream& operator<<(vcl_ostream& os, Jim const& jim)
332  @{
333    os<<"Jim: "<<vcl_endl;
334    vsl_indent_inc(os);
335    jim.print(os);
336    vsl_indent_dec(os);
337    return os;
338  @}
339
340  main()
341  @{
342    Jim jim;
343    vcl_cout<<jim<<vcl_endl;
344  @}
345@end example
346
347This produces output:
348@example
349 Jim:
350   Fred's data
351   Jim's other data
352@end example
353
354If Jim were then included as a member of another class, Harry, one could get
355output of the form
356@example
357  Harry:
358   Harry's basic data
359   jim1:
360     Fred's data
361     Jim's other data
362   jim2:
363     Fred's data
364     Jim's other data
365@end example
366
367and so forth.  The author humbly suggests that this makes the summaries
368quite readable.
369
370@section Error Detection
371
372IO is often prone to errors beyond the control of the programmer. In
373particular, files can be come corrupted, given to programs that can't read
374a new format, read on platforms that do not support large enough numbers.
375
376vsl attempts to detect as many error conditions as possible.
377It prints an error message to @code{vcl_cerr} and sets the fail bit
378on the input stream. Any objects that were being loaded when the error
379occurred should be consistent at least as far as being able to delete the
380object safely.
381
382During the opening of a binary input stream, vsl also checks
383for a schema version number, and magic number that confirm that
384the stream was written by vsl.
385
386It is easy to detect the error condition as the example shows
387@example
388  vsl_b_ifstream bfs_in(path);
389  if (!bfs_in)
390  @{
391    vcl_cout << "Could not open " << path
392             << " for reading as binary IO" << vcl_endl;
393    return;
394  @}
395  vsl_b_read(bfs_in, my_obj);
396  if (!bfs_in)
397  @{
398    vcl_cout << "Unable to read my_obj" << vcl_endl;
399    return;
400  @}
401  bfs_in.close();
402@end example
403