OOP344 Assignment Two

From CDOT Wiki
Revision as of 09:32, 6 November 2009 by Corazu (talk | contribs) (Constructors: - minor spelling fix (in sample code block))
Jump to: navigation, search

OOP344 - OOP344 Student List - OOP344 Teams - OOP344 Assignment One - OOP344 Assignment Two - OOP344 IRC Schedules
Under construction...

File Names

Save your work in separate files for each class. Name the files to the same name as the classes. Each class should have a header file and a code file.

For example for the class IOField, create iofield.h and iofield.cpp. The header file should hold the class declaration and any other possible declaration related to the class. the "cpp" file should hold the definition (implementation) of the class and its methods and possible functions.

Create a Make file to build your project with respect to dependencies of classes.

SVN Quick Notes

  1. Checkout the code $svn co svn://zenit.senecac.on.ca/ops344_093aXX/trunk/PRJ --username yourUserName
  2. Add selected ciol.h ciol.cpp and if present the class files (all compiled) $svn add filename
  3. Commit the new files $svn commit
  4. Team members check out the individually
  5. development starts.

How to reuse your C code in C++ programs

Include your already existing C code into your C++ code as follows:

extern "C"{
#include "ciol.h"

This tells to C++ compiler, the included header file contains C functions and should be complied and called as such. Remember, you do not need and should not rename your ciol.c to ciol.cpp, since the compiler is already aware of the C functions in ciol.c.

Team Project Wiki Pages

Add a link to your Team project Wiki pages here:

West Side Connection

A Team

All are welcome


Team ++ website


Team Temporary Name

Team Funktion website

Best Team Ever :)


General Definition Header file

create a file called: io_def.h. This file will contain any nessecary definitions or inclusions for the project.

for now add the following define statements in io_def.h

#define IO_NO_ACTION (0x1)
#define IO_CLEAR (0x2)
#define IO_SHOW_ALL (0x4)
#define IO_SHOW_ERROR (0x8)
#define IO_SHOW_HELP (0x16)

#define _FRAME_CHARS "|+-+|+-+"
#define _FRAME_UNDERLINE "      - "
#define _FRAME_UNDEROVER "  -   - "

#ifdef NO_FUNC
# undef NO_FUNC
#define NO_FUNC ((void(*)(MessageStatus, IO_Form&))(0))

#ifdef NO_VFUNC
# undef NO_VFUNC
#define NO_VFUNC ((bool(*)(const char*, IO_Form&))(0))

enum MessageStatus{ClearMessage,SetMessage};

Mandatory Classes

As the first part of your project this semester, you are to create few classes to encapsulate Console Input Output Library


IO_Field is the base class for all different types of Fields on a Form.

class IO_Form;
class IO_Field{
  int _row;
  int _col;
  void* _data;
  IO_Form* _owner;
  IO_Field(int row, int col, void* data = (void*)0);
  void* data(void);
  virtual void display(void)const = 0;
  virtual int edit(void)= 0;
  virtual bool editable(void)const = 0;
  virtual void set(const void *) = 0;
  virtual void set(IO_Form* owner);
  int getRow(void)const;
  int getCol(void)const;
  virtual ~IO_Field(void);


IO_Field(int row, int col, void* data = (void*)0);

This constructor sets the corresponding attributes to the values of incoming arguments.

Public Methods


void* data();

Returns the value of the data attribute.


virtual void set(IO_Form* owner);

Sets the _owner attribute to incoming argument.


int getRow(void)const;
  • if _owner is NULL, it returns the _row attribute
  • if _owner is not NULL it returns the sum of _row and _owner->getTop(); (see IO_Frame for getTop())


int getCol(void)const;
  • if _owner is NULL, it returns the _col attribute
  • if _owner is not NULL it returns the sum of _col and _owner->getLeft(); (see IO_Frame for getLeft())


'''virtual ~IOField();'''

An empty destructor (the body of the method is blank)

Pure Virtual Methods

virtual void display() = 0;
virtual int edit()= 0;
virtual bool editable() = 0;
virtual void set(const void *) = 0;

These are pure virtual methods enforcing the creation of the identical methods in the derived classes. This make the IO_Field class an abstract class. Note: The purpose of passing a IO_Form pointer to the Edit method, is to make the future Edit methods capable of sending a message (errors and help messages)to the Screen they are being Edited on.


IO_Frame class, encapsulates a frame. It Draws a frame at left top corner of left and top with specified width and height.

A Frame can be instantiated as follows:

IO_Frame(int top, int left, int width, int height, const char* frameChars = (const char*)0);

the frameChars are characters used to draw the frame, characters in order are:

  1. left side
  2. left top corner
  3. top side
  4. top right corner
  5. right side
  6. right bottom corner
  7. bottom side
  8. bottom left corner

So to draw this:

  |        |
  |        |  
  |        |

frame chars should be:


If however, the frameChars argument is missing (it is NULL) then a defaut, defined value in "io_def.h"; _FRAME_CHARS should be used.

Public Methods


void display(int topOffset=0, int leftOffset=0)const;

display(), starts from top + topOffset and left + leftOffset and draws the frame to the width and hight received form the constructor.


int getLeft();

returns the _left attribute;

int getTop();

returns the _top attribute;

void set(int top, int left);

sets the _top and _left attributes to corresponding arguments.


IO_Frame has a virtual destructor. This destructor does nothing.


IO_Frame is inherited into a container class called IO_Form. IO_Form organizes the IO_Field classes and give the user a panel to use them sequentially.

IO_Form should be able to hold unlimited number of IO_Fields. (Use either a dynamic array, or linked list structure of IO_Fields to implement this)


IO_Form is constructed as follows:

 IO_Form(bool framed = false, 
         int row = 0, int col = 0 , int width = 0 , int height = 0, 
         const char* frameChars = (const char*)0);

framed: if true, IO_Form will use its parent to draw a frame around itself when display()ed row, col, width and height are passed to the parent under following conditions:
If framed is true and "width or height" are 0 (zero) then the full size of the terminal is used for dimensions of the IO_Frame.


virtual ~IO_Form();

When IO_Form is going out of scope, is being destroyed, all dynamic IO_Fields are deleted. (the rest will not be deallocated)

Private Methods

Adding a Field to the Form

the IO_Form must be able to add several IO_Field classes to itself, one at a time and then provide the user, the means of editing them in order they were added.

int add(IO_Field* f, bool dynamic, bool submitter);

IO_Form uses this private add method to provide means of adding fields through several public add methods. this method adds an IO_Field to the end of the IO_Fields in the IO_Form;

  • f: is the address of the IO_Field begin added to the IO_Form
  • dynamic: if set to true, it sets this IO_Field to be deallocated by IO_Form's destructor at the end;
  • submitter: if set to true, it tags this IO_Field to terminate the IO_Form's edit() if ENTER_KEY is hit. see the edit() method for more info.

add() also calls set(IO_Form* owner) of IO_Field and passes its pointer (this) to it. By doing this, each IO_Field will know who is its owner. add() returns the number of fields currently in IO_FORM.

Public Methods

Add methods

int add(IO_Field* f, bool submitter  = false);

Uses the private add() method to add a dynamic IO_field.

int add(IO_Field& f, bool submitter = false);

Uses the private add() method to add a non-dynamaic IO_field.

int addHelp(IO_Label* L);

Uses the private add() method to add a dynamaic IO_field and also saves its address for the general help message label of the Form.

int addHelp(IO_Label& L);

Uses the private add() method to add a non-dynamaic IO_field and also saves its address for the general help message label of the Form.

int addError(IO_Label* L);

Uses the private add() method to add a dynamaic IO_field and also saves its address for the general error message label of the Form.

int addError(IO_Label& L);

Uses the private add() method to add a dynamaic IO_field and also saves its address for the general error message label of the Form.

IO_Form& operator<<(IO_Field* f);

Uses the private add() method to add a non-submitter, dynamic IO_field.

IO_Form& operator<<(IO_Field& f);

Uses the private add() method to add a non-submitter, non-dynamic IO_field.

Setting General Error and Help messages

void setError(const char* mes);
void setHelp(const char* mes);

Set the text of the general Error message and the general Help message of the Form.

Number of fields in Form

int length();

Returns number of fields currently in IO_Form.

Acessing Field Data

void* data(unsigned int fieldnumber=0);

Returns address of the data of "fieldnumber"th, IO_Field in the IO_Form. If "fieldnumber" is zero, then it returns the data of the current IO_Field.

The index operator

IO_Field& operator[](unsigned int index);

Returns the reference of the IO_Field, number "fieldnumber-1". First Field has the index 0;

Displaying the Form

void display(unsigned int how = IO_CLEAR | IO_SHOW_ALL ,unsigned int fieldnumber = 0 );  

Displays the Form and all the Fields on it.

fieldnumber is the number of the field that was returned by any of the add() functions.

how: is a binary mask, and following bits can be set in it:

IO_CLEAR (0x2u)
IO_SHOW_ALL (0x4u)
IO_SHOW_HELP (0x10u)

The display function work in the following order:

  1. If IO_CLEAR bit is set in how, then before anything the screen should be be cleared.
  2. If the field number is 0 (zero) then
    1. if IO_SHOW_ALL is set in how, then all the fields are displayed in the order they were added
    2. otherwise either of IO_SHOW_ERROR or IO_SHOW_HELP is set, only their corresponding designated IO_Label will be displayed.
  3. if the filed number is not zero, then the IO_Field number "fieldnumber" will be displayed. If the number is larger than the number of fields in the IO_Form then it will be rotated back from the beginning. (i.e. if there are 4 fields in the form and fieldnumber is 5, then filed number 1 will be displayed)

Editing(running) the Form

int edit(unsigned int* fieldnumber = (unsigned int*)0, bool Clear = true);

If neither of the IO_Fields is editable, it displays all the IO_Fields, then waits for a key entry and then ends the function by returning the key entered.

If the fieldnumber pointer is NULL, it should use a local variable’s addrss with the value of “0” zero instead.

if *fieldnumber is (0) zero, then display all the fields before editing the fields and then set *fieldnumber to (1) one.

Start editing from field number *fieldnumbe.

Call the edit of each field and depending on the value returned, do the following:

  • For TAB_KEY and DOWN_KEY, go to next editable IO_Field, if this is the last editable IO_Field , restart from the IO_Field number one.
  • For UP_KEY go to the previous editable IO_Field, if there is no previous editable IO_Field, go to the last editable IO_Field of the IO_Form. (continue the search from the last IO_Field in the IO_Form).
  • for ENTER_KEY do exactly as TAB_KEY, except that if the current IO_Field is a submitter (is tagged to be a submitter when added), in which case terminate the eidt function returning the ENTER_KEY.
  • for any other key, terminate the edit function returning the character which caused the termination.


IO_Label class mostly, encapsulates the io_display() function. Inherit a new class called IO_Label from IO_Field to Display a text message on IO_Form. In addition to the attributes of its parent IO_Field, IO_Label has a private integer attribute called _len. _len is used to hold the length of Field in which the text is to be displayed. IO_Label holds its data dynamically and will never point to an external one. IO_Label can be created in two ways;

  IO_Label(int row, int col, int len);

This constructor will create an empty (blank) IO_Label with capacity of len characters. In this case row and col are passed to the parent for initialization and _len (the attribute) is set to the incoming argument len. Then IO_Field::_data will be set to the address of a newly allocated memory to the size of _len + 1 bytes which also will be set to an empty string. Note that data is a void pointer; to use it here, you must cast it to a character pointer!

  IO_Label(char* str, int row, int col, int len = -1);

If len is greater than zero, This constructor will create an IO_Label by allocating len + 1 characters pointed by IO_Field::_data, and then the contents of str is copied into IO_Field::_data up to len characters. _len attribute is set to len; If len is less than or equal to zero, then len will be set to the length of str and then constructor will work as previous case. Like the previous constructor it will pass row and col to its parent.

Public Methods

void Display(void);

Display() is a direct call to io_display() function printing _data, at getRow() and getCol() up to _len characters. If _len is less than zero, then _data will be printed to the end.

  void Set(const void* str);

Set() will copy the content of str into IO_Field::_data up to _len characters, if _len is not negative.

bool Editable(void)const;

Editable() returns false!.

