Figure 1   Employee

employee.h

 #include <iostream.h>
//------------------------------------------------
class Division; // declared below
class Peon;     // declared below

class Employee : public CObject
{
    DECLARE_SERIAL( Employee );
public:
    typedef int Id;
private:
    Id employee_id;
public:
    enum { id_none = 0 };    // number that's not a valid id

    virtual void Serialize( CArchive &ar     );
    virtual void Dump( CDumpContext &dc ) const;

    virtual 
    ~Employee(){}
     Employee( Id id = id_none ) : employee_id(id) {}
    //...
    int is_this_you ( Id id ) const;
    virtual void set_up_pointers ( const Division &division){ASSERT(0);}
};
//------------------------------------------------
inline int Employee::is_this_you(Id id) const
{
    return employee_id == id;
}
//-------------------------------------------------
class Manager : public Employee
{
    DECLARE_SERIAL( Manager );

    enum{ max_group_size = 64 };

    Peon            *group   [ max_group_size ];
    Employee::Id    group_ids[ max_group_size ];
    int             group_size;

    virtual void set_up_pointers( const Division &division );

    virtual void Serialize( CArchive &ar     );
    virtual void Dump( CDumpContext &dc ) const;
public:
    void i_work_for_you( Peon *peon, Employee::Id id )
    {
        group    [group_size  ] = peon;
        group_ids[group_size++] = id;    
    }

    virtual
    ~Manager( void ){}
     Manager( Employee::Id id = Employee::id_none )
                                    : Employee(id)
                                    , group_size(0)
    {}
};
//------------------------------------------------
class Peon : public Employee
{
    DECLARE_SERIAL( Peon );

    Manager *boss;
    virtual void Serialize( CArchive &ar     );
    virtual void Dump      ( CDumpContext &dc ) const;

public:
    void you_work_for_me( Manager *b ){ boss = b; }

    virtual
    ~Peon( void ){}
     Peon( Employee::Id id     = Employee::id_none,
           Manager *the_boss = NULL );
};
//------------------------------------------------
inline Peon::Peon( Employee::Id id, Manager *the_boss )
                        : Employee(id)
                        , boss    (the_boss)
{
    if( the_boss )
        the_boss->i_work_for_you( this, id );
}
//------------------------------------------------
class Division : public CObject
{
    DECLARE_SERIAL( Division );
    enum{ Max_workers = 1024 };

    Employee *workers[ Max_workers ];    // workers in the division
    int         size;                        // number of workers in division

    virtual void Serialize( CArchive &ar     );
    virtual void Dump      ( CDumpContext &dc ) const;

public:
    Employee *give_me_worker_with_this_id( Employee::Id id ) const;

    Division( void ) : size(0)
    {}

    ~Division( void )
    {
        while( --size >= 0 )
            delete workers[size];
    }

    void hire( Employee *emp );
};

employee.cpp

 #include "stdafx.h"
#include "Serial.h"

#include "SerialDoc.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//------------------------------------------------
IMPLEMENT_SERIAL( Employee,    CObject,    1 );
IMPLEMENT_SERIAL( Manager,    Employee,    1 );
IMPLEMENT_SERIAL( Peon,       Employee,    1 );
IMPLEMENT_SERIAL( Division,   CObject,     1 );

