Chinaunix首页 | 论坛 | 博客
  • 博客访问: 818403
  • 博文数量: 756
  • 博客积分: 40000
  • 博客等级: 大将
  • 技术积分: 4980
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-13 14:40
文章分类

全部博文(756)

文章存档

2011年(1)

2008年(755)

我的朋友

分类:

2008-10-13 16:12:22

??? - Version 1.33.0

New Libraries

  • - Framework for defining streams, stream buffers and i/o filters, from Jonathan Turkanis.

Updated Libraries

  • : Introduced several new algorithms and improved existing algorithms:
    • , from Lauren Foutz and Scott Hill.
    • , from Kristopher Beevers and Jufeng Peng.
    • , from Doug Gregor and Indiana University.
    • , from Jeremy Siek, Janusz Piwowarski, and Doug Gregor.
    • has been updated, tested, and documented.
    • , from Jeremiah Willcock and Doug Gregor of Indiana University.
    • , from D. Kevin McGrath of Indiana University.
    • has been recast as an invocation of breadth_first_search and now supports graphs with multiple components.
    • now uses a relaxed heap [] as its priority queue, improving its complexity to O(V log V) and improving real-world performance for larger graphs.
    • now has a new, Spirit-based parser that works for all graph types and supports arbitrary properties on the graph, from Ron Garcia. The old, Bison-based GraphViz reader has been deprecated and will be removed in a future Boost release. also supports dynamic properties.
    • See the for additional changes and bug fixes.
  • :
    • New .
    • Added .
    • For a complete list of changes, see the library .
  • : Introduced the class, which provides dynamically-typed access to a set of property maps.
  • : added slot blocking/unblocking, from Frantz Maerten. Huge improvements to signal invocation performance from Robert Zeh.

序列化类简介:

Serialization

Tutorial


An output archive is similar to an output data stream. Data can be saved to the archive with either the << or the & operator:


ar << data;
ar & data;

An input archive is similar to an input datastream. Data can be loaded from the archive with either the >> or the & operator.


ar >> data;
ar & data;

When these operators are invoked for primitive data types, the data is simply saved/loaded to/from the archive. When invoked for class data types, the class serialize function is invoked. Each serialize function is uses the above operators to save/load its data members. This process will continue in a recursive manner until all the data contained in the class is saved/loaded.

These operators are used inside the serialize function> to save and load class data members.

Included in this library is a program called which illustrates how to use this system. Below we excerpt code from this program to illustrate with the simplest possible case how this library is intended to be used.


#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 input archive the & operator is defined similar to >>.
    template
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & degrees;
        ar & minutes;
        ar & seconds;
    }
    int degrees;
    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);
    // read class state from archive
    gps_position newg;
    ia >> newg;
    // close archive
    ifs.close();
    return 0;
}

For each class to be saved via serialization, there must exist a function to save all the class members which define the state of the class. For each class to be loaded via serialization, there must exist a function to load theese class members in the same sequence as they were saved. In the above example, these functions are generated by the template member function serialize.

The above formulation is intrusive. That is, it requires that classes whose instances are to be serialized be altered. This can be inconvenient in some cases. An equivalent alternative formulation permitted by the system would be:


#include 
#include 

class gps_position
{
public:
    int degrees;
    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

In this case the generated serialize functions are not members of the gps_position class. The two formulations function in exactly the same way.

The main application of non-intrusive serialization is to permit serialization to be implemented for classes without changing the class definition. In order for this to be possible, the class must expose enough information to reconstruct the class state. In this example, we presumed that the class had public members - not a common occurence. Only classes which expose enough information to save and restore the class state will be serializable without changing the class definition.

A serializable class with serializable members would look like this:


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(){}
};

That is, members of class type are serialized just as members of primitive types are.

Note that saving an instance of the class bus_stop with one of the archive operators will invoke the serialize function which saves latitude and longitude. Each of these in turn will be saved by invoking serialize in the definition of gps_position. In this manner the whole data structure is saved by the application of an archive operator to just its root item.

Derived classes should include serializations of their base classes.


