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