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