Click here to Skip to main content
15,881,757 members
Articles / Desktop Programming / MFC

Persistence is the Key

Rate me:
Please Sign up or sign in to vote.
4.73/5 (16 votes)
24 Jan 2013Ms-PL16 min read 79K   563   42  
Tutorial for using the Calvin C++ persistence library.
#ifndef CALVIN_H
#define CALVIN_H

/*-----------------------------------------------------------------------------
File  :	Calvin.H
Title :	Calvin persistence library
Owner :	Jay Kint

	Copyright (C) 2005, Jay Kint, All Rights Reserved
-------------------------------------------------------------------------------
Description:
This library is licensed under the BSD license.  You are free to use it and 
included it any source code, though original copyright is retained by Jay
Kint.

It requires the use of the boost libraries, found at www.boost.org.

This scheme I'm working on seems to be a very small object database.  It uses
names to identify objects, and each object is stored separately in its own
file/record.  The downside to this is that each object to be stored must
have some unique identifier.  This unique identifier can be a type selected
by the user of the library, as long as it can be converted to a string using
the << operator.

The way this works is that when an object is saved, it is written in its
own record. When someone loads an object, the new object is put into a
registry.  When others wish to load that object, the regsitry is checked
and that object is returned instead of a new object.

This means that creation for a persistent object must be controlled.  A
small price to pay I would think.

The main reasoning for using a scheme like this is that I would like to
use the same module to save data, but not necessarily in the same map
or relationship.  Consider having two programs, one that is a game that
loads many of the objects in a heirarchy such as a level, and another being
a tool that converts objects from source material to compatible for the game.
Using any other library, I would have to replicate the object heirarchy exactly and 
then save it.  Using this scheme, I can convert the objects individually and 
the game can still load them as a heirarchy later if it so desires.

*/



//-----------------------------------------------------------------------------
//							Include Files
//-----------------------------------------------------------------------------

// module includes

// system includes
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/variant.hpp>
#include <boost/bind.hpp>
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <string>
#pragma warning( disable: 4996 )
#include <list>
#include <vector>
#include <deque>
#include <sstream>
#include <iostream>
#include <typeinfo>
#if defined(_DEBUG)
#include <crtdbg.h>
#endif

// selective error handling
// throw exception when exceptions are enabled, otherwise call the function 
// throw_exception.  throw_exception must be defined by the user or a link
// error will occur if that option is chosen.
namespace calvin {

class calvin_exception : public std::exception {
public:
      calvin_exception( std::string& what ) : std::exception( what.c_str() ) {}
      calvin_exception( const char* what ) : std::exception( what ) {}
};

}

#if !defined(NO_CALVIN_EXCEPTIONS)

#define CALVIN_ERROR(x)    throw calvin_exception(x)

#else

// prototype for user defined function that handles errors from the calvin library.
namespace calvin {
    void throw_exception( calvin::calvin_exception const& e );
}
#define CALVIN_ERROR(x)    calvin::throw_exception( calvin_exception( x ))

#endif

// these two should be defined if calvin is being used in a DLL that
// is linked by an application.
// For a DLL, define CALVIN_DECLSPEC=__declspec(dllexport)
// For an EXE that links the DLL, define CALVIN_EXPORT=extern and define CALVIN_DECLSPEC=__declspec(dllimport)
#if defined(CALVIN_DLL) && defined(CALVIN_SOURCE)
// DLL being compiled
#define CALVIN_EXPORT   
#define CALVIN_DECLSPEC __declspec(dllexport)
#elif defined(CALVIN_DLL)
// DLL being imported
#define CALVIN_EXPORT   extern
#define CALVIN_DECLSPEC __declspec(dllimport)
#else
// static library
#define CALVIN_EXPORT
#define CALVIN_DECLSPEC
#endif




//-----------------------------------------------------------------------------
//							Namespace
//-----------------------------------------------------------------------------