#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 std::string & s2_
    ) :
        bus_stop(lat_, long_), street1(s1_), street2(s2_)
    {}
};

Note the serialization of the base classes from the derived class. Do NOT directly call the base class serialize functions. Doing so might seem to work but will bypass the code that tracks instances written to storage to eliminate redundancies. It will also bypass the writing of class version information into the archive. For this reason, it is advisable to always make member serialize functions private. The declaration friend boost::serialization::access will grant to the serialization library access to private member variables and functions.

Suppose we define a bus route as an array of bus stops. Given that

  1. we might have several types of bus stops (remember bus_stop is a base class)
  2. a given bus_stop might appear in more than one route.

it's convenient to represent a bus route with an array of pointers to bus_stop.


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 member of the array stops will be serialized. But, remember each member is a pointer - so what can this really mean? The whole object of this serialization is to permit reconstruction of the original data structures at another place and time. In order to accomplish this with a pointer, it is not sufficient to save the value of the pointer, rather the object it points to must be saved. When the member is later loaded, a new object has to be created and a new pointer has to be loaded into the class member.

All this is accomplished automatically by the serialization library. The above code is all that is necessary to accomplish the saving and loading of objects accessed through pointers.

The above formulation is in fact more complex than necessary. The serialization library detects when the object being serialized is an array and emits code equivalent to the above. So the above can be shortened to:


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(){}
};

The above example uses an array of members. More likely such an application would use an STL collection for such a purpose. The serialization library contains code for serialization of all STL classes. Hence, the reformulation below will also work as one would expect.


#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(){}
};

Suppose we're satisfied with our bus_route class, build a program that uses it and ship the product. Some time later, it's decided that the program needs enhancement and the bus_route class is altered to include the name of the driver of the route. So the new version looks like:


#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(){}
};

Great, we're all done. Except... what about people using our application who now have a bunch of files created under the previous program. How can these be used with our new program version?

In general, the serialization library stores a version number in the archive for each class serialized. By default this version number is 0. When the archive is loaded, the version number under which it was saved is read. The above code can be altered to exploit this


#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)

By application of versioning to each class, there is no need to try to maintain a versioning of files. That is, a file version is the combination of the versions of all its constituent classes. This system permits programs to be always compatible with archives created by all previous versions of a program with no more effort than required by this example.

The serialize function is simple, concise, and guarantees that class members are saved and loaded in the same sequence - the key to the serialization system. However, there are cases where the load and save operations are not as similar as the examples used here. For example, this could occur with a class that has evolved through multiple versions. The above class can be reformulated as:


#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)

The macro BOOST_SERIALIZATION_SPLIT_MEMBER() generates code which invokes the save or load depending on whether the archive is used for saving or loading.

Our discussion here has focused on adding serialization capability to classes. The actual rendering of the data to be serialized is implemented in the archive class. Thus the stream of serialized data is a product of the serialization of the class and the archive selected. It is a key design decision that these two components be independent. This permits any serialization specification to be usable with any archive.

In this tutorial, we have used a particular archive class - text_oarchive for saving and text_iarchive for loading. There other archives included in with the library and their interfaces are identical (with one exception). Once serialization has been defined for a class, that class can be serialized to any type of archive.

If the current set of archive classes doesn't provide the attributes, format, or behavior needed for a particular application, one can either make a new archive class or derive from an existing one. This is described later in the manual.

Note also that though our examples save and load the program data to an archive within the same program, this merely a convenience for purposes of illustration. In general, the archive may or may not be loaded by the same program that created it.

The complete demo program - does the following:

  1. Creates a structure of differing kinds of stops, routes and schedules
  2. Displays it
  3. Serializes it to a file named "testfile.txt" with one statement
  4. Restores to another structure
  5. Displays the restored structure

is sufficient to verify that all the originally stated requirements for a serialization system are met with this system. The can also be displayed as serialization files are ASCII text.


© Copyright 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at )


 


 

iostream介绍“

Tutorial

Defining Streams and Stream Buffers for a New Data Source

