Serialization

xiaoxiao2021-03-06  35

Serialization Overview

demand

Other implementation

Here, we use terminology

Serialization to indicate that a set of original C data structures represents the purpose of byte stream to retrofitted. Such systems can be used to re-establish the original data structure in another program environment. Therefore, it can also be used as an object persistence (transote parameter passing), or the implementation of other features. In our system, the term will be used

Archive represents a specific byte stream. The file can be a binary file, a text file, an XML file, or a type defined by other user.

our target is:

The transplantability of the code - relying only on the characteristics of ANSI C . Economic - excavation of various C features such as RTTI, templates, and multi-inheritance, etc., allow users to be easily used and the code is short. Independence of class versions. - When a class definition changes, the files of the old version of the class can still be imported into the new version of the class. Deep storage and recovery of pointers. - Save or restore the pointer while saving or recovering the pointer points to the pointer. The correct process of processing multiple pointers When pointing to the same object. Direct support for serialization of STL and other common templates. The portability of data - byte stream established on one platform should be correct on another platform. Orthogonality in serialization and archive format - files that can be applied to any format as files when the class is not changed. Support for non-intrusive formulas. Category does not need to derive or implement specific member functions from a particular class. This is quite necessary for the case where we cannot or unwilling to modify classes. The interface of the file should be simple enough to make the work of establishing a new type of file easily. The file should support the XML format.

Other implementations I found some current implementation before starting this work.

