Monday, April 29, 2013

Boost Serialization (Part 1)

/**
 * The following is an example of how to use Boost Serialization.
 * After googling around, I wasn't able to find many quality
 * examples that did anything interesting. And those examples I
 * did find were always either too complicated or incomplete.
 * This example fully compiles, but make sure to link in
 * boost_serialization.
 */


// You must include this header in order to perform serialization
// on shared pointers in boost.
#include <boost/serialization/shared_ptr.hpp>
#include <boost/shared_ptr.hpp>

// This class is required for use of the macro BOOST_CLASS_EXPORT_GUID
#include <boost/serialization/export.hpp>

// These includes are necessary to build an archive. The archive
// handles operator &, which provides a generic serialization
// operation; this can serialize in or out.
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <iostream>

// My favorite stream type: stringstream. It's so flexible and
// easy to use.
#include <sstream>

#include <string>

/**
 * The character class, which is the class we are going to serialize.
 */
class Character
{
    public:
        Character() {}
        virtual ~Character() {}

        void setName(std::string const& n) { this->name = n; }
        std::string const& getName() const { return this->name; }

        void setAge(int a) { this->age = a; }
        int getAge() const { return this->age; }

        void dump()
        {
            std::cout << this->name << "(" << this->age << ")" << std::endl;
            dump_();
        }

        /**
         * This is a brilliant mechanism provided by boost. If the
         * method is never used it is never generated. The Archive
         * can provide either input or output serialization because
         * we pass by reference. Also, the Archive as a template
         * parameter allows us to serialize it in so many different
         * ways (xml, text, binary, etc.). Like I said, simply
         * brilliant design!
         */
        template <typename Archive>
        void serialize(Archive& ar, unsigned int const /* version */)
        {
            ar & this->name;
            ar & this->age;
        }

    private:
        std::string name;
        int age;

    private:
        virtual void dump_() { }
};

/**
 * This is a simple derived class that has a little more information
 * to it. This illustrates how boost takes care of serialization of
 * derived classes.
 */
class ComicCharacter : public Character
{
    public:

        ComicCharacter() : Character() { }

        template <typename Archive>
        void serialize(Archive& ar, unsigned int const version)
        {
            // This is the way that is outlined in the boost documentation
            // for serializing the derived/base classes. Without this line,
            // you will get: "unregistered void cast" errors.
            boost::serialization::void_cast_register( static_cast<ComicCharacter*>(0)
                                                    , static_cast<Character*>(0));

            Character::serialize(ar, version);
            ar & this->strip;
        }

        void setStrip(std::string const& s) { this->strip = s; }
        std::string const& getStrip() const { return this->strip; }

    private:
        std::string strip;   

    private:
        void dump_() { std::cout << "    from strip: " << this->strip << std::endl; }
};


BOOST_CLASS_EXPORT_GUID(Character, "Character")
BOOST_CLASS_EXPORT_GUID(ComicCharacter, "ComicCharacter")


/**
 * Main entry point.
 */
int main(int, char**)
{
    // Create a stream object to write our output archive to.
    std::stringstream out;

    // Create an archive. This passes an ostream object in as a place to
    // output the results of the serialization.
    boost::archive::text_oarchive oa(out);

    // Create a shared pointer to a comic character AS a
    // comic character.
    boost::shared_ptr<ComicCharacter> comicCharacter(new ComicCharacter());

    comicCharacter->setName("Charlie Brown");
    comicCharacter->setAge(8); // [2]
    comicCharacter->setStrip("Peanuts");       

    // When I write to the archive I want to make sure that the
    // operation occurs on the BASE CLASS. This illustrates that the
    // polymorphic data is also written to the stream.
    boost::shared_ptr<Character> baseCharacter = comicCharacter;

    // Finally now write the base object to the stream.
    oa << baseCharacter;

    std::string const contents = out.str();
    std::cout << "ARCHIVE CONTENTS: " << contents << std::endl;

    // Create a stream for input and populate it with the serialized
    // contents.
    std::stringstream in;
    in << contents;

    // Now we create a text input archive, again passing in an ostream
    // object.
    boost::archive::text_iarchive ia(in);
   
    // Create an empty Character shared pointer. Note that character is
    // the base class. We serialized a ComicCharacter into the archive-
    // therefore we should get one out.
    boost::shared_ptr<Character> mysteryCharacter;

    // Here is the deserialization call.
    ia >> mysteryCharacter;   

    // Finally to prove that we indeed received a Comic Character
    // named Charlie Brown:
    mysteryCharacter->dump();

}

#if 0

The following is the result of running this example:

ARCHIVE CONTENTS: 22 serialization::archive 10 0 1 2 14 ComicCharacter 1 0
0 13 Charlie Brown 8 7 Peanuts
Charlie Brown(8)
    from strip: Peanuts

In conclusion, using the serialization capabilities in boost to serialize
polymorphic types is difficult. Hopefully this thorough example helps
folks muddling their way through.
#endif

No comments:

Post a Comment