namespace calvin {


//-----------------------------------------------------------------------------
//							Types and Structures
//-----------------------------------------------------------------------------

// convenience typedefs

// forward declarations
// used by classes in DLLs that wish to be persistent

template <typename Type>
struct allow_persistence;

// base class for those who wish to be able to be persisted
// to some sort of storage.
template <typename Key>
class persistent {
 public:
    // Types and constants
    typedef boost::variant< persistent<Key>*, boost::weak_ptr<persistent<Key> > > reg_entry;
    typedef std::deque< reg_entry > registry;
    typedef Key is_persistent;
	// Constructors
	// Destructor
    ~persistent(void);
	// Operations
    // Accessors
    const Key& name(void) const { return _name;  }
	// Class Functions
 protected:
	// Constructors
    persistent( void );
    persistent( const Key& key );
	// Attributes
    static const int version = 1;
	// Operations
	// Class Attributes
	// Class Functions
 private:
	// Attributes
    Key _name;
	// Operations
    void set_name( const Key& name )
    {
        _name = name;
    }
	// Class Attributes
#pragma warning( push )
#pragma warning( disable: 4251 )
    static registry registry_;
#pragma warning( pop )
	// Class Functions
    //static void PrintRegistry(void);    // debug function to dump what's in the registry
template <typename T> friend class archive;
template <typename T> friend struct allow_persistence;
};

// specialization to be used by objects that need to have serialize called but
// aren't meant to have a name.  Usually small objects that might have a pointer to
// some other object are candidates for this specialization.
template <>
class persistent<void> {
public:

protected:
    static const int version = 1;

private:
template <typename T> friend class archive;
template <typename T> friend struct allow_persistence;
};

struct type_registry {

    static CALVIN_DECLSPEC type_registry* head_;

    type_registry* _next;
    std::string _type_name;

    type_registry( void )
    {
        _next = head_;
        head_ = this;
    }

    type_registry( std::string& type_name )
        : _type_name( type_name )
    {
        _next = head_;
        head_ = this;
    }

    virtual ~type_registry( void ) {}

    virtual void* instantiate( void ) = 0;
};


// helper class that allows the persistence module to give persistence to a class
// with a simple declaration.  The class must also have a default constructor 
// and a template serialize method.
template <typename Type>
struct allow_persistence : public type_registry {

    allow_persistence( void )
    {
        _type_name = typeid(Type).name();
    }

    virtual ~allow_persistence( void )
    {
    }

    template <typename T, class Stream>
    static Stream& serialize( Stream& s, T* t, unsigned int version )
    {
        return t->serialize( s, version );
    }

    template <typename T, class Stream>
    static Stream& serialize( Stream& s, boost::shared_ptr<T> t, unsigned int version )
    {
        return t->serialize( s, version );
    }

    template <typename T, typename Key>
    static boost::shared_ptr<T> shared_ptr_cast( boost::shared_ptr< calvin::persistent<Key> >& p )
    {
        calvin::persistent<Key>* temp = p.get();
        T* t = static_cast<T*>(temp);
        boost::shared_ptr<T> s;
        s.px = t;
        s.pn = p.pn;
        return s;
    }

    template <typename T, typename Key>
    static boost::shared_ptr<T> shared_ptr_cast( calvin::persistent<Key>* p )
    {
        T* t = static_cast<T*>(p);
        boost::shared_ptr<T> s(t);
        return s;
    }

    virtual void* instantiate( void )
    {
        return new Type();
    }

    template <typename T, typename Key>
    static boost::shared_ptr<T> instantiate( const Key& key, std::string& type )
    {
        static allow_persistence<T> static_type_registry;

        for( type_registry* current = allow_persistence<T>::head_; current != NULL; current = current->_next ) {
            if( current->_type_name == type ) {
                boost::shared_ptr<T> t( reinterpret_cast<T*>(current->instantiate()));
                t->set_name( key );
                return t;
            }
        }

        CALVIN_ERROR( "Type not found." );        
        return boost::shared_ptr<T>();
    }

    template <typename T, typename Key>
    static T* instantiate_ptr( const Key& key )
    {
        T* t = new T( key );
        return t;
    }

    template <typename T>
    static unsigned int version(void)
    {
        return T::version;
    }

    template <typename Key>
    static typename persistent<Key>::registry& registry( void )
    {
        return persistent<Key>::registry_;
    }
};

template <>
struct allow_persistence<void> {

    template <typename Stream, typename T>
    static Stream& serialize( Stream& s, T& t ) {
        return t.serialize( s, T::version );
    }
};

// IfTypeElse
// compile time type selection that determines which function to call at 
// compile time based on the two types True and False
// see below for an example of its use
template <bool Cond, typename True, typename False>
struct IfTypeElse {
    typedef True result;
};

template <typename True, typename False>
struct IfTypeElse<false, True, False> {
    typedef False result;
};


// is_persistent
// used to see if something inherits from Persistent by having the
// is_persistent type defined

template <typename T>
struct is_persistent {

