Click here to Skip to main content
15,896,063 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 79.9K   563   42  
Tutorial for using the Calvin C++ persistence library.
/*-----------------------------------------------------------------------------
File  :	test_resource.CPP
Title : Test the resource loading and saving module
Owner :	Jay Kint
Tabs  :	column 5 every 4

	Copyright (C) 2004, Jay Kint, All Rights Reserved
-------------------------------------------------------------------------------
Description:
Test harness for the Calvin persistent library

*/



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

// module includes
#include "calvin.h"
#include "fs_archive.h"
#include "cmdline.h"

// system includes
#include <crtdbg.h>
#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <cstdio>
#include <windows.h>


//-----------------------------------------------------------------------------
//							Namespaces
//-----------------------------------------------------------------------------

using namespace calvin;
using namespace std;
using namespace boost;


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

#define INT_KEY
#if defined(INT_KEY)
#define KEY int
#define A1_NAME 0xa1
#define A2_NAME 0xa2
#define A3_NAME 0xa3
#define A4_NAME 0xa4
#define A5_NAME 0xa5
#define A6_NAME 0xa6
#define B_NAME 0x0b
#define C_NAME 0x0c
#define Q_NAME 0xff
#else
#define KEY std::string
#define A1_NAME "a1"
#define A2_NAME "a2"
#define A3_NAME "a3"
#define A4_NAME "a4"
#define A5_NAME "a5"
#define A6_NAME "a6"
#define B_NAME "b"
#define C_NAME "c"
#define Q_NAME "q"
#endif

struct made_of_prims {
    int i;
    float f;
    double d;
};

class persistent_void_test : public calvin::persistent<void> {
    std::string* msg;
friend struct calvin::allow_persistence<void>;
public:
    persistent_void_test( void ) : msg( new std::string( "I'm a calvin::persistent<void>" )) {}
    ~persistent_void_test( void )
    {
        delete msg;
        msg = NULL;
    }

    template<typename Stream>
    Stream& serialize( Stream& s, unsigned int version )
    {
        return s ^ msg;
    }

    void print( void )
    {
        cout << *msg << endl;
    }
};

class A : public calvin::persistent<KEY> {

    int a;
    float a2;
    double a3;
 
    struct Aa {
        int a4;
        int a5;

        Aa(void) : a4(4), a5(5) {}
    };

    Aa a6;
public:
    typedef char stupid;
    void print( void ) { std::cout << a << ' ' << a2 << ' ' << a3 << ' ' << a6.a4 << ' ' << a6.a5 << std::endl; }
    A( void ) : a(1), a2(2.0f), a3(3.0), a6(Aa()) {}
    A( const KEY& name ) : persistent<KEY>(name), a(1), a2(2.0f), a3(3.0), a6(Aa()) {
        return;
    }
	virtual ~A( void ) {
        // memset( this, 0, sizeof(A) );
        return;
    }

protected:
	template <typename Stream>
	Stream& serialize( Stream& s, const unsigned int version ) { return s ^ a ^ a2 ^ a3 ^ a6.a4 ^ a6.a5; }

private:
    friend calvin::allow_persistence<A>;
};


class B : public A {

public:
    B( void ) : A(), b1(0), b2(1), stupid(NULL) {}
    B( const KEY& name ) :  A(name), b1(0), b2(1), stupid(NULL) {}
    B( const KEY& name, const char* stupid ) :  A(name), b1(0), b2(1), stupid(stupid) {
        return;
    }
	virtual ~B( void ) { /*memset( this, 0, sizeof(B) );*/ }
    void print( void ) { A::print(); sm.print(); std::cout << b1 << ' ' << b2 << std::endl; }
    void set_b1( int b1 ) { this->b1 = b1; }
    void set_b2( unsigned int b2 ) { this->b2 = b2; }
    void add( made_of_prims& p)
    {
        vec_of_prims.push_back( p );
    }
private:
	int b1;
	unsigned int b2;
    std::vector<made_of_prims> vec_of_prims;
    persistent_void_test sm;
    const stupid* stupid;
    template <typename Stream>
	Stream& serialize( Stream& s, const unsigned int version ) 
    {
        return A::serialize( s, version ) ^ 
               b1 ^ 
               b2 ^
               vec_of_prims ^
               sm ^
               PtrArray<const char>( stupid, (stupid == NULL) ? 0 : (unsigned int) strlen(stupid)+1 /*null terminator*/);
    }
    friend calvin::allow_persistence<B>;
};

