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