A (partial) class definition of RecSpec follows:
enum BOOL { FALSE, TRUE}; class RecSpec { public: enum field_type { INT, STR }; RecSpec(unsigned int maxstrlen, field_type sortfield); BOOL operator==(const RecSpec &rs) const; int compare(const void *rec1, const void *rec2) const; BOOL print_rec(const void *rec) const; BOOL get_from_file(ifstream &is, void *buf) const; unsigned int rec_size() const; private: const unsigned int max_str_len; const field_type sort_field; };When an object of type RecSpec is declared, the private fields are initialized by the constructor in the obvious way. Again, note that the RecSpec object has no field to actually store a record; it simply keeps some descriptive information about (a collection of) records, and provides some useful methods to manipulate such records. field_type is used to specify (to the constructor) which field the records will be sorted on. operator== is used to verify that two RecSpec objects are the same, allowing their associated records to be merged (see step 2). Two RecSpec objects are defined to be equal if they both use the same field for sorting, and if they have the same maximum string length. compare compares the sort fields of the two given records. It returns a negative value is rec1 < rec2, 0 if rec1 == rec2, and a positive value if rec1 > rec2. print_rec "nice-prints" a record to the standard output (see cout). get_from_file reads a record from an input file into a buffer. It does not manipulate the data in any shape or form. You should use the fstream classes to do this (see section 3 for more details). rec_size returns the size of the records associated with this RecSpec (you might want to think about making this an inline method).
A (partial) class definition for Seq is shown below:
class Seq { public: Seq(const RecSpec *recinfo); ~Seq(); virtual BOOL get_next_rec(void *&recptr) = 0; inline const RecSpec *get_rec_spec() const { return rec_info; } protected: const RecSpec *rec_info; };The constructor for Seq should initialize rec_info. get_rec_spec should return a const pointer to the RecSpec. get_next_rec attaches the pointer argument to a buffer containing the next record. It returns TRUE on success and FALSE on error. If there are no more records to be passed, get_next_rec returns TRUE, but assigns the pointer argument to NULL. You should check recursively down a Seq tree that all RecSpecs are in fact equal before get_next_rec can be executed (see MergeSeq for more information on this).
A (partial) class definition for FileSeq is shown below:
class FileSeq : public Seq { public: FileSeq(const char *name, const RecSpec *recinfo); ~FileSeq(); virtual BOOL get_next_rec(void *&recptr); private: ifstream in_file; char *buffer; };The constructor for FileSeq should open a file for reading and allocate a buffer large enough for the record sizes it will have to handle.
The destructor will close the file and free the buffer.
get_next_rec should read the next record into the buffer and return it as was discussed for Seq.
A (partial) class definition for MergeSeq is shown below:
class MergeSeq : public Seq { public: MergeSeq(Seq *s1, Seq *s2); ~MergeSeq(); virtual BOOL get_next_rec(void *&recptr); private: Seq *seq1; Seq *seq2; };The MergeSeq class constructor accepts two Seq objects as arguments. It should first check that the sequences are mergeable by comparing their two RecSpecs (using the == operator). If so, a MergeSeq object should be created with the RecSpec object returned from the get_rec_spec method on one of the input sequences passed as the argument to the Seq constructor.
The destructor should recursively delete its children. This way, a Seq tree can be deleted simply by calling delete on its root.
get_next_rec should compare the two current records, using the RecSpec::compare method, and return the lesser. Some sort of a flag will be needed to remember which input's current record has not yet been used (the greater record from the last call).
Empty sequences and end-of-sequence should be handled along the lines indicated in FileSeq.
Use the fstream classes for I/O operations. (To do so, you must #include both iostream.h and fstream.h in your header file.)
Both FileSeq and MergeSeq objects should operate in ``lazy manner'', e.g., the input file should be read one record at a time in response to successive calls to get_next_rec on a FileSeq object.
If you want to modify any of the class definitions shown above by adding/removing/modifying private and/or protected members, feel free to do so. But you must keep the public interfaces as they have been described above,
You should use C++'s memory management (new and delete). Do not use malloc and free! Be sure to study how to use new and delete for arrays.
You are expected to check for error conditions, and ensure that your code including all the public methods above terminates gracefully on all inputs, with meaningful error msgs. While some error conditions have been explicitly discussed, e.g., get_next_rec on an empty file, others have not, e.g., what happens when RecSpec::get_from_file is called on an empty file. So think carefully about potential errors, and write defensive code.