Suppose we want to define a standard stream buffer to read from a new type of input device. Typically this means deriving a new class from a specialization of std::basic_streambuf and overriding the protected virtual function underflow, and possibly also uflow and xsgetn. If we wish to be able to put back characters to the stream buffer, we also need to override pbackfail. If we wish to add buffering, we need to allocate a buffer and use the buffer manipulation functions setg, eback, gptr, egptr and gbump.

This process can be confusing to those who do not routinely work with the protected virtual interface of std::basic_streambuf. More importantly, most of the work involved has very little to do with the new data source we wish to access. Very often, the relevant behavior of a new data source can be represented by a single member function, prototyped as follows:

        std::streamsize read(char* s, std::streamsize n);

The function reads up to n characters into the buffer s, returning the number of characters read and throwing exceptions to indicate errors. If an integer less than n is returned, the end of the stream has been reached.

The Iostreams Library allows users to create a standard input stream or stream buffer by defining a classes with a single member function read and several member types. Buffering and the ability to put back characters come for free. For a simple example, suppose we want to define a stream buffer to read a random sequence of characters.

We first define a as follows:

    #include 
    #include  // source

    using namespace std;
    using namespace boost::io;

    struct random_source : public source {
        random_source(unsigned int seed = 0) { srand(seed); }
        std::streamsize read(char* s, std::streamsize n)
            {
                for (streamsize z = 0; z < n; ++z) 
                    s[z] = (char) (rand() * 256 / RAND_MAX);
                return n;
            }
    };

Here is a convenience base class which provides several member types as well as default implementations of several member functions. (The member functions are not needed here.) Given the above definition, we can construct a standard stream buffer as follows:

    streambuf_facade in(random_source(5));  
    ...                             // read from stream
    in.close();                     // close stream
    in.open(random_source(634875)); // use a new seed
    ...                             // read from stream

We can also define a standard input stream:

    stream_facade  in(random_source(5));

Both streambuf_facade and stream_facade have constructors and overloads of open which forward their arguments to Filter or Device constructor. Therefore, the first example could have been written:

    streambuf_facade in(5);  
        ...
    in.close();
    in.open(634875); 
        ...

Defining Streams and Stream Buffers for a New Data Sink

The case is similar if we wish to define a standard stream buffer to write to a new type of output device. Instead of overriding the protected virtual functions overflow, xsputn and sync and making use of the buffer manipulation functions setp, pbase, pptr, epptr and pbump, we simply define a class with several member types and a member function write prototyped as follows:

        void write(const char* s, std::streamsize n);

The function writes n characters to the output device, throwing exceptions to indicate errors. For a simple example, suppose we want to define a stream buffer to write characters to the end of a standard vector. We can define a as follows:

    #include 
    #include  // sink

    using namespace std;
    using namespace boost::io;

    struct vector_sink : public sink {
        vector_sink(vector<char>& vec) : vec(vec) { }
        void write(const char* s, std::streamsize n)
            {
                vec.insert(vec.end(), s, s + n);
            }
        vector<char>& vec;
    };

Here is a convenience base class which provides several member types as well as default implementations of several member functions. (The member functions are not needed here.) Given the above definition, we can construct a standard stream buffer as follows:

    vector<char>                  v;
    streambuf_facade out(vector_sink(v));  // stream buffer which appends to v

We can also define a standard output stream:

    stream_facade out(vector_sink(v));

It's tempting to write:

    vector<char>                  v;
    streambuf_facade out(v); // error!!

Unfortunately, constructor-argument-forwarding only works for constuctors which take their arguments by value or by const reference; the vector_sink constructor takes a non-const reference.

For easier ways to append to STL sequences, see .

Filtering Input

Suppose we want to filter data read from a standard input stream. Let us say we wish to read only the alphabetic characters. We can do this by defining a component — an — which reads data from a provided Source and modifies it before returning it to the user:

    #include                  // isalpha
    #include                   // EOF
    #include    // input_filter
    #include  // get

    using namespace std;
    using namespace boost::io;

    struct alphabetic_input_filter : public input_filter {
        template<typename Source>
        int get(Source& src)
            {
                int c;
                while ((c = boost::iostreams::get(src)) != EOF && !isalpha(c))
                    ;
                return c;
            }
    };