MFC is an implementation of my very familiar. I have used it for a few years, and I found it very useful. However, it does not meet the requirements 1, 2, 3, 6, 7, 9. Despite this, this person is the most useful implementation I found. In addition, I found that some of the type of version -MFC implements this feature - support is indispensable in my program. For example, version 1.x transport programs often store more information than previously available data. The MFC is the only implementation of the version of the feature - although only the Most Derived Class. But there is a total of not good. In addition, MFC does not support serialization of STL containers, which is only for MFC container services. Commonc libraries [1] This is quite close to the implementation of the MFC and also solves the problem of partial MFC. It is portable, it creates portable file but does not support version functions. It correctly handles the storage of ruling and supports STL containers. It also solves the compression of the file (although I don't like her implementation). But this library needs a better document. It does not meet the requirements 2, 3, 7, 8, 9. Eternity [2] This is a "naked" :-). Its code is very beautiful but it does need a good document and more examples. If you don't fully learn his source code, you don't know how to use it. The latest version supports XML format. It does not meet the requirements 3, 6, 7?, 8, 9. Holub's Implementation [3] Its first makes me carefully think about the implementation of myself on serialization. If you are not holding a big and arrogant attitude, reading it will be quite interesting and worthwhile. It does not meet the requirements 2, 3, 4, 5, 6. S11n [13] The goal of this library is quite close. Some aspects are also quite similar. When writing this article, some of its issues is:

The portability of the code (1). The code can only be used in the recent GCC version. Independence (3) of class version. The class version is not supported. Demand (5). It does not have an automatic processing multi-pointer problem. I also summarized the same data structure as the diagram from the documentation. It has many different places with our implementation.

Revised 1 November, 2004

© Copyright Robert Ramey 2002-2004. Distributed Under The Boost Software License, Version 1.0. (See Accompanying File License_1_0.txt or copy at http://www.boost.org/license_1_0.txt )serialization Guide

A simple example

Non-intrusive version

Sequential members

Derived class

pointer

Array

STL container

Class version

Separateize SAVE / LOAD

file

Ar << Data;

AR & data; an output file and an output stream are very like. The data can be deposited in the << or & operator.

AR >> DATA;

AR & data;

An input file and output stream are also very similar. Use >> and & operators to obtain data from the file.

When these operators are used for basic data types, data is simple to store files / from files. When used for class type data, the class's Serialize function is called. Each Serialize function is stored and loaded into a class of data by those operators. This is a recursive process until all all storage / load work has been completed.

A simple example

<< and & operators are used inside the serialize function to store and load data members.

Demo.cpp demonstrates how to use our system. Demonstrate this library should be used by the simplest example of the code extracted.

#include

// include Headers That Implement A Archive In Simple Text Format

#include

#include

/

// GPS Coordinate

//

// illustrates serialization for a Simple Type

//

Class GPS_Position

{

Private:

Friend Class Boost :: Serialization :: Access;

// when the class archive corresponds to an output archive, the

// & operator is defined Similar to <<. Likewise, When the Class Archive

// is a type of infunt archive the & operator is defined Similar to >>.

Template

Void Serialize (Archive & Ar, Const Unsigned Int Version)

{

AR & Degrees;

Ar & minutes;

Ar & Seconds;

}

Int degreees;

Int minutes;

FLOAT Seconds;

PUBLIC:

GPS_POSition () {};

GPS_Position (int D, int M, float s):

Degrees (D), Minutes (M), Seconds (s)

{}

}

Int main () {

// Create and Open a Character Archive for Output

Std :: OFStream OFS ("FileName");

Boost :: Archive :: Text_oarchive OA (OFS);

// CREATE CLASS INSTANCE

Const GPS_Position G (35, 59, 24.567F);

// Write Class Instance to Archive

OA << g;

// Close Archive

OFS.CLOSE ();

// ... Some Time Later Restore The Class Instance To Its Orginal State

// Create and Open an Archive for Input

Std :: ifstream IFS ("FileName", std :: ios :: binary);

Boost :: Archive :: Text_iarchive IA (IFS);

// r r c s / / a

GPS_Position newg;

IA >> NewG;

// Close Archive

IFS.Close ();

Return 0;

}

Any class who wants to store by serialization must have a function to store its members to represent the status of this class. Correspondingly, the class that wants to obtain data from the sequence must define a function, and the function is obtained in the same order as the stored order. In the above example, this function appears in the form of a member template function serialize.

Non-intrusive version

The above example uses the invasive expression, that is, the definition of the class must be corresponding to the serialization requirements. This may be inconvenient in some cases. There is also an equivalent expression in this system:

#include

#include

Class GPS_Position

{

PUBLIC:

Int degreees;

Int minutes;

FLOAT Seconds;

GPS_POSition () {};

GPS_Position (int D, int M, float s):

Degrees (D), Minutes (M), Seconds (s)

{}

}

Namespace boost {

Namespace serialization {

Template

Void Serialize (Archive & Ar, GPS_Position & G, Const unsigned int version)

{

Ar & g.degrees;

Ar & g.minutes;

Ar & g.seconds;

}

} // Namespace Serialization

} // Namespace Boost

The Serialize function is no longer a member function in this example. But the work mode is almost the same as the function in the above example.

Non-intrusion is mainly used to change the definition of classes and want to join serialization support. In order to achieve this, the class must provide sufficient information and interface to rebuild the status of the class. In this example, we use our public data members - this is of course very uncommon. Serialized support can only be added without changing the classes when providing sufficient information and interface storage and loading classes.

Sequential members

A serialized class with sequentialized members:

Class bus_stop

{

Friend Class Boost :: Serialization :: Access;

Template

Void Serialize (Archive & Ar, Const Unsigned Int Version)

{

Ar & latitude;

AR & longitude;

}

GPS_Position Latitude;

GPS_Position longitude; protected:

Bus_stop (const gps_position & lat_, const gps_position & long_):

Latitude (lat_), longitude (long_)

{}

PUBLIC:

Bus_stop () {}

// See Item # 14 in Effective C by Scott Meyers.

// Re Non-Virtual Destructors in base classes.

Virtual ~ bus_stop () {}

}

It can be seen that there is no difference between the serialization method and the basic data type of the serialized member.

Note that the storage of a BUS_STOP class also calls Latitude and longitude's Serialize functions, which is defined in GPS_Position. This approach allows the entire data structure as long as you can store their roots.

Derived class

Detective classes should be responsible for calling the serialization functions of their base class.

#include

Class bus_stop_corner: public bus_stop

{

Friend Class Boost :: Serialization :: Access;

Template

Void Serialize (Archive & Ar, Const Unsigned Int Version)

{

// Serialize Base Class Information

Ar & Boost :: Serialization :: Base_Object (* this);

Ar & street1;

AR & street2;

}

Std :: string street1;

Std :: string street2;

Virtual std :: string description () const

{

Return Street1 "AND" street2;

}

PUBLIC:

Bus_stop_corner () {}

Bus_stop_corner (const gps_position & lat_, const gps_position & long_,

Const std :: string & s1_, const st :: string & s2_

):

Bus_stop (lat_, long_), street1 (s1_), street2 (S2_)

{}

}

Note that the derived class is how to call the sequence function of the base class. Don't call the base class of the Serialize function directly. Doing so seems to be work, but this will bypass code used to exclude redundant data, and bypass the version mechanism. Based on this reason, it is recommended to set Serialize as a private member function. Statement Friend Boost :: Serialization :: Access will ensure that Serialization Library can access private members of the class and the private function of the call class.

pointer

Suppose we now define an array of bus stops as a Bus Route. considering:

We may have several different bus stops (bus_stop as base classes) a specific bus_stop may not only appear in one line.

In an array of bus_stop pointers, a Bus Route is a natural way.

Class bus_route

{

Friend Class Boost :: Serialization :: Access;

Bus_stop * stops [10];

Template

Void Serialize (Archive & Ar, Const unsigned int version) {

INT I;

For (i = 0; i <10; i)

Ar & Stops [I];

}

PUBLIC:

Bus_route () {}

}

Each element of the STOPS array will be serialized. However, remembering each element of it is a pointer - what does this mean? The object pointing to the pointer can survive at another address when the next time is re-constructed. In order to complete the serialization of a pointer, only the value of the storage pointer is not enough, and the object to which it points to the same must also be stored. When the next pointer member is re-loaded, the object it points to it must have been established, but the pointer points to this new object.

All of this is completely complete by our library. The above code is that all the work you want to do correctly when a pointer is a pointer.

Array

Of course, the above example is still a bit more complex, we have a simpler way. Our sequence library automatic detection an object is an array, if it is generated with the above equivalent code. So the above code can be simplified:

Class bus_route

{

Friend Class Boost :: Serialization :: Access;

Bus_stop * stops [10];

Template

Void Serialize (Archive & Ar, Const Unsigned Int Version)

{

Ar & Stops;

}

PUBLIC:

Bus_route () {}

}

STL container

The above example uses a member array. When more common is that the program uses a STL container to achieve the same purpose. Our sequence reservoir includes all code required for serialized STL classes. So the following code is as you want to work.

#include

Class bus_route

{

Friend Class Boost :: Serialization :: Access;

Std :: List stops;

Template

Void Serialize (Archive & Ar, Const Unsigned Int Version)

{

Ar & Stops;

}

PUBLIC:

Bus_route () {}

}

Class version

Assume that we are now very satisfied with Bus_Route, we construct a program and release it. After a period of time, the discovery program needs to be enhanced, and BUS_ROUTE must make a corresponding change is the name of the driver. Our new version is as follows:

#include

#include

Class bus_route

{

Friend Class Boost :: Serialization :: Access;

Std :: List stops;

Std :: string driver_name;

Template

Void Serialize (Archive & Ar, Const Unsigned Int Version)

{

Ar & driver_name;

Ar & Stops;

}

PUBLIC:

Bus_route () {}

}

Very good, we did it. But what about those who use our previous version of the program? They may have a large number of file files constructed with previous programs, how do you continue to use in our new version of the program? Normally, our library stores a version number in each class that requires serialization. By default, this version number is 0. When the file is loaded, the serialization function will get the version number of the file, and use it we can reach the upwardly compatible purpose:

#include

#include

#include

Class bus_route

{

Friend Class Boost :: Serialization :: Access;

Std :: List stops;

Std :: string driver_name;

Template

Void Serialize (Archive & Ar, Const Unsigned Int Version)

{

// only save / load driver_name for newer archives

IF (Version> 0)

Ar & driver_name;

Ar & Stops;

}

PUBLIC:

Bus_route () {}

}

BOOST_CLASS_VERSION (Bus_Route, 1)

The version of the version is not necessarily considering the version of the maintenance file, and the file version is a collection consisting of the version included in it. Our library makes the new version of the program and the archive of the previous version of the program to maintain compatibility, and the job to do will not be more complicated than the above code.

Separateize SAVE / LOAD

Serialize is quite concisely ensured that the class is stored and loaded in the correct order - this is the key to a serialization system. However, in some cases, the load and storage operations are not necessarily similar to the example above. For example, this often occurs when a class evolution is multi-version:

#include

#include

#include

#include

Class bus_route

{

Friend Class Boost :: Serialization :: Access;

Std :: List stops;

Std :: string driver_name;

Template

Void Save (Archive & Ar, Const unsigned int version) Const

{

// Note, Version Is Always the Latest When Saving

Ar & driver_name;

Ar & Stops;

}

Template

Void Load (Archive & Ar, Const Unsigned Int Version)

{

IF (Version> 0)

Ar & driver_name;

Ar & Stops;

}

Boost_Serialization_Split_member ()

PUBLIC:

Bus_route () {}

}

BOOST_CLASS_VERSION (Bus_Route, 1)

Macro boost_serialization_split_member () generates code that correctly calls Save () and LOAD ().

file

The above discussion is mainly focused on increasing serialization in the class. But the real description of the data is implemented in the Archive class. Therefore, the serialized data stream is the product generated together with the types of files used. Separating the two is a critical design. This allows specific serialization processes for any file type.

In this guide, we only use a file type - text_oarchive used to store text_oarchive and Text_iarchive used to enter. Other file types from the library are identical (only one exception). Once a serialization function is defined for the class, the class can use any file to perform a specific serialization process.

If the various files provided can not meet the needs of a specific program, you can also construct your own file type or derive from the existing archive class. This will be discussed in the later manual.

Note that although our example is to use the same archive file in the same program, this is just for the purpose of demonstration. But you can still use files generated by different programs in different programs.

Complete demo - Demo.cpp made the following:

Establish a data structure composed of different stations, lines, and schedules. Show it. Sequence it serize it to the file "testfile.txt" reloads the data in another structure to display another data structure.

The output of the program is enough to prove that our system has reached 10 needs mentioned in Overview. The content of the file file can be displayed in the form of a normal ASCII file.

© Copyright Robert Ramey 2002-2004. Distributed Under The Boost Software License, Version 1.0. (See Accompanying File License_1_0.txt or copy at http://www.boost.org/license_1_0.txt)

转载请注明原文地址:https://www.9cbs.com/read-64528.html

New Post(0)