    typedef char test_true;
    typedef struct { char dummy[2]; } test_false;

    template <typename P> static test_true test( typename P::is_persistent * );
    template <typename P> static test_false test( ... );

    static const bool value = ( sizeof( test<T>(0) ) == sizeof(test_true));
};


// names of the fields in a class.  These are used
// by streams that require text descriptions associated with
// the data, such as an XML or database.  Using this
// allows us to circumvent requiring a separate serialize method
// for named fields vs non named fields.
template <typename T>
struct Names {

    Names( const char* n1,
        const char* n2 = NULL,
        const char* n3 = NULL,
        const char* n4 = NULL,
        const char* n5 = NULL,
        const char* n6 = NULL,
        const char* n7 = NULL,
        const char* n8 = NULL,
        const char* n9 = NULL,
        const char* n10 = NULL,
        const char* n11 = NULL,
        const char* n12 = NULL,
        const char* n13 = NULL,
        const char* n14 = NULL,
        const char* n15 = NULL,
        const char* n16 = NULL,
        const char* n17 = NULL,
        const char* n18 = NULL,
        const char* n19 = NULL,
        const char* n20 = NULL )
    {
        names[0] = n1;
        names[1] = n2;
        names[2] = n3;
        names[3] = n4;
        names[4] = n5;
        names[5] = n6;
        names[6] = n7;
        names[7] = n8;
        names[8] = n9;
        names[9] = n10;
        names[10] = n11;
        names[11] = n12;
        names[12] = n13;
        names[13] = n14;
        names[14] = n15;
        names[15] = n16;
        names[16] = n17;
        names[17] = n18;
        names[18] = n19;
        names[19] = n20;
    }

    const char* Field( int f )
    {
        _ASSERT( names[f] != NULL );
        return names[f];
    }

    const char* names[20];
};

template <typename T>
Names<T> make_names( T, const char* f0, const char* f1 = NULL, const char* f2 = NULL, const char* f3 = NULL,
                     const char* f4 = NULL, const char* f5 = NULL, const char* f6 = NULL, const char* f7 = NULL,
                     const char* f8 = NULL, const char* f9 = NULL, const char* f10 = NULL, const char* f11 = NULL,
                     const char* f12 = NULL, const char* f13 = NULL, const char* f14 = NULL, const char* f15 = NULL,
                     const char* f16 = NULL, const char* f17 = NULL, const char* f18 = NULL, const char* f19 = NULL )
{
    return Names<T>( f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19 );
}


// archive
// an archive is a factory of streams.  Given a key, it resolves the key into
// a stream that can then load or save an object.  The streams must be Standard C++ 
// istream or ostream compliant.  Other than that, it's easy. :)

template <typename Key>
class CALVIN_DECLSPEC archive {
 public:
    // Types and constants
	// Constructors
    archive( void ) {}
	// Destructor
    ~archive( void ) {}
	// Operations
    template <typename T>
    bool save( T* resource );
    template <typename T>
    bool save( boost::shared_ptr<T> resource );
    //template <typename T>
    //boost::shared_ptr<T> load( const Key& name );
    template <typename T>
    boost::shared_ptr<T> load( const Key& name );
    template <typename T>
    T* load_ptr( const Key& name );
    // Accessors
	// Class Functions
    // a default archive can be set so that the normal save and load
    // functions can be used rather than the save and load functions
    // on a specific archive.  This is used both for saving and 
    // loading.
    static void set_default_archive( archive<Key>* default_archive ) { default_archive_ = default_archive; }
    static archive<Key>* current_archive( void ) { return current_archive_; }
 protected:
	// Constructors
	// Attributes
	// Operations
	// Class Attributes
	// Class Functions
 private:
	// Attributes
	// Operations
    virtual boost::shared_ptr<std::ostream> resolve_output( const Key& ) = 0;
    virtual boost::shared_ptr<std::istream> resolve_input( const Key& ) = 0;
	// Class Attributes
    // these are used by the internal operator overloads to save persistent objects
    // within persistent objects to the same archive (though usually different streams)
    static archive<Key>* default_archive_;
    static archive<Key>* current_archive_;
	// Class Functions
template <typename T> friend bool save( T* re );
template <typename T, typename Key> friend T* load( const Key& );
};


// visitor used to determine if two keys are equal by the boost::variant in the
// registry.  A boost::variant is used to keep track of instances 
template <typename Key>
class are_names_equal : public boost::static_visitor<int> {

public:
    const Key& _name;

