fld2.cpp
// MField - example field and Record classes, version 2
//
//
#include <fstream.h>
#include <assert.h>
#include <string.h> // for memset and strlen
#include <stdio.h> // for sprintf
#include <stdlib.h> // for atoi
#include <ctype.h> // for isdigit
// Our base class - MField
class MField
{
public:
// Constructor and destructor
MField( char *NewName=0, int NewSize=0, int NewOffset=0 );
virtual ~MField() { delete [] Data; delete [] Name; }
MField( const MField& Src );
MField& operator=(const MField&Src ) { assert( strcmp(GetType(),Src.GetType() )==0 ); memmove( Data, Src.Data, Size ); return(*this);}
// Data getting and setting
virtual char* GetAsString() const =0; // Pure virtual
virtual void SetFromString(char* Src) =0; // Pure virtual
// Basic interface routines
char *GetName() { return(Name?Name:""); }
int GetSize() { return( Size ); }
void Clear() { memset( Data, 0, Size ); }
// A routine to help identify data type
virtual char * GetType() const = 0; // Pure virtual function, MField is 'abstract'
protected:
// Can be called only by this and derived classes
void SetData( int NewSize );
void SetName( char* NewName );
void* GetDataPntr() const { return( Data ); }
private:
int Offset, Size;
char * Data; // char * instead of void pointer, for simplicity
char * Name;
};
// Constructor
MField::MField( char *NewName, int NewSize, int NewOffset )
{
Name = 0;
SetName(NewName);
Data=0;
SetData(NewSize);
Offset = NewOffset;
Clear();
}
// Copy constructor
MField::MField( const MField& Src )
{
Name = 0;
SetName(Src.Name);
Data=0;
SetData(Src.Size);
Offset = Src.Offset;
Clear();
}
void MField::SetData( int NewSize ) // Sets buffer size
{
delete [] Data;
Data = new char[ NewSize ];
assert( Data != 0 );
Size = NewSize;
}
void MField::SetName( char* NewName ) // Sets field name
{
delete [] Name;
Name = new char[ strlen( NewName?NewName:"" ) + 1 ];
assert( Name!= 0 );
strcpy( Name, NewName?NewName:"" );
}
////////////////////////////////////////////////////////////////////////
class MDateField : public MField
{
typedef struct { short Y; char M, D; } MyDate;
public:
MDateField( char* NewName=0, int Offset=0 ) : MField(NewName, sizeof(MyDate), Offset ) {}
// Provide the functions that are pure virtual in our class:
virtual char* GetAsString() const;
virtual void SetFromString(char* Src);
virtual char * GetType() const { return( "Date" ); }
};
// Returns a char pointer for the contents of the field
char* MDateField::GetAsString() const
{
static char Perm[11];
MyDate * Tmp = (MyDate*)GetDataPntr();
sprintf( Perm, "%02d/%02d/%04d", Tmp->M, Tmp->D, Tmp->Y );
return( Perm );
}
void MDateField::SetFromString(char* Src)
{
MyDate * Tmp = (MyDate*)GetDataPntr();
Tmp->M = Tmp->D = 0;
Tmp->Y = 0;
Tmp->M = atoi(Src);
while( isdigit(*Src) )
Src++;
while( *Src && !isdigit( *Src ) )
Src++;
Tmp->D = atoi(Src);
while( isdigit(*Src) )
Src++;
while( *Src && !isdigit( *Src ) )
Src++;
Tmp->Y = atoi(Src);
}
///////////////////////////////////////////////////////////////////////
class MShortField : public MField
{
public:
MShortField( char* NewName=0, int NewOffset=0 ) : MField(NewName, sizeof(short), NewOffset ) {}
char* GetAsString() const;
void SetFromString(char* Src);
virtual char * GetType() const { return( "Short" ); }
};
char* MShortField::GetAsString() const
{
static char Perm[7];
sprintf( Perm, "%d", *(short*) GetDataPntr() );
return( Perm );
}
void MShortField::SetFromString(char* Src)
{
*(short*) GetDataPntr() = atoi(Src);
}
///////////////////////////////////////////////////////////////////////
class MRecord
{
public:
MRecord() { FieldCount=0; }
~MRecord() { while( FieldCount-- ) delete Fields[FieldCount]; }
// Stores a dynamically-allocate MField-type class
void AddField( MField* ToAdd )
{
assert( FieldCount<(sizeof(Fields)/sizeof(Fields[0])) );
Fields[ FieldCount++ ] = ToAdd;
}
// FindField - Finds a field index based on name
int FindField( char* Name ) { for( int i=0; i < FieldCount; i++ )
if( strcmp( Fields[i]->GetName(), Name )==0 )
return( i );
return(-1);
}
// Removes an MField class added via AddField
void RemoveField( char * Name ) { RemoveField( FindField(Name) ); }
void RemoveField( int Index ) { assert( Index>=0 && Index<FieldCount);
delete Fields[Index];
for( ; Index < FieldCount-1; Index++ )
Fields[Index] = Fields[Index+1];
FieldCount--;
}
//Gives direct access to a field vie index or name
MField* Field( int Index ) { assert( Index>=0&&Index<FieldCount ); return(Fields[Index]);}
MField* Field( char* Name ) { int i = FindField( Name );return( i==-1? 0 : Fields[i] );}
// Blanks out all fields in the record
void Clear() { for( int i=0; i < FieldCount; i++ ) Fields[i]->Clear(); }
// Prompts user to populate all fields
void Populate()
{
for( int i=0; i < FieldCount; i++ )
{
char Input[128];
cout << "Enter value for " << Fields[i]->GetName() << ":" << endl;
cin >> Input;
Fields[i]->SetFromString( Input );
}
}
// Dumps all fields in record
void Dump()
{
cout << "Record contents: " << endl;
for( int i =0; i < FieldCount; i++ )
cout << " " << Fields[i]->GetName() << " -> " << Fields[i]->GetAsString() << endl;
}
int GetFieldCount() const { return( FieldCount ); }
private:
MField * Fields[128]; // The 128 should really be dynamic
int FieldCount;
// Note: For now, I disable you from doing assignment and copy, by making them
// private, and throwing an assert of you do execute them
MRecord(const MRecord& Src ) { assert(0); };
MRecord& operator=(const MRecord& Src ) { assert(0); return(*this);};
};
void main()
{
MRecord Rec;
Rec.AddField( new MDateField("BDay") );
Rec.AddField( new MShortField("ID") );
Rec.Populate();
Rec.Dump();
Rec.Clear();
cout << "Record, after Clear: " << endl;
Rec.Dump();
Rec.AddField( new MDateField("HireDate") );
Rec.Dump();
Rec.RemoveField(0);
Rec.Dump();
Rec.Field("HireDate")->SetFromString("1/1/97");
Rec.Dump();
}