//------------------------------------------------
/*virtual*/ void Employee::Dump( CDumpContext &dc ) const
{
    dc << "id=" << employee_id ;
}
//------------------------------------------------
/*virtual*/ void Peon::Dump( CDumpContext &dc ) const
{
    Employee::Dump(dc);
    dc << "(Peon) works for: " ;
    boss->Employee::Dump( dc );    // just prints id
    dc << "\n";
}
//------------------------------------------------
/*virtual*/ void Manager::Dump( CDumpContext &dc ) const
{
    Employee::Dump(dc);
    dc << "(Manager) group is: " ;

    for( int i = 0; i < group_size; ++i )
    {
        group[i]->Employee::Dump( dc );
        dc << ", ";
    }
    dc << "\n";
}
//------------------------------------------------
/*virtual*/ void Division::Dump( CDumpContext &dc ) const
{
    dc << "division size = " << size << ", members:\n" ;
    for( int i = 0; i < size; ++i )
        workers[i]->Dump(dc);
        dc << "\n";
    dc << "\n";
}
//------------------------------------------------
/*virtual*/ void Employee::Serialize( CArchive &ar )
{
    if( ar.IsStoring() )
        ar << employee_id;
    else
        ar >> employee_id;
}
//------------------------------------------------
/*virtual*/ void Peon::Serialize( CArchive &ar )
{
    Employee::Serialize( ar );
    // There's nothing else to serialize at this
    // level, but if there was, you'd do it here.
}
//------------------------------------------------
/*virtual*/ void Manager::Serialize( CArchive &ar )
{
    Employee::Serialize( ar );

    int *cur = group_ids;
    if( ar.IsStoring() )
    {
        ar << group_size;
        for( int i = group_size; --i >= 0 ;)
            ar << *cur++;
    }
    else
    {
        ar >> group_size;
        for(int i = group_size; --i >= 0 ;)
            ar >> *cur++;
    }
}
//------------------------------------------------
/*virtual*/ void Division::Serialize( CArchive &ar )
{
    Employee **cur = workers;
    int i = 0;
    if( ar.IsStoring() )
    {
        ar << size;
        for(i = size; --i >= 0 ;)
            ar << *cur++;
    }
    else
    {
        // Must load the entire division before you
        // can set up the pointers in any of the workers.

        ar >> size;
        for(i = size; --i >= 0 ;)
            ar >> *cur++;

        cur = workers;
        for(i = size; --i >= 0 ;)
            if( dynamic_cast<Manager *>(*cur) )    // If it's a manager
                (*cur++)->set_up_pointers(*this);
    }
}
//------------------------------------------------
/*virtual*/ void Manager::set_up_pointers ( const Division &division )
{    
    // When called, the id array has been initialied by
    // the group array has not. For every id, ask the division
    // for the associated employee and put a pointer to it
    // in the group array.

    int    i = group_size;
    while( --i >= 0 )
    {
        Employee *emp = division.give_me_worker_with_this_id( group_ids[i] );
        Peon *peon = dynamic_cast<Peon *>(emp);
        ASSERT( peon );

        peon->you_work_for_me( this );
        group[i] = peon;
    }
}
//------------------------------------------------
Employee *Division::give_me_worker_with_this_id( Employee::Id id ) const
{
    Employee *const *cur = workers;
    for( int i = size; --i >= 0 ;)
    {
        if( (*cur)->is_this_you( id ) )
            return *cur;
        ++cur;
    }
    return NULL;
}
//------------------------------------------------
void Division::hire( Employee *emp )
{
    ASSERT( size <= Max_workers );
    workers[size++] = emp;
}

Figure 2   SerialDoc.cpp

 // SerialDoc.cpp : implementation of the CSerialDoc class
#include "stdafx.h"
#include "Serial.h"
#include "SerialDoc.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CSerialDoc
IMPLEMENT_DYNCREATE(CSerialDoc, CDocument)

BEGIN_MESSAGE_MAP(CSerialDoc, CDocument)
    //{{AFX_MSG_MAP(CSerialDoc)
    ON_COMMAND(ID_POPULATE, OnPopulate)
    ON_COMMAND(ID_DUMP, OnDump)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CSerialDoc construction/destruction

CSerialDoc::CSerialDoc() : division( new Division )
{}

CSerialDoc::~CSerialDoc()
{
    delete division;
}
//------------------------------------------------
void CSerialDoc::Serialize(CArchive& ar)
{
    if (ar.IsStoring())
    {
        ar << division;    // Must be a CObject pointer.
    }
    else
    {
        CObject *p;
        ar >> p;
        
        division = dynamic_cast<Division *>(p);
        ASSERT( division );
    }
}
//------------------------------------------------
BOOL CSerialDoc::OnNewDocument()
{
    if (!CDocument::OnNewDocument())
        return FALSE;

    if( division )
    {
        delete division;
        division = new Division;
    }

    return TRUE;
}
//------------------------------------------------
void CSerialDoc::OnPopulate() 
{
    // TODO: Add your command handler code here
    
    Manager *mgr = new Manager( 1 );
    division->hire( mgr                 );
    division->hire( new Peon( 2, mgr )    );
    division->hire( new Peon( 3, mgr )    );
    division->hire( new Peon( 4, mgr )    );

    CDocument::SetModifiedFlag( TRUE );
}
//------------------------------------------------
void CSerialDoc::OnDump() 
{
    afxDump << division;
}