    are_names_equal( const Key& name ) : _name( name ) {}

    int operator()( boost::weak_ptr< persistent<Key> > o ) const
    {
        if( o.lock() == NULL )
            return false;
        if( o.lock()->name() == _name )
            return true;

        return false;
    }

    int operator()( persistent<Key>* o ) const
    {
        if( o == NULL )
            return false;
        if( o->name() == _name )
            return true;

        return false;
    }
};

//-----------------------------------------------------------------------------
//							Constants and Definitions
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//							Variables
//-----------------------------------------------------------------------------

template <typename Key>
typename persistent<Key>::registry persistent<Key>::registry_;

template <typename Key>
archive<Key>* archive<Key>::default_archive_;

template <typename Key>
archive<Key>* archive<Key>::current_archive_;


// explicit instantiation for filesys_archive in archive.h
// You muse use similar to these if you define persistent classes and archives
// that use other types for keys and put them in a .h file included by
// the application that imports it.  See the comments for the defintion 
// of CALVIN_EXPORT and CALVIN_DECLSPEC.
#pragma warning(push)
#pragma warning(disable:4231)
CALVIN_EXPORT template class CALVIN_DECLSPEC persistent<std::string>;

CALVIN_EXPORT template CALVIN_DECLSPEC persistent<std::string>::registry;

CALVIN_EXPORT template persistent<std::string>::registry persistent<std::string>::registry_;

#pragma warning(pop)


//-----------------------------------------------------------------------------
//							Functions
//-----------------------------------------------------------------------------

template <typename Key>
persistent<Key>::persistent( void ) : _name() {}         // base used by instances not meant to be persistent

template <typename Key>
persistent<Key>::persistent( const Key& key ) : _name( key ) 
{
    if( _name == Key() )
        return;
    for( registry::iterator i = persistent<Key>::registry_.begin(); i != persistent<Key>::registry_.end(); ++i ) {

        if( boost::apply_visitor( are_names_equal<Key>( key ), (*i) )) {
            CALVIN_ERROR( "Name already used" );
        }
        //if( boost::weak_ptr<Key> w = (*i).get<boost::weak_ptr<Key> >() ) {
        //    if( w.lock()->name() == key )
        //        CALVIN_ERROR( "Name already used" );
        //}
        //else if( persistent<Key>* t = (*i).get<persistent<Key> >() ) {
        //    if( t == NULL )
        //        CALVIN_ERROR( "Incorrect value in registry" );
        //    if( t->name() == key )
        //        CALVIN_ERROR( "Name already used" );
        //}
        //else {
        //    CALVIN_ERROR( "Incorrect type in registry" );
        //}
    }
    // save it in the registry
    reg_entry re;
    re = this;
    registry_.push_back( re );
}

template <typename Key>
persistent<Key>::~persistent(void)
{
    if( _name == Key() )
        return;
    // remove the weak_ptr reference from the registry
    registry::iterator i;
    for( i = registry_.begin(); i != registry_.end(); ++i ) {
        //if( (*i)._ptr.px->_name == _name )
        //    break;       
        if( boost::apply_visitor( are_names_equal<Key>( _name ), (*i) )) {
            break;
        }
    }
    if( i != registry_.end() )
        registry_.erase(i);
}

// persistent<void>
template <typename T, typename Stream>
struct void_reader {
    static void func( Stream& s, T& t ) {
        calvin::allow_persistence<void>::serialize( s, t );
    }
};

template <typename T, typename Stream>
struct void_writer {
    static void func( Stream& s, T& t ) {
        calvin::allow_persistence<void>::serialize( s, t );
    }
};

// values
template <typename T, typename Stream>
struct reader {
    static void func( Stream& s, T& t) {
		s.read( (char*) &t, sizeof(T) );
        return;
    }
};

template <typename T, typename Stream>
struct writer {
    static void func( Stream& s, T t) {
        s.write( (const char*) &t, sizeof(T));
        return;
    }
};

template <typename T, typename Stream>
Stream& operator^( Stream& s, T& t )
{
    BOOST_STATIC_ASSERT(( boost::is_same< std::ostream, Stream >::value || 
                          boost::is_same< std::istream, Stream >::value ));

	IfTypeElse<
       boost::is_same< std::ostream, Stream >::value,
        IfTypeElse<
            boost::is_base_and_derived< calvin::persistent<void>, T >::value,
            calvin::void_writer< T, Stream >,
            calvin::writer< T, Stream > >::result,
        IfTypeElse<
            boost::is_base_and_derived< calvin::persistent<void>, T >::value,
            calvin::void_reader< T, Stream >,
            calvin::reader< T, Stream > >::result
	>::result::func( s, t );

	return s;
}

// pointers
template <typename T, typename Stream>
struct writer<T*, Stream> {