class C : public calvin::persistent<KEY> {
    struct deleter {
        boost::shared_ptr<A> operator()( boost::shared_ptr<A> a )
        {
            a.reset();
            return a;
        }
    };

public:
    C( void ) : persistent<KEY>() { b.reset(); ppint = new int*; *ppint = new int; }
    C( const KEY& name ) : persistent<KEY>( name ) /*, b(NULL)*/ { b.reset(); ppint = new int*; *ppint = new int; }
    C( const KEY& name, B* b, char* strz ) : persistent<KEY>(name), b(b)
    {
        strcpy( reinterpret_cast<char*>(this->strz), strz );
        mpc = 10;
        mp = new made_of_prims[10];
        ppint = new int*;
        *ppint = new int;
        **ppint = 0xbaadface;
    }
	virtual ~C( void ) 
    {
        //delete b;
        b.reset();
        transform( list_of_a.begin(), list_of_a.end(), list_of_a.begin(), deleter() ); 
        list_of_a.clear();
        delete *ppint;
        delete ppint;
        ppint = NULL;
        //memset( this, 0, sizeof(C) );
    }
    void print(void) { b->print(); printf( "%08x\n", **ppint );}
    void add( boost::shared_ptr<A> a ) { list_of_a.push_back( a ); }

private:
    boost::shared_ptr<B> b;
    std::list<boost::shared_ptr<A> > list_of_a;
    char strz[10];
    unsigned int mpc;
    made_of_prims* mp;
    int ** ppint;
	template <typename Stream>
    Stream& serialize( Stream& s, const unsigned int version ) { return s ^ b ^ list_of_a ^ strz ^ ppint ^ mpc ^ PtrArray<made_of_prims>( mp, mpc ); }

    friend calvin::allow_persistence<C>;        
};

template <typename T>
struct test_self {

    int a;
    float b;
    long c;

    test_self( void ) { a = 1; b = 2.0f; c = 3; }

    static const test_self& get( void );
    static std::string self_name_;
};

template <typename T>
const test_self<T>& test_self<T>::get( void )
{
    static test_self<T> self_;

    return self_;
}

template <typename T>
std::string test_self<T>::self_name_ = "This is me!";


//-----------------------------------------------------------------------------
//							Internal Constants
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//							Internal Function Prototypes
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//							Internal Globals
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//							Exported Globals
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//							Functions - Internal and External
//							All kept alphabetically ordered.
//-----------------------------------------------------------------------------

int main(void)
{
    const test_self<int>& self2 = test_self<int>::get();

    file_archive<KEY> ar( "." );
    file_archive<KEY>::set_default_archive( &ar );
    command_line_parser cmd_parser;
    std::string cmdline( GetCommandLine() );

    if( !cmd_parser.parse( cmdline )) {
        cerr << "Command line didn't parse correctly.";
        exit(-1);
    }

    // test loading instead of saving if the "load=true" is on the command line.  We do this so that
    // there is no way that the save/load can succeed based on residual pointers or anything
    // still being around.
    if( cmd_parser.get_value( "load" ) == "true" || cmd_parser.get_value( "load" ) == "1" ) {

        shared_ptr<C> c;
        c = ar.load<C>( C_NAME );
        c->print();
        return 0;
    }


    made_of_prims p1, p2, p3;

    p1.i = 0;
    p1.f = 1.0f;
    p1.d = 2.0;

    p2.i = 100;
    p2.f = 101.0f;
    p2.d = 102.0;

    p3.i = 200;
    p3.f = 201.0f;
    p3.d = 202.0;

	B *b = new B( B_NAME, "more stupid" );
	shared_ptr<C> c( new C( C_NAME, b, "stupid" ));

    b->set_b1( 100 );
    b->set_b2( 200 );
    b->add( p1 );
    b->add( p2 );
    b->add( p3 );

    c->add( boost::shared_ptr<A>( new A(A1_NAME)));
    c->add( boost::shared_ptr<A>( new A(A2_NAME)));
    c->add( boost::shared_ptr<A>( new A(A3_NAME)));
    c->add( boost::shared_ptr<A>( new A(A4_NAME)));
    c->add( boost::shared_ptr<A>( new A(A5_NAME)));
    c->add( boost::shared_ptr<A>( new A(A6_NAME)));

	ar.save( c );

#if defined(NO_CALVIN_EXCEPTIONS)
    shared_ptr<C> c_too( new C( C_NAME, NULL, "not as stupid" ));
#else
    try {
        shared_ptr<C> c_too( new C( C_NAME, b, "not as stupid" ));
    }
    catch( calvin_exception& e ) {
        cerr << e.what() << endl;
        cout << "Duplicate name test passed" << endl;
    }
#endif
    c.reset();
    
    // test for errors
#if defined(NO_CALVIN_EXCEPTIONS)
    c = ar.load<C>( Q_NAME );  // no object named q should exist
#else
    try {
        // no object named q should exist
        c = ar.load<C>( Q_NAME );
    }
    catch( calvin_exception& c ) {
        cerr << c.what() << endl;
        cout << "Non existent object test passed" << endl;
    }
#endif
}

namespace calvin {

void throw_exception( const calvin_exception& e )
{
    cerr << e.what() << endl;
    exit(-1);
}

}

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