Figure 7   Creating Classes Dynamically

 #include <iostream.h>
#include "dynamic.h" // Classes for persistence and dynamic creation
//------------------------------------------------
class Division; // declared below
class Peon;     // declared below

class Employee
{
public:
    typedef int Id;
private:
    Id employee_id;
public:
    enum { id_none = 0 };    // number that's not a valid id

    virtual 
    ~Employee(){}
     Employee( Id id = id_none ) : employee_id(id) {}

    int is_this_you    ( Id    id ) const;
    virtual void set_up_pointers    ( const Division div){ASSERT(0);}
};
//------------------------------------------------
class Manager : public Employee
{
    enum{ max_group_size = 64 };

    Peon           *group     [ max_group_size ];
    Employee::Id   group_ids[ max_group_size ];
    int            group_size;

    virtual void set_up_pointers( const Division &division );
public:
    void i_work_for_you( Peon *peon, Employee::Id id );
    virtual
    ~Manager( void ){}
     Manager( Employee::Id id = Employee::id_none );
};
class Peon : public Employee
{
    Manager *boss;

public:
    void you_work_for_me( Manager *b ){ boss = b; }

    virtual
    ~Peon( void ){}
     Peon( Employee::Id    id     = Employee::id_none,
         Manager        *the_boss = NULL );
};
//------------------------------------------------
dynamic::factory< Peon    > peon_factory;
dynamic::factory< Manager > mgr_factory;

void create_a_few_objects(void)
{
    Employee *ep = (Employee *)dynamic::create( "class Peon" );
    if( dynamic_cast<Peon*>( ep ) )
        cout << "It's a Peon!";
    delete ep; // am done with the object, delete it.

    ep = (Employee *)dynamic::create( "class Manager" );
    if( dynamic_cast<Manager*>( ep ) )
        cout << "It's a Manager!";
    delete ep;
}

Figure 8   Dynamic

dynamic.h

 #ifndef DYNAMIC_H__
#define DYNAMIC_H__

#include <stdio.h>
#include <string.h>
#include <typeinfo.h>
// Support for polymorphic persistence. There's an example in the 
// implemenation file (/src/tools.cpp/persist.cpp).
//
// Under Microsoft compiler, must be compiled with /Gr switch:
// Under Settings, "C/C++" tab, "C++ Language" catagory
//                    check "Enable run-time type information"
//
// Dynamically-creatable objects must have a default constructor
// and at least one virtual function, usually the destructor.
//
namespace dynamic
{
    void *create( const char *cls_name );
    //------------------------------------------------------------------
    class abstract_factory
    {
    public:
        virtual void *create_obj( const char *class_name ) = 0;
    };
    //------------------------------------------------------------------
    class factory_list_ele
    {
        friend void *create( const char *cls_name );
    
        static factory_list_ele *head;
        factory_list_ele        *next;
        abstract_factory        *manufacturer;
    public:
        factory_list_ele( abstract_factory *fact );
        virtual ~factory_list_ele( void );

    };
    //------------------------------------------------------------------
    inline factory_list_ele::factory_list_ele(  abstract_factory *fact )
                            : manufacturer( fact )
    {
        next = head;
        head = this;
    }
    //------------------------------------------------------------------
    // disable Warning C4355: "'this' used in base member initializer
    // list." The warning is generated by the initialzation list of the
    // factory<t> constructor. The warning is noise in the current context,
    // because "manufacturer" is not actually a base class. The pragma
    // must be at the global level because the warning is kicked out
    // every time the template is expanded.

    #pragma    warning(disable : 4355)
    //------------------------------------------------------------------
    // The "manufacturer" field isn't static because I want to initialize
    // it in the constructor. This way I can avoid having to define one
    // for every template expansion. This means that you might have more
    // than one manufacturer for a given class (if you declare several
    // factories for the same class, for example), but it's a harmless
    // error.

    template <class t>
    class factory: public abstract_factory
    {
        static t an_object;
        factory_list_ele manufacturer;
                                                      
    public:
        factory( void ): manufacturer( this ) {}