	template <typename T, typename Stream>
	struct write_persistent {

		static void func( Stream& s, T* t )
		{
            s ^ (std::string&) t->name();
            archive<T::is_persistent>::current_archive()->save( t );
		}
	};

	template <typename T, typename Stream>
	struct write_pointer {

		static void func( Stream& s, T* t )
		{
			// s.write( (const char *) t, sizeof(T) );
			s ^ *t;
		}
	};

	static void func( Stream& s, T* t )
	{
		IfTypeElse<
            is_persistent< T >::value,
			write_persistent< T, Stream >,
			write_pointer< T, Stream >
		>::result::func( s, t );
	}
};

template <typename T, typename Stream>
struct reader< T*, Stream > {

	template <typename T, typename Stream>
	struct read_pointer {

		static void func( Stream& s, T*& t )
		{
			// s.read( (char *) t, sizeof(T));
            if( t == NULL )
                t = new T;      // is this necessary?
			s ^ *t;
		}
	};

	template <typename T, typename Stream>
	struct read_persistent {

		static void func( Stream& s, T*& t )
		{
            T* temp;
			// read in name as string
			T::is_persistent name;
			s ^ name;
			// load_ptr is a special method meant to be used internally only
			temp = archive<T::is_persistent>::current_archive()->load_ptr<T>( name );
            t = temp;
		}
	};

	static void func( Stream& s, T*& t ) {

		IfTypeElse<
            is_persistent<T>::value,
			read_persistent< T, Stream >,
			read_pointer< T, Stream >
		>::result::func( s, t );
	}
};

template <typename T, typename Stream >
Stream& operator^( Stream& s, T*& t )
{
    // for now just make sure that it's one or the other.  To handle iostream would
    // most likely require a streambuf hack that would get the openmode and even
    // then a run time error would happen when it was opened for both input and
    // output as that makes it impossible to determine which one of these to call.
    BOOST_STATIC_ASSERT(( boost::is_same< std::ostream, Stream >::value || 
                          boost::is_same< std::istream, Stream >::value ));

	IfTypeElse<
        boost::is_same< std::ostream, Stream >::value,
        calvin::writer< T*, Stream >,
        calvin::reader< T*, Stream >
    >::result::func( s, t );

    return s;
}

// boost::shared_ptr
template <typename T, typename Stream>
struct writer< boost::shared_ptr<T>, Stream> {

	template <typename T, typename Stream>
	struct write_persistent {

        static void func( Stream& s, boost::shared_ptr<T> t )
		{
            s ^ (std::string&) t->name();
            archive<T::is_persistent>::current_archive()->save( t.get() );
		}
	};

	template <typename T, typename Stream>
	struct write_pointer {

		static void func( Stream& s, boost::shared_ptr<T> t )
		{
			// s.write( (const char *) t, sizeof(T) );
			s ^ *t;
		}
	};

	static void func( Stream& s, boost::shared_ptr<T> t )
	{
		IfTypeElse<
            is_persistent< T >::value,
			write_persistent< T, Stream >,
			write_pointer< T, Stream >
		>::result::func( s, t );
	}
};

template <typename T, typename Stream>
struct reader<boost::shared_ptr<T>, Stream > {

	template <typename T, typename Stream>
	struct read_pointer {

		static void func( Stream& s, boost::shared_ptr<T>& t )
		{
			s ^ *t;
		}
	};

	template <typename T, typename Stream>
	struct read_persistent {

		static void func( Stream& s, boost::shared_ptr<T>& t )
		{
			// read in string of name of 
            T::is_persistent name;
			s ^ name;
            boost::shared_ptr<T> temp( archive<T::is_persistent>::current_archive()->load<T>( name ));
            t = temp;
		}
	};