  int Edit();

Edit(), Calls Display() and returns 0. This method ignores the Owner, IO_Form pointer.


The destructor, delete[]s the _data. (make sure _data is casted to char* before deleting.

  IO_Label &operator=(const char* str);

operator=(), Calls Set() and returns itself.


Inherit IO_Field and IO_Frame into a new class called IO_Edit. (Read: Practical Programming Techniques Using C++, pages 94 to 96, Multiple Inheritance topic)

IO_Edit, encapsulates the io_edit() function of ciol library when isTextEditor is false. IO_Edit calls the io_edit function to edit a string that is either created dynamically by the IO_Edit itself, or a string that is external and is received through the constructor aruguments.

Also IO_Edit gives the user the option of surrounding the editing line with a frame.

Editing without a frame at row:2 col:5

2    Hello! I am editing a text_

Editing with a frame at row:2 col:5 (the actual editing happens at row:2+1 and col: 5+1

2    +-------------------------------------+
3    |Hello! I am editing a text_          |
4    +-------------------------------------+

Constructors and Destructor


IO_Edit can be created in two ways:

IO_Edit::IO_Edit(int row, int col, int fieldlen,
    int maxdatalen, int* insertmode,
    bool framed = false, const char* frameChars = (const char*)0);

This constructor dynamically creates an array of maxdatalen + 1 chars (an empty string) and sets IO_Field::_data to point to it.

row and col and frameChars will be passed directly to the Frame class's top and left and frameChars, respectively. But since a IO_Frame needs to surround the edit field, it has to have one char extra for each side. Therefore, (fieldlen + 2) should be passed to IO_Frame as width and 3 should be passed to IO_Frame as height.

On the other hand, the IO_Field class holds the actual coordinate at which, the editing should happen. So, depending on the value of the framed being false or true the values, (row and col) or (row+1 and col+1) will be passed to IO_Field's constructor respectively.

The IO_Edit In this case is marked to be dynamic, so at destruction time, the memory is deallocated.

All the arguments needed for ciol's io_edit() function should be kept as attributes so they can be used at edit() time.

IO_Edit::IO_Edit(char* str, int row, int col, int fieldlen,
    int maxdatalen, int* insertmode, 
    bool framed = false, const char* frameChars = (const char*)0);

This constructor works exactly like the above constructor, with one exception. The data being edited is not dynamic here. So the IO_Fields::_data should point where the str argument is pointing to. Obviously, in this case IO_Edit should be marked as non-dynamic so at destruction time, the memory is NOT deallocated.


The IO_Edit's destructor, checks to see if the object is marked to be dynamic. In this case it will delete[] the the character content of IO_Field's _data.

Public Methods


void IO_Edit::display()const;

display, first checks to see if IO_Edit is framed. if it is it will call the IO_Frame's display(), using _owner->getTop() and _owner->getLeft() as its topOffset and leftOffset agruments. Then it will makes a direct call to ciol's io_display() function passing IO_Field's data, getRow() and getCol() and also the fieldlen value form IO_Edit's attrubutes.

Passing _owner->getTop() and _owner->getLeft() to IO_Frame's display() and also, using getRow() and getCol() for extracting row and col from IO_Field, makes row and col of the IO_Edit, relative to top, left of the form it is on. (if IO_Edit belongs to an IO_Form, that is, if _owner is not NULL)


int IO_Edit::edit();

edit() makes a direct call to ciol's io_edit with corresponding data from the IO_Edit's attributes.

the Owner argument is simply ignored.

edit() returns the value returned by ciol's io_edit.


ibool  IO_Edit::editable()const;

Always returns true;


void IO_Edit::set(const void *str);

set() copies the incoming string str into the IO_Field's _data up to maxdatalen characters.

(note: if using strncpy() make sure the string is null terminated)


Inherit IO_Edit to a Validated line editor called IO_Vedit. IO_Vedit, works exactly like an IO_Edit, with two differences. IO_Vedit has two extra attributes that are pointers to Validation and Help functions:

  void (*_Help)(MessageStatus, IO_Form&);
  bool (*_Validate)(const char*, IO_Form&);


  IO_Vedit(int row, int col, int fieldlen,
    int maxdatalen, int* insertmode,
    bool framed = false,
    bool (*Validate)(const char* , IO_Form&) = NO_VFUNC, 
    void (*Help)(MessageStatus, IO_Form&) = NO_FUNC,
    const char* frameChars = (const char*)0);
  IO_Vedit(char* str, int row, int col, int fieldlen,
    int maxdatalen, int* insertmode, 
    bool framed = false,
    bool (*Validate)(const char*, IO_Form&) = NO_VFUNC, 
    void (*Help)(MessageStatus, IO_Form&) = NO_FUNC,
    const char* frameChars = (const char*)0);

These two constructors pass all the information directly to IO_Edit's constructor and then set the "function pointers" attributes to their corresponding arguments.

Public Function

 int edit();
  • if _owner of IO_Vedit is null, then it simply calls the IO_Edit's edit() and terminates (returning the same value as IO_Edit::edit())
  • if _owner is not null
    • if the help function pointer attribute is not NULL, it will call it passing SetMesssage , and *_owner' as arguments (This will show the help message for this field, before editing begins.)
    • if the validation function pointer is not null then it will call the IO_Edit::edit() and validate the data with it by keep repeating the IO_Edit::edit() until either validation function return true or the IO_Edit::edit() was terminated by a non navigation key.
    • Navigation keys are: UP_KEY DOWN_KEY TAB_KEY and ENTER_KEY
    • Right before the IO_Vedit::edit() Terminates, if the help function pointer attribute is not NULL, it will call it again passing ClearMesssage , and *_owner' as arguments (this will clear the help message after editing is done.)
    • IO_Vedit::edit() will return the return value of IO_Eidt::edit().





The Text Editor


Sample Main

#include "io_def.h"
#include "io_form.h"
#include "io_label.h"
#include "io_edit.h"
#include "io_vedit.h"
#include <string.h>
#include <stdio.h>
bool ValidYear(const char* data, IO_Form& F){
  bool res = true;
  int i = 0;
  if( i < 1895 || i > 2010){
    res = false;
    res = true;
  return res;
bool ValidRating(const char*data, IO_Form& F){
  bool res = true;
  double d = -1.0;
  sscanf(data,"%lf", &d);
  if( d<0.0 || d>10 ){
    res = false;
    res = true;
  return res;
void YearHelp(MessageStatus m, IO_Form& F){
   if(m == SetMessage){
    F.setHelp("years valid between 1895 and 2010");
void RateHelp(MessageStatus m, IO_Form& F){
  if(m == SetMessage){
    F.setHelp("Numbers between 0.0 and 10");

int main(void){
  int insert = 1;
  char str[51] = "testing this";
  IO_Form F(true, 1, 3, 70,21 );
  F <<new IO_Label("| Movie Information Entry |", 0, 3)
    <<new IO_Label("Name:", 2,2)<<new IO_Label("Director:", 5, 2)
    <<new IO_Label("Release Date:", 8, 2)<<new IO_Label(8, 21, 10)
    <<new IO_Label("User rating 1->10:", 11, 2)<<new IO_Label(11, 25, 10)
    <<new IO_Label("Classification:", 14, 2)
    <<new IO_Label("Gnere:", 2, 36)
    <<new IO_Label("Commnet:", 13, 29)
    <<new IO_Edit(1, 7, 15, 25, &insert, true, _FRAME_UNDERLINE)
    <<new IO_Edit(4, 11, 15, 30, &insert, true, _FRAME_UNDERLINE)
    <<new IO_Vedit(7,15, 5, 4, &insert, true, ValidYear, YearHelp,  _FRAME_UNDERLINE)
    <<new IO_Vedit(10,20,4, 3, &insert, true, ValidRating, RateHelp, _FRAME_UNDERLINE);
  F.addHelp(new IO_Label(19,2, 60));
  return 0;

Screen Shot of the above program:

   +--| Movie Information Entry |---------------------------------------+
   |                                                                    |
   | Name:                             Gnere:                           |
   |       ---------------                                              |
   |                                                                    |
   | Director:                                                          |
   |           ---------------                                          |
   |                                                                    |
   | Release Date:                                                      |
   |               -----                                                |
   |                                                                    |
   | User rating 1->10:                                                 |
   |                    ----                                            |
   |                            Commnet:                                |
   | Classification:                                                    |
   |                                                                    |
   |                                                                    |
   |                                                                    |
   |                                                                    |
   |                                                                    |


If you want to use a linkedlist to implement IO_Form having a node like this would be a good idea:

class IO_Node{
  IO_Field* _f;
  bool _dynamic;
  bool _submitter;
  IO_Node* next;
  IO_Node* prev;

And then make your IO_Form a linkedlist of IO_Nodes....

With this you can have the IO_field kept in the node and at the same time keep track of the IO_Field being dynamically created and if it is a submitter or not. So the data of the node will be the three attributes; _f, _dynamic, and submitter.

Fardad adds io_textedit

This is an edited dialogue copied from an IRC meeting

fardad: I am going to do IO_TextEdit. I will write the steps as I am doing it

  1. I am adding a headerfile and cpp file to my visual studio this will add the headerfile and the cpp file to the working copy.
  2. In solution exp. rightclick on headerfiles, add / new item
  3. Added code/headerfile: "io_textedit.h"
  4. Now i am righclicking on sourcefiles/newitem code/cpp file
  5. Added inclusion guards code to the headerifle (#ifndef __IO_TEXTEDIT_H__.....)
  6. Included general headerifle in io_textedit.h. It will have all necessary definitions and includes for the project
  7. Adding io_textedit.h and io_textdit.cpp to working copy($svn add ..., this tags them to be added to repository the next time svn is commited)
  8. Compiled and it was successful, so I will commit now

fardad: Done, you can now update you working copy and see what I did. You should do the same for all the other classes. It should not take you more than 30 minutes to do you part, just pick a class and write it.