        void *create_obj( const char *class_name )
        {
            return !strcmp( typeid(an_object).name(), class_name)
                    ? (void*)( new t )
                    : 0
                    ;
        }
    };
}
#endif // DYNAMIC_H__

dynamic.cpp

 #include "dynamic.h"

namespace dynamic
{
    /*static*/ factory_list_ele *factory_list_ele::head = 0;
    //------------------------------------------------------------------
    void *create( const char *cls_name )
    {
        // Create a new object dynamically. Search the list of dynamic
        // objects and call the create_obj() until one of them succeedes
        // in making the required object. If you get to the end of the
        // list, you can't make the requested object.

        void                    *new_object = 0;
        const factory_list_ele  *cur = factory_list_ele::head;

        for( ; cur ; cur = cur->next )
            if( new_object = cur->manufacturer->create_obj(cls_name) )
                break;

        return new_object;
    }
    //------------------------------------------------------------------
    /*virtual*/ factory_list_ele::~factory_list_ele( void )
    {
        // When the factory<t> is destroyed, its "manufacturer"
        // field is also destroyed, so this destructor is called.
        // Remove the current factory from the factory_list. The
        // list is not doubly linked, so we have to track over from
        // the left to find the node to delete. I've reduced code
        // size a bit by using double indirection. The nextp pointer
        // always holds the address of the pointer to the current node.
        // This will be either the address of the head-of-list pointer
        // or the address of the "next" field of the previous node,
        // depending on whether we're at the head of list or not. In
        // any event, the first item in the list is not a special case
        // and we won't have to keep pointers to both the current and
        // previous nodes to do the delete.

        factory_list_ele    **nextp = &factory_list_ele::head;

        for( ; *nextp ; nextp = &(*nextp)->next )
            if( *nextp == this )
            {
                *nextp = (*nextp)->next;
                break;
            }
    }
}

To the rescue come namespaces, a new feature in Visual C++ 4.0. A namespace is defined like this:

 namespace dynamic
{
    class my_class
    {
        // . . .
    };

    class fred : public my_class
    {
        // . . .
    }
}

class fred
{
    // . . .
};

dynamic::fred  x;
fred y;

Figure 9   Inserting a Factory into the List

 class abstract_factory
{
public:
    virtual void *create_obj( 
                          const char *class_name ) = 0;
};

     class factory_list_ele
    {
        friend void *create( const char *cls_name );
    
        static factory_list_ele *head;
        factory_list_ele        *next;
        abstract_factory        *manufacturer;
    public:
        factory_list_ele( abstract_factory *fact );
        virtual ~factory_list_ele( void );
    };

Figure 10   persist.h

 #ifndef PERSIST_H__
#define PERSIST_H__

#include "dynamic.h"

//----------------------------------------------------------------------
// Support for persistance. For any given persistant type, a factory<t>
// must exist before the object can be loaded from the disk. The
// factory<t> can be a local variable in the function that does the
// loading, however.

class persistent_store
{
public:
    virtual ~persistent_store(){}

    virtual size_t write( const void *buffer, size_t size, size_t count)=0;
    virtual size_t read    ( void    *buffer, size_t size, size_t count)=0;
};
//----------------------------------------------------------------------
class persistent_file: public persistent_store
{
    FILE *fp;
public:
    persistent_file( FILE *stream = stdout ) : fp(stream) {}

    virtual size_t write( const void *buffer, size_t size, size_t count);
    virtual size_t read ( void       *buffer, size_t size, size_t count);
};
//----------------------------------------------------------------------
class persistent
{
private:
    enum direction { loading = 0, flushing = 1 };
    enum           { name_size = 126    // max size of a class name
                   };
    typedef short version_t;

public:
    enum error
    {
        success = 0,
        bad_version,
        cannot_create_dynamically,
        file_error,
        file_corrupt,
        internal_error,
        serialize_error
    };

    static persistent    *load( persistent_store &stream, error *err    );
    error                flush( persistent_store &stream ) const;

    virtual error serialize( persistent_store &stream,
                             direction am_flushing );
    virtual version_t version  ( void ) const;
};

#endif // PERSIST_H__