	static void func( Stream& s, boost::shared_ptr<T>& t ) {

		IfTypeElse<
            is_persistent<T>::value,
			read_persistent< T, Stream >,
			read_pointer< T, Stream >
		>::result::func( s, t );
	}
};

template <typename T, typename Stream>
Stream& operator^( Stream& s, boost::shared_ptr<T>& t )
{
    BOOST_STATIC_ASSERT(( boost::is_same< std::ostream, Stream >::value || 
                          boost::is_same< std::istream, Stream >::value ));

    IfTypeElse<
        boost::is_same< std::ostream, Stream>::value,
        calvin::writer< boost::shared_ptr<T>, Stream >,
        calvin::reader< boost::shared_ptr<T>, Stream >
    >::result::func( s, t );

    return s;
}

// arrays
template <typename T, int N, typename Stream>
struct array_writer {

    static void func( Stream& s, T (&t)[N], int n )
    {
        s ^ n;
        // s.write( t, sizeof(T) * n );
        for( int i = 0; i < n; ++i ) {
             s ^ t[i];
        }
    }
};

template <typename T, int N, typename Stream>
struct array_reader {

    static void func( Stream& s, T (&t)[N], unsigned int n )
    {
		size_t size;
		s.read( (char*) &size, sizeof( size_t ) );
        _ASSERT( size <= n );
        // s.read( t, sizeof(T) * n );
        for( unsigned int i = 0; i < size; ++i ) {
             s ^ t[i];
        }
    }
};

template <typename T, int N, typename Stream>
Stream& operator^( Stream& s, T (&t)[N] )
{
    BOOST_STATIC_ASSERT(( boost::is_same< std::ostream, Stream >::value || 
                          boost::is_same< std::istream, Stream >::value ));

	IfTypeElse<
        boost::is_same< std::ostream, Stream >::value,
		calvin::array_writer< T, N, Stream >,
		calvin::array_reader< T, N, Stream >
	>::result::func( s, t, N );

	return s;
}

// pointer arrays

template <typename T>
struct PtrArray {

    unsigned int n;
    T*& p;

    PtrArray( T*& p, unsigned int n ) : p(p), n(n) {}
};

template <typename T, typename Stream>
struct ptr_writer {

    static void func( Stream& s, T*& p, unsigned int n )
    {
        s ^ n;
        s.write( (const char*) p, sizeof(T) * n );
    }
};

template <typename T, typename Stream>
struct ptr_reader {

    static void func( Stream& s, T*& p, unsigned int n )
    {
		int size;
		s.read( (char*) &size, sizeof( int ) );
        p = new T[ size ];
        s.read( (char*) p, sizeof(T) * size );
    }
};

// specialized for void where the count is 
// for bytes, since void has no sizeof
template <>
struct PtrArray<void> {

    unsigned int n;
    void*& p;

    PtrArray( void*& p, unsigned int n ) : p(p), n(n) {}
};

template <typename Stream>
struct ptr_writer<void, Stream> {

    static void func( Stream& s, void*& p, unsigned int n )
    {
        s ^ n;
        s.write( (const char*) p, n );
    }
};

template <typename Stream>
struct ptr_reader<void, Stream> {

    static void func( Stream& s, void*& p, unsigned int n )
    {
		int size;
		s.read( (char*) &size, sizeof( int ) );
        p = new char[ size ];
        s.read( (char*) p, size );
    }
};

template <typename T, typename Stream>
Stream& operator^( Stream& s, PtrArray<T>& p )
{
    BOOST_STATIC_ASSERT(( boost::is_same< std::ostream, Stream >::value || 
                          boost::is_same< std::istream, Stream >::value ));

	IfTypeElse<
        boost::is_same< std::ostream, Stream >::value,
		calvin::ptr_writer< T, Stream >,
		calvin::ptr_reader< T, Stream >
	>::result::func( s, p.p, p.n );

	return s;
}

// string specialization
template <typename Stream>
struct writer<std::string, Stream> {

	static void func( Stream& s, std::string& str )
	{
		size_t size = str.length();
		s.write( (const char*) &size, sizeof( size_t )); 
        s.write( (const char*) str.c_str(), (std::streamsize) str.size() );
	}
};

template <typename Stream>
struct reader<std::string, Stream> {