Here is a convenience base class which provides several member types as well as default implementations of several member functions. (The member functions are not needed here.) The function reads a character from an arbitrary Source; it is provided by the Iostreams Library to allow all Sources to be accessed with a single interface. Given the above definition, we can read filtered data from standard input as follows:

    filtering_streambuf in;
    in.push(alphabetic_input_filter());
    in.push(cin);

The last item added to the chain could be any . E.g., we could read a random stream of alpabetic characters using the class random_source defined above:

    filtering_streambuf in;
    in.push(alphabetic_input_filter());
    in.push(random_source());

We could also add any number of to the chain before adding the .

These examples all remain valid if filtering_stream is substituted everywhere for filtering_streambuf. They could also use the following convenience typedefs:

    typedef filtering_streambuf filtering_istreambuf;
    typedef filtering_stream    filtering_istream;

Filtering Output

Suppose we want to filter data written to a standard output stream. Let us say we wish to transform each alphabetic character to upper case. We can do this by defining a component — an — which modifies data provided by the user before writing it to a given Sink:

    #include                  // toupper
    #include    // output_filter
    #include  // put

    using namespace std;
    using namespace boost::io;

    struct toupper_output_filter : public output_filter {
        template<typename Sink>
        void put(Sink& snk, char c) 
        { 
            boost::iostreams::put(snk, toupper(c)); 
        }
    };

Here is a convenience base class which provides several member types as well as default implementations of several member functions. (The member functions are not needed here.) The function writes a character to an arbitrary Sink; it is provided by the Iostreams Library to allow all Sinks to be accessed with a single interface. Given the above definition, we can write filtered data to standard output as follows:

    filtered_streambuf out;
    out.push(toupper_output_filter());
    out.push(cout);

The last item added to the chain could be any . E.g., we could append uppercase characters to a standard vector using the class vector_sink defined above:

    vector<char>               v;
    filtered_streambuf out;
    out.push(toupper_output_filter());
    out.push(vector_sink(v));

We could also add any number of to the chain before adding the .

These examples all remain valid if filtering_stream is substituted everywhere for filtering_streambuf. They could also use the following convenience typedefs:

    typedef filtering_streambuf filtering_ostreambuf;
    typedef filtering_stream    filtering_ostream;

Efficient Filtering: Multi-Character Filters

A complex filtering operation will often not be inlinable. In such cases, each time a character is read from an ordinary one incurs the overhead of a call to its member function get. To avoid this overhead, the library allows users to define Filters which can process several characters at once by implementing a member function read. This function will typically be called only when the buffer associated with the Filter by the Iostreams Library becomes full. For example, the following is a multi-character implementation of the alphabetic_input_filter described .

    #include                  // isalpha
    #include                   // EOF
    #include    // multi_char_input_filter
    #include  // get

    using namespace std;
    using namespace boost::io;

    struct alphabetic_input_filter : public multi_char_input_filter {
        template<typename Source>
        streamsize read(Source& src, char* s, streamsize n)
            {
                int   c;
                char* first = s;
                char* last  = s + n;
                while ( first != last &&
                        (c = boost::iostreams::get(src)) != EOF &&
                        isalpha(c) )
                {
                    *first++ = c;
                }
                return static_cast(first - s);
            }
    };

OutputFilters can also be . For example:

    #include                // toupper
    #include  // multichar_output_filter

    using namespace std;
    using namespace boost::io;

    struct toupper_filter : public multichar_output_filter {
        template<typename Sink>
        void write(Sink& snk, const char* s, streamsize n) 
            { 
                while (n-- > 0)
                    boost::iostreams::put(snk, toupper(*s++));
            }
    };

This is an instance of a limitation of C++ known as the forwarding problem (see ).

Technically, boost::iostreams::get and boost::iostreams::read require that a Source be .

Technically, boost::iostreams::put requires that a Sink be .

里面已经内部有了zlib,bzip


--------------------next---------------------

阅读(420) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~