Figure 11   Encapsulating an ANSI-C FILE Pointer

 class persistent_file: public persistent_store
{
    FILE *fp;
public:
    persistent_file( FILE *stream = stdout ) : fp(stream) {}

    virtual size_t write( const void *buffer, size_t size,
                          size_t count);
    virtual size_t read ( void *buffer, size_t size, size_t count);
};
//------------------------------------------------------------------
/*virtual*/ size_t persistent_file::write( const void *buffer, 
                                           size_t size, size_t count)
{
    return fwrite( buffer, size, count, fp );
}
//------------------------------------------------------------------
/*virtual*/ size_t persistent_file::read( void *buffer,
                                          size_t size, size_t count)
{
    return fread( buffer, size, count, fp );
}

Figure 12   persistence.cpp

 #include "persist.h"
#include <stdlib.h>
//----------------------------------------------------------------------
/*virtual*/ size_t persistent_file::write( const void *buffer,
                                           size_t size, size_t count)
{
    return fwrite( buffer, size, count, fp );
}
//----------------------------------------------------------------------
/*virtual*/ size_t persistent_file::read( void *buffer,
                                          size_t size, size_t count)
{
    return fread( buffer, size, count, fp );
}
//======================================================================
/*virtual*/ persistent::error persistent::serialize( persistent_store &stream,
                                     persistent::direction am_flushing_to_disk )
{
    // Do nothing, it's here only to allow the user to chain
    // to it in a rote sort of way.
    return success;
}
//----------------------------------------------------------------------
/*virtual*/ persistent::version_t  persistent::version( void ) const
{
    return 0;
}
//----------------------------------------------------------------------
persistent::error persistent::flush( persistent_store &stream ) const
{
    char  buf[ name_size ];
    version_t ver = version();

    memset( buf, 0, name_size );
    strncpy( buf, typeid(*this).name(), sizeof(buf)        );

    if(    stream.write( buf, name_size, 1 ) != 1    )
        return file_error;

    if( stream.write( &ver, sizeof(ver), 1 ) != 1    )
        return file_error;

    // the  ((persistent *)this)-> is necessary because the
    // serialize function cannot be const because we want load()
    // to call it. Nonetheless, we want the flush() function to be const.
    // because it doesn't modify the flushed object.

    if( ((persistent *)this)->serialize( stream, flushing ) != success)
        return serialize_error;
    
    return success;
}
//----------------------------------------------------------------------
/*static*/ persistent *persistent::load( persistent_store &stream,
                                         error *err )
{
    // Loads a persistent object from the disk without having to know the
    // objects actual class. Returns false (which has the value
    // persistant::success) on success and loads *obj_ptr with a
    // pointer to the newly-created object (which can be destroyed
    // by passing it to delete). *obj_ptr is not modified if an
    // error is returned.

    char cls_name[name_size], *p = cls_name;
    persistent *the_object = NULL;
    version_t version_number;

    *err = internal_error;

    if( stream.read( cls_name, name_size, 1 ) != 1 )
        *err == file_error;
    
    else if( *cls_name == '\0' )
        *err = file_corrupt;
    
    else if( !(the_object = (persistent *) dynamic::create( cls_name )) )
        *err = cannot_create_dynamically;

    else if( stream.read(&version_number, sizeof(version_number), 1) != 1 )
        *err = file_error;

    else if(version_number != the_object->version())
        *err = bad_version;

    else if( the_object->serialize( stream, loading ) != success )
        *err = serialize_error;
    else
        *err = success;


    if( *err != success && the_object )
    {
        delete the_object;
        the_object = NULL;
    }
    return the_object;
}

Figure 13   mfc_pers

mfc_pers.h

 #ifndef MFC_PERSIST_H__
#define MFC_PERSIST_H__

#include "persist.h"
#include <afxwin.h>         // MFC core and standard components

class persistent_CFile: public persistent_store
{
    CFile *fp;
public:
    persistent_CFile( CFile *stream ) : fp(stream)          {}
    persistent_CFile( CArchive &ar  ) : fp(ar.GetFile())    {}

    virtual size_t write( const void *buffer, size_t size, size_t count);
    virtual size_t read ( void       *buffer, size_t size, size_t count);
};

#endif // MFC_PERSIST_H__

mfc_pers.cpp

 #include "mfc_pers.h"