	static void func( Stream& s, std::string& str )
	{
		size_t size;
		s.read( (char*) &size, sizeof( size_t ) );
		str.clear();
		str.resize( size );
		s.read( (char*) str.c_str(), (std::streamsize) size );
	}
};

template <typename Stream>
Stream& operator^( Stream& s, std::string& str )
{
    BOOST_STATIC_ASSERT(( boost::is_same< std::ostream, Stream >::value || 
                          boost::is_same< std::istream, Stream >::value ));

	IfTypeElse<
        boost::is_same< std::ostream, Stream >::value,
		calvin::writer< std::string, Stream >,
		calvin::reader< std::string, Stream> >
	::result::func( s, str );

	return s;
}

template <typename T, typename Stream>
struct sequence_writer {

	typedef typename T::iterator I;

	static Stream& sequence( Stream& s, T& c )
	{
		I b = c.begin();
		I e = c.end();

		size_t size = c.size();
		s ^ size;

		for( I i = b; i != e; ++i ) {
			s ^ *i;
		}

		return s;
	}
};

template <typename T, typename Stream>
struct sequence_reader {

	static Stream& sequence( Stream& s, T& c )
	{
		size_t size;
		s ^ size;

		for( size_t i = 0; i < size; ++i ) {
			T::value_type t;
			s ^ t;
			c.push_back( t );
		}

		return s;
	}
};

// a specialized version for lists
template <typename T, typename Stream>
Stream& operator^( Stream& s, std::list<T>& l )
{
    BOOST_STATIC_ASSERT(( boost::is_same< std::ostream, Stream >::value || 
                          boost::is_same< std::istream, Stream >::value ));

    typedef std::list<T> list_type;

	IfTypeElse<
        boost::is_same< std::istream, Stream >::value, 
		calvin::sequence_reader< list_type, Stream >,
		calvin::sequence_writer< list_type, Stream >
	>::result::sequence( s, l );

    return s;
}

// a specialized version for vectors
template <typename T, typename Stream>
Stream& operator^( Stream& s, std::vector<T>& v )
{
    BOOST_STATIC_ASSERT(( boost::is_same< std::ostream, Stream >::value || 
                          boost::is_same< std::istream, Stream >::value ));

    typedef std::vector<T> vector_type;

	IfTypeElse<
        boost::is_same< std::istream, Stream >::value, 
		calvin::sequence_reader< vector_type, Stream >,
		calvin::sequence_writer< vector_type, Stream >
	>::result::sequence( s, v );

    return s;
}

// a specialized version for deque
template <typename T, typename Stream>
Stream& operator^( Stream& s, std::deque<T>& v )
{
    BOOST_STATIC_ASSERT(( boost::is_same< std::ostream, Stream >::value || boost::is_same< std::istream, Stream >::value ));

    typedef std::vector<T> vector_type;

	IfTypeElse<
        //boost::is_base_and_derived< std::istream, Stream >::value,
        boost::is_same< std::istream, Stream >::value,
		calvin::sequence_reader< vector_type, Stream >,
		calvin::sequence_writer< vector_type, Stream >
	>::result::sequence( s, v );

    return s;
}


//-----------------------------------------------------------------------------
//							Inline functions
//-----------------------------------------------------------------------------

template <typename T, typename Key>
T* load( const Key& name )
{
    archive<T::is_persistent>* ar = archive<T::is_persistent>::default_archive_;
    archive<T::is_persistent>::current_archive_ = archive<T::is_persistent>::default_archive_;

    if( ar == NULL )
        CALVIN_ERROR( "No default archive set when saving." );

    T* t = ar->load<T>( name );

    return t;
}

template <typename T>
bool save( T* re )
{
    archive<T::is_persistent>* ar = archive<T::is_persistent>::default_archive_;
    archive<T::is_persistent>::current_archive_ = archive<T::is_persistent>::default_archive_;

    if( ar == NULL )
        CALVIN_ERROR( "No default archive set when saving." );

    ar->save( re );

    return true;
}

// This is the default and preferred interface for loading 
// objects.  If the object has been loaded, the same object (named the same)
// is returned rather than new objects.  I keep weak_ptrs to all
// the objects returned so the app owns the pointers, but I can track
// them to return correct requests.

template <typename Key>
template <typename T>
boost::shared_ptr<T> archive< Key >::load( const Key& name )
{
    boost::shared_ptr<T> t;

    try {

    current_archive_ = this;

    for( typename persistent<Key>::registry::iterator i = typename persistent<Key>::registry_.begin();
         i != typename persistent<Key>::registry_.end(); ++i ) {
    
        if( boost::get< boost::weak_ptr< persistent<Key> > >( *i ).lock() == NULL )  {
            boost::weak_ptr< persistent<Key> > wp( boost::get< boost::weak_ptr< persistent<Key> > >( *i ));
            boost::shared_ptr<T> p = allow_persistence<T>::shared_ptr_cast<T, Key>(wp.lock());
            if( p->name() == name )
                return p;
        }
        else if( boost::get< persistent<Key>* >(*i) == NULL ) {
            persistent<Key>* wp = boost::get< persistent<Key>* >(*i);
            if( wp->name() == name ) {
                boost::shared_ptr<T> p = allow_persistence<T>::shared_ptr_cast<T, Key>(wp);
                boost::weak_ptr< persistent<Key> > t( p );
                (*i) = t;
                return p;
            }
        }
        else {
            CALVIN_ERROR( "Unknown type in load_ptr" );
        }
    }

    // open the archive
    boost::shared_ptr<std::istream> in = resolve_input( name );
    // load and check the types and version to make sure we're not smoking crack
    std::string type;
    getline( *(in.get()), type, '\0' );

    unsigned int version;
    in->read( (char*) &version, sizeof(unsigned int) );

    // create a blank object
    t = allow_persistence<T>::instantiate<T, Key>( name, type );

    // read in the object
    allow_persistence<T>::serialize<T, std::istream>( (*in), t, version );

    //// save it in the registry
    //boost::weak_ptr<persistent<Key> > wt;
    //wt = t;
    //persistent<Key>::registry_.push_back( wt );

    }
    catch( std::exception& e ) {
        t.reset();
        std::ostringstream os;
        os << "Couldn't load object " << name;
        CALVIN_ERROR( e.what() ); 
    };

    return t;
}


// This version is used internally by Calvin.  It can also be used by a programmer,
// but something loaded by this isn't included in the registry.

template <typename Key>
template <typename T>
T* archive< Key >::load_ptr( const Key& name )
{
    T* t;
    try {
    current_archive_ = this;

    // create a blank object
    t = allow_persistence<T>::instantiate_ptr<T, Key>( name );

    // open the archive
    boost::shared_ptr<std::istream> in = resolve_input( name );
    // load and check the types and version to make sure we're not smoking crack
    std::string type;
    getline( *(in.get()), type, '\0' );
    ASSERT( type == typeid(T).name() );

    unsigned int version;
    in->read( (char*) &version, sizeof(unsigned int) );

    // read in the object
    allow_persistence<T>::serialize<T, std::istream>( (*in), t, version );

    }
    catch( std::exception e ) {
        delete t;
        std::ostringstream os;
        os << "Couldn't load object " << name;
        CALVIN_ERROR( os.str().c_str() ); 
    };

    return t;
}

// use the user supplied archive to create a stream and save the object
template <typename Key>
template <typename T>
bool archive< Key >::save( T* re )
{
    current_archive_ = this;

    // open the archive
    boost::shared_ptr<std::ostream> out = resolve_output( re->name() );
    std::string type = typeid(*re).name();
    out->write( type.c_str(), (std::streamsize) type.length() + 1 );
    unsigned int version = allow_persistence<T>::version<T>();
    out->write( (const char*) &version, sizeof( unsigned int ));

    // let the object fill it
    allow_persistence<T>::serialize<T, std::ostream>( (*out), re, version );

    return true;
}

template <typename Key>
template <typename T>
bool archive< Key >::save( boost::shared_ptr<T> re )
{
    current_archive_ = this;

    // open the archive
    boost::shared_ptr<std::ostream> out = resolve_output( re->name() );
    std::string type = typeid(*(re.get())).name();
    out->write( type.c_str(), (std::streamsize) type.length() + 1 );
    unsigned int version = allow_persistence<T>::version<T>();
    out->write( (const char*) &version, sizeof( unsigned int ));

    // let the object fill it
    //resource->Serialize( archive, version );
    allow_persistence<T>::serialize<T, std::ostream>( (*out), re, version );

    return true;
}

}   // namespace calvin


#endif		// RESOURCE_H

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Web Developer
United States United States
Jay Kint has been a software engineer/hacker for most of his life, starting with 8 bit Ataris. After a lengthy stint in the game industry, he now works at Microsoft in SQL Server.

Comments and Discussions