/*virtual*/ size_t persistent_CFile::write( const void *buffer,
                                            size_t size, size_t count)
{
    // Throws a CFileException on a write error, which is unrecoverable
    // becuase there's no way to determine how many bytes were written
    // before the problem occured.

    fp->Write( buffer, size * count );
    return size * count;
}
//----------------------------------------------------------------------
/*virtual*/ size_t persistent_CFile::read( void *buffer,
                                           size_t size, size_t count)
{
    return fp->Read( buffer, size * count );
}

Figure 14   persistent_base

>

 // First, persistent objects must derive from class
// persistent and they must provide overrides of version()
// and serialize() functions:

class persistent_base : public persistent
{
    char buf[ some_size ];
    // . . .
    virtual version_t version( void ) const
    {
        return 1;
    }
protected:
    virtual persistent::error serialize(
    persistent_store &stream, direction am_flushing )
    {
        if( am_flushing )
            stream.write( buf, sizeof(buf), 1 );
        else
            stream.read( buf, sizeof(buf), 1 );
        return persistent::success;
    }
public:
// . . .
};

// Next, you need to provide an object factory in the cpp file
// in order to load objects from the disk:

dynamic::factory<persistent_base>     factory;

// Having done all that, here's how to transfer objects to or
// from the persistent store:

void_demo_persistence( void )
{
    persistent_base    obj;

    FILE *fp = fopen( "test", "w" );
    obj.flush( persistent_file(fp) );
    fclose( fp );

    fp = fopen( "test", "r" );

    persistent::error err;
    persistent *p = persistent::load( persistent_file(fp), &err);
    
    persistent_base *bp = dynamic_cast<persistent_base*>(p);
   // if bp isn't NULL, the it's indeed an object of the correct
   // type. When you're done with the object, pass it to delete:

    delete dp;
}

Figure 15   test.cpp

 //======================================================================
// Test and demo routine
//======================================================================
#include <stdio.h>
#include <share.h>
#include <stdlib.h>
#include "persist.h"
//----------------------------------------------------------------------
// The report macro works much like an ASSERT, except that int prints
// an "okay" message if the condition is true and prints an ERROR message
// (and goes to the debugger---that's the _asm int 3) if something
// is wrong. This makes it easy to test the code: A string of okays means
// that everything's fine, but an error gets you into the debugger at the
// point of error. Note that since this is a macro, the debugger will
// be positioned at the REPORT invocation, not up here in the macro
// definition. When you're not debugging, all REPORT statments just
// disappear (or to be more accurate, they expand to empty strings).

#ifndef NDEBUG
    #define REPORT( condition, str )\
        if( condition )\
            printf("okay: " #condition "\n" );\
        else\
        {\
            printf("***ERROR*** %s\n", str);\
            _asm int 3\
        }
#else
    #define REPORT(c,s) // empty
#endif
//----------------------------------------------------------------------
// Define a base class and a few derived classes to test things.

class base
{
public:
    virtual ~base(){}
    // base(){ printf("creating %s\n", typeid(*this).name()); }
};

class derived : public base
{
public:
    virtual ~derived(){} 
    // derived(){ printf("creating %s\n", typeid(*this).name()); }
};

template <class t>
class templ
{
public:
    virtual ~templ(){}
    // templ(){ printf("creating %s\n", typeid(*this).name()); }
};
//----------------------------------------------------------------------
// Template factories must either be declared at the global level or
// allocated from new.

dynamic::factory< templ<int> > f;

void test_dynamic_creation( void )
{
    dynamic::factory< derived >    f2;
    {
        dynamic::factory< base >    f1;

        base *bp = (base *)dynamic::create( "class base" );
        
        REPORT( bp,                         "Couldn't create base");
        REPORT( dynamic_cast<base*>(bp),    "base not RTTI identifiable");
        REPORT( !dynamic_cast<derived*>(bp),"base incorrectly identifed "\
                                            "as derived");
    }

    base *bp = (base *)dynamic::create( "class base" ); // should fail
    REPORT( !bp, "Was able to create an object when no factory exists" );

    bp = (derived *) dynamic::create("class derived");
    REPORT( bp, "Couldn't create derived");
    REPORT( dynamic_cast<derived*>(bp), "derived not RTTI identifiable");
    REPORT( dynamic_cast<base*>(bp), "derived not RTTI identifiable "\
                                     "as base");

    templ<int> *tp = (templ<int> *) dynamic::create("class templ<int>");
    REPORT( tp, "Couldn't create templ<int>");
    REPORT( dynamic_cast< templ<int>* >(tp),"templ<int> not RTTI "\
                                            "identifiable");
}
//======================================================================
class persistent_base : public persistent
{
    char buf[32];

    virtual version_t version( void ) const
    {
        return 1;
    }
    
protected:
    virtual persistent::error serialize( persistent_store &stream,
                                         direction am_flushing )
    {
        if( am_flushing )
            stream.write( buf, sizeof(buf), 1 );
        else
            stream.read( buf, sizeof(buf), 1 );
        
        return persistent::success;
    }
public:
    persistent_base()
    {
        memset(buf, '-', sizeof(buf) );
        strcpy(buf, "Base-class obj/" );
    }
    virtual int check_ok( void )
    {
        return strcmp(buf,"Base-class obj/")==0;
    }
};
//----------------------------------------------------------------------
class persistent_derived: public persistent_base
{
    char buf[32];

    virtual persistent::error serialize(persistent_store &stream,
                                        direction am_flushing)
    {
        persistent::error err = persistent::success;

        if( !(err = persistent_base::serialize(stream, am_flushing)) )
        {
            if( am_flushing )
                stream.write( buf, sizeof(buf), 1 );
            else
                stream.read( buf, sizeof(buf), 1 );
        }
        return err;
    }

    virtual version_t version( void ) const
    {
        return 2;
    }
public:
    persistent_derived()
    {
        memset(buf, '=', sizeof(buf) );
        strcpy(buf, "Derived-class obj/" );
    }
    virtual int check_ok( void )
    {
        return strcmp(buf,"Derived-class obj/")==0
                                    ? persistent_base::check_ok()
                                    : 0;
    }
};
//----------------------------------------------------------------------
template <class t>
class persistent_templ: public persistent
{
    t a_field;
public:
    persistent_templ(void): a_field(0xabcd) {}
    int check_ok( void ){ return a_field == 0xabcd; }

    virtual ~persistent_templ(){}
    virtual persistent::error serialize(persistent_store &stream,
                                        direction am_flushing)
    {
        if( am_flushing )
            stream.write( &a_field, sizeof(t), 1 );
        else
            stream.read( &a_field, sizeof(t), 1 );

        return persistent::success;
    }
};
//----------------------------------------------------------------------
dynamic::factory<persistent_derived>     d_factory;
dynamic::factory<persistent_templ<int> > t_factory;
//----------------------------------------------------------------------
void test_persistence( void )
{
    persistent_derived     derived_obj;
    persistent_templ<int>  template_obj;

    FILE *fp = _fsopen( "test", "w", _SH_DENYNO );
    REPORT( fp, "Couldn't open test file\n" );
    
    REPORT(!derived_obj.flush( persistent_file(fp) ), "flush derived" );
    REPORT(!template_obj.flush( persistent_file(fp) ), "flush template" );
    fclose( fp );

    fp = _fsopen( "test", "r", _SH_DENYNO );
    REPORT( fp, "Couldn't reopen test file\n" );

    persistent::error err;
    persistent *p = persistent::load( persistent_file(fp), &err);
    REPORT( p, "Persistent load failed" );
    
    persistent_derived *dp = dynamic_cast<persistent_derived*>(p);
    REPORT( dp, "Couldn't do dynamic cast to derived type\n");
    REPORT( dp->check_ok(), "Dynamically-loaded object corrupt\n");
    delete dp;

    p = persistent::load( persistent_file(fp), &err);
    REPORT( p, "Persistent load failed" );
    
    persistent_templ<int> *tp = dynamic_cast<persistent_templ<int>*>(p);
    REPORT( tp, "Couldn't do dynamic cast to template type\n");
    REPORT( tp->check_ok(), "Dynamically-loaded object corrupt\n");
    delete tp;
}
//----------------------------------------------------------------------
void main( void )
{
    test_dynamic_creation();
    test_persistence();

    printf("Press ENTER to terminate\n");
    getchar();

    exit( 0 );
}