Description

5/5 - (2 votes)

1 Objectives

The objectives of this assignment are for you to practice: (1) the use of C++ I/O streams, includ-ing error handling, (2) writing a simple class with constructors, accessors and mutators, and (3) dynamic allocation and de-allocation of one-dimensional arrays. You will do so through the design of a program that parses drawing commands from the standard input, displaying appropriate error messages if necessary, and by creating and maintaining objects that represent drawn shapes.

2 Problem Statement

The assignment consists of two main parts. In the rst part, you will write a command parser that provides a textual input interface to your program. The parser takes a sequence of commands as input. The commands create, delete modify and display shapes to be drawn on the screen. Each command consists of an operation keyword followed by arguments. The command and the arguments are separated by one or more spaces. Thus, the code you will write for this part of the assignment should take input from the standard input, parse it, verify that it is correct, print a response or error message and either create, delete or modify shape objects that represent the shapes speci ed in the command. The command parser loops, processing input as long as input is available.

In the second part of the assignment, you will implement a simple \database” of objects that stores the created shapes. To do so, you will implement a class called shape that is used to store the shape properties. Further, you will create and maintain a dynamically allocated array of pointers to shape objects to keep track of shape objects created and deleted.

In this assignment, you will not actually draw the shapes to the screen, just process the com-mands and maintain the database of the shape objects.

3 String Streams

In the lectures, you were introduced to the standard input stream (handled by cin) as well as user-created le streams (handled by ifstream objects that you create). There is one more useful type of user-de ned streams, namely string streams. These streams come handy when processing input one line at a time, as you will do in this assignment. The rest of this section introduces you to string streams and how to use them. You will nd that they are not that much di erent than using cin or ifstream.

String streams allows the extraction of input from a string, as opposed to from the keyboard (cin) or a le (ifstream)1. The following example demonstrates how this may be done.

 

1
1 #include <iostream>

2 using namespace std;

3 #include <string>

4 #include <sstream>

5

6 int main () {

7int anInteger;

8string inputLine = “204 113 ten”;

9

10 stringstream sin (inputLine);

11

12 sin >> anInteger; // Extracts 204

13 if (sin.fail()) return (-1);

14 sin >> anInteger; // Extracts 113

15 if (sin.fail()) return (-1);

16 sin >> anInteger; // Extraction fails

17 if (sin.fail()) return (-1);

18

19 return (0);

20 }

 

The #include <sstream> on line 4 imports the de nition of string streams, allowing it to be used in the example. The main function creates a string variable called inputLine on line 8 and initializes it to “204 113 ten”. The declaration on line 10 creates a new string stream handler called sin (ala cin, but you can give it any other name). This stream is initialized from the inputLine string variables created and initialized earlier2.

Once this is done, we can use the sin handler in the same way we use cin. We can extract an integer from the stream, as shown on line 12. The handler sin has the same set of ags that cin has. Thus, we can check if the extraction operation failed by invoking the method sin.fail(). In the example, the extraction succeeds and the value 204 is placed in anInteger. The same happens for the second extraction on line 14, which extracts 113. In contrast, the third extraction on line 16 fails, the value of anInteger is not a ected and main returns with exit code -1.

The above example is not very interesting because it extracts input from a string initialized by the program and has the foreknowledge that three integers are expected. More interesting is when we wish to extract input from a string provided by the user and we have no knowledge of how many extractions we have to do. The example below illustrates how to do this.

In the example, the while loop iterates until there is no more input. In the loop, the method cin.getline() is used to read the input the user provides through the keyboard and places the entire stream received by cin into the string inputLine. This includes all the white spaces in the stream (see your lecture notes for details). It also appends an eof to the stream. Thus, while a cin stream may end with an Enter or an end-of- le (eof) a string stream always ends with an eof. In the example, we assume the user entered 204 113 10 as input. Thus, inputLine contains “204 113 10”.

The string inputLine is then used to build the string stream handler called sin on line 14.

2A copy of the string variable is made inside the string stream. Thus, if inputLine changes after the string stream is created, the stream in sin does not change.

Subsequently, sin is used to extract an integer from the input (the string), as shown on line 16. The rst extraction succeeds and thus the fail and eof ags of sin are false. The integer (204 in our example) is printed to the standard output and the moreInput ag remains true. The next two iterations of the while loop extract the next two integers, 113 and 10, and prints them to the output.

On the next iteration of the while loop, the extraction fails because the eof is encountered. Both the fail and the eof ags are set to true. The code checks for these ags as shown on lines 17 and 18. Since the eof ag is true, the moreInputs ag is set to false (line 19) causing the while loop to exit and the program to terminate. Since the program immediately exits after checking the eof ag, there is really no need to clear the ags of sin, as the comment indicates in the code on line 20. Thus, it is safe to remove this line from the code.

1 #include <iostream>

2 using namespace std;

3 #include <string>

4 #include <sstream>

5

6 int main () {

7int anInteger;

8string inputLine;

9

10 bool moreInput=true;

11 while (moreInput) {

12 cin.getLine(inputLine);

13
14 stringstream sin(inputLine);

15
16 sin >> anInteger;

17 if (sin.fail()) {

18 if (sin.eof()) {

19 moreInput = false;
20 sin.clear(); // Not necessary
21 else {
22 cout << “Bad input, please re-try\n”;
23 sin.clear(); // Not necessary
24 sin.ignore(10000,’\n’); // Again not necessary

25 }

26 }

27 else cout << “The integer read is: ” << anInteger << endl;

28 }

29

30 return (0);

31 }

Now, let’s assume that the user provides ” 204 113 ten” as input. The rst two extraction succeed as above. However, the third extraction fails because of the ten. The fail ag is set to true but the eof ag is set to false. The code then checks if the reason of failure is the eof (line 18), and this is not the case. A message is printed to instruct the user that the input is bad and

to re-try. When using cin the ags must be cleared (using cin.clear()) and the input stream must be ushed using cin.ignore(). However, with string streams, this is unnecessary since we simply discard the input by reading another line from cin using getline() and then rebuilding the stream handler using the new input. This automatically resets the ags and ushes the old input, replacing it by the new one. Thus, the two calls on lines 23 and 24 are not really necessary and can be removed from the code.

The use of string streams is helpful when input must be processed one line at a time and the user is not allowed to break input across multiple lines of input, separated by Enters. String streams allows your program to get the entire line of input, analyze it and decide if the line is valid or not. While this can be done using cin, it is more di cult since cin allows user input to be split into multiple lines. Indeed, this is the case for this assignment and the skeleton of the main program (included with the lab release) shows a modi ed version of the above example.

4 Speci cations

It is important that you follow the speci cations below carefully. Where the speci cation says shall or must (or their negatives), following the instruction is required to receive credit for the assignment. If instead it says may or can, these are optional suggestions. The use of should indicates a recommendation; compliance is not speci cally required. However, some of the recommendations may hint at a known-good way to do something or pertain to good programming style. Your code will be marked subjectively for style, so it’s best to take the recommendations unless you have a good reason not to.

Example input and output for the program are provided in Section 6 for your convenience. They do not cover all parts of the speci cation. You are responsible for making sure your program meets the speci cation by reading and applying the description below.

4.1 Coding Requirements

1. The code you will write shall be contained in two source les named main.cpp and shape.cpp. Skeletons of the two les are released with the assignment’s zip le. Also released are some include (i.e., .h) les. These include les shall not be modi ed in any way. However, you may make use of functions to split up the code for readability and to make it easier to re-use parts of the code in the future, so long as these functions are implemented in one of the aforementioned two .cpp les.

2. Input and output must be done only using the C++ standard library streams cin and cout.

3. The stream input operator >> and associated functions such as fail() and eof() shall be used for all input. C-style IO such as printf and scanf shall not be used.

4. Strings shall be stored using the C++ library type string, and operations shall be done using its class members, not C-style strings.

5. C-library string-to-integer conversions (including but not limited to atoi, strtol, etc) shall not be used.

Argument Description, type, and range

name a string consisting of any non-whitespace characters3; except strings that

represent commands, shape types or the reserved word all.
type a string that represents the type of a shape and must be one of: ellipse,
rectangle or triangle
loc a positive integer (0 or larger) that represents the location of the shape in
either the x or y dimension
size a positive integer (0 or larger) that represents the size of the a shape in
either the x or y dimension
value a positive integer (0 or larger) that represents the maximum number of
shapes in the database
angle a positive integer between 0 and 360 that represents angle of rotation of a
shape
Table 1: Acceptable input arguments

4.2 Command Line Input

Input will be given one command on one line at a time. The entire command must appear on one line. All input must be read using the C++ standard input cin. The program shall indicate that it is ready to receive user input by prompting with a greater-than sign followed by a single space (> ); see Section 6 for an example. Input shall always be accepted one line at a time, with each line terminated by a newline character4. If there is an error encountered when parsing a line, the program shall print an error message (see Section 4.3), the line shall be discarded, and processing shall resume at the next line. The program shall continue to accept and process input until an end-of- le (eof) condition is received5.

Each line of valid input shall start with a command name, followed by zero or more arguments, each separated by one or more space characters. The number and type of arguments accepted depend on the command. The arguments and their permissible types/ranges are shown below in Table 1.

Command Arguments Output if Command is Valid

maxShapes value New database: max shapes is value
create name type loc loc size size Created name: type loc loc size size
move name loc loc Moved name to loc loc
rotate name angle Rotated name by angle degrees
draw name Drew name: type loc loc size size
draw all Drew all shapes
delete name Deleted shape name
delete all Deleted: all shapes

Table 2: Valid commands and arguments and their output

The valid commands, their arguments, and their output if the command and its arguments are

3 Whitespace characters are tab, space, newline, and related characters which insert \white space”; they mark the boundaries between values read in by operator<<. All other characters (digits, letters, underscore, symbols, etc.)

are non-whitespace characters.
4A newline character is input by pressing Enter.
5eof is automatically provided when input is redirected from a le. It can also be entered at the keyboard by pressing Ctrl-D.

Page 5 of 12

Error message Cause

invalid command The rst word entered does not match one of the valid commands
invalid argument The argument is not of the correct type. For example, a oating point
number or a string may have been entered instead of an integer where
an integer is expected.
invalid shape name The name used for a shape is a reserved word (e.g., a command name
or a shape type)
shape name exists A shape with the name name exists in the database, i.e., has once
been created and has not been deleted
shape name not found A shape with the name name speci ed in a command does not exist
invalid shape type The type used for a shape is not one of the allowed types
invalid value The value speci ed in a command is invalid. For example, a less than
0 value for a loc argument or a rotation angle not between 0 and 360.
too many arguments More arguments were given than expected for a command
too few arguments Fewer arguments were given than expected for a command
shape array is full An attempt to create more shapes than the argument given to the
maxShapes command

Table 3: List of errors to be reported, in priority order

all legal are shown below in Table 2. Notice that the last two commands (draw and delete) can be run in two ways (depending on their argument): with a speci c shape name, or with the keyword all. In the case of the draw all command, the program prints not only the message shown in the table, but also all the shapes in the database (see the example in Section 6). The program shall verify that the command and arguments are correctly formatted and within range, and that a command is followed by the correct number of arguments. The handling of command names shall be case-sensitive.

The rst line of input to your program will always be the maxShapes command to set the maximum allowed number of shapes. You shall assume that the command will be entered only once and always as the rst input line. You shall further assume that this command will not have any errors in it.

If there is an error, a message shall be displayed as described in Section 4.3. Otherwise, a successful command produces a single line of output on the C++ standard output, cout, as shown in Table 2. The values in italics in Table 2 must be replaced with the values given by the command argument. Strings must be reproduced exactly as entered. Where locs or sizes are printed, they shall appear on the order entered in the command.

4.3 Error Checking

The program must check that the input is valid. It must be able to identify and notify the user of the following input errors, in order of priority. Where multiple errors exist on one input line, only one should be reported: the one that occurs rst as the line is read from left to right. If more than one error could be reported for a single argument in the line, only the error occuring rst in Table 3 should be reported.

Errors shall cause a message to be printed to cout, consisting of the text \Error:” followed by a single space and the error message from Table 3. In the messages, italicized values such as name should be replaced by the value causing the error. Error message output must comply exactly (content, case, and spacing) with the table below to receive credit. There are no trailing spaces

Page 6 of 12

following the text.

The program is not required to deal with errors other than those listed in Table 3. The following are some clari cation on the errors.

1. The commands and the shape types are case sensitive. Thus, while a shape cannot be named all, draw or triangle, it can be named All, Draw or triAngle.

2. For every line of input, your program must output something. Either a message indicating success (Table 2) or an error (Table 3). So for an empty line of input (nothing or just whitespace) your program should print Error: invalid command.

3. Only the rst error from the left should be reported per line of input. In the case of miss-ing/extra arguments, these are errors in the arguments that are missing/extra and should be reported only if the preceding arguments were valid.

4. You should let the extraction operator (<<) do the work for you. Recall that the operator stops when the next character cannot be converted to the destination type. This may or may not be a white space. Learn how to use the cin.peek() method explained in Section 5 below.

4.4 The shape Class

The shape class holds the properties of a shape, including its name, type, location, size and rotation. The de nition of the class appears in shape.h, which is re-produced in Figure 1. Examine the le and read through the comments to understand the variables and methods of the class. You must implement this class in the le shape.cpp. You are NOT allowed to modify shape.h in any way.

4.5 The Database

The program shall keep track of all shapes using an array whose elements are pointers to shape objects. The array should be dynamically allocated after the rst line of the input to have a size that matches exactly the argument given to the maxShapes command. The array elements should all be initialized to NULL. This array is declared in main.cpp. An integer variable shapeCount is used to track the actual number of shape objects stored in the database. Figure 2 depicts what the database may look like during program execution.

All shapes shall be stored in the array (i.e., by having the pointer element of the array point to a shape object) starting at element 0 for the rst shape added and incrementing from there. When a shape is deleted, the memory allocated to the object must be freed and the element of the array that used to point to the shape must be assigned the value NULL6. When a new shape is added after another shape is deleted, it must be added at location shapeCount. Thus, you must not \pack” the array after deletions or reuse \deleted” locations.

1 //

2 // shape.h

3 // lab3

4 //

5 // Created by Tarek Abdelrahman on 2018-08-25.

6 // Copyright 2018 Tarek Abdelrahman. All rights reserved.

7 //

8

9 // *********** ECE244 Student: DO NOT MODIFY THIS FILE ***********

10

11 #ifndef shape_h

12 #define shape_h

13

14 #include <iostream>

15 #include <string>

16 using namespace std;

17

18 #endif /* shape_h */

19

20 class shape {

21 private:

22 string name; // The name of the shape
23 string type; // The type of the shape (see globals.h)
24 int x_location; // The location of the shape on the x-axis
25 int y_location; // The location of the shape on the y-axis
26 int x_size; // The size of the shape in the x-dimension
27 int y_size; // The size of the shape in the y-dimension
28 int rotation = 0; // The rotations of the shape (integer degrees)

29 public:

30 // Build a shape object with its properties

31 shape(string n, string t, int x_loc, int y_loc, int x_sz, int y_sz);

32

33 // Accessors

34 string getType(); // Returns the type
35 string getName(); // Returns the name of the shape
36 int getXlocation(); // Returns location of the shape on the x-axis
37 int getYlocation(); // Returns location of the shape on the y-axis
38 int getXsize(); // Returns the size of the shape in the x-dimension
39 int getYsize(); // Returns the size of the shape in the y-dimension
40

41 // Mutators

42 void setType(string t); // Sets the type (see globals.h)
43 // No error checking done inside the method
44 // The calling program must ensure the type
45 // is correct
46 void setName(string n); // Sets the name of the shape

47 void setXlocation(int x_loc); // Sets location of the shape on the x-axis

48 void setYlocation(int y_loc); // Sets location of the shape on the y-axis

49 void setXsize(int x_sz); // Sets size of the shape in the x-dimension
50 void setYsize(int y_sz); // Sets size of the shape in the y-dimension
51
52 void setRotate(int angle); // sets the rotation of the shape
53

54 // Utility methods

55 void draw(); // Draws a shape; for this assignment it
56 // only prints the information of the shape

57 };

Figure 1: De ntion of the class shape (you may not modify this de nition)
0 1 max_shapes-1

shapesArray

 

 

 

shape shape shape

Figure 2: A depction of the database

5 Hints

You can check a stream for end-of- le status using the eof member function.

The ignore member function in iostream may be useful to you if you need to ignore the remainder of a line.

To save typing, you can create one or more test les and pipe them to your program. You can create a text le using a text editor (try gedit, gvim, or the NetBeans editor). If your le is called test.txt, you can then send it to your program by typing main < test.txt. Building a good suite of test cases is important when developing software.

If you want to look ahead (\peek”) at what character would be read next without actually reading it, peek() does that. For instance, if you type \Hello” then each time you run peek() you will get ‘H’. If you read a single character, it will return ‘H’ but then subsequent calls to peek() will return ‘e’.

When interacting with your program from the keyboard, Ctrl-D will send an end-of- le (eof) marker.

Reading from cin removes leading whitespace. When reading strings, it discards all whites-pace characters up to the rst non-whitespace character, then returns all non-whitespace characters until it nds another whitespace. For integers (numbers), it skips whitespace and reads to the rst non-digit (0-9) character.

Remember you can use the debugger to pause the program, step through it, and view variables (including strings).

If you decide to pass the string stream you created to a function, remember that string streams (and other types of streams for that matter) can only be passed by reference, not by value.

A suggested (but not mandatory) structure for your code appears in the skeleton main.cpp le released within the assignment’s zip le.

6 Examples

6.1 A Short Example

The program when rst started, ready to receive input:

>

Now the user types a command (ending with Enter) to create a new database of size 100.

> maxShapes 100

To which the program should respond with the message indicating the successful creation of new database with 100 entries.

New database: max shapes is 100

The user then creates a new ellipse called my circle located at x and y positions of 30 and 40 and with a x and y sizes of 10 and 10.

> create my_circle ellipse 30 40 10 10

To which the program should respond with the message for a successful creation of a shape:

Created my_circle: ellipse 30 40 10 10

6.2 Full session

The following is an example session. Note that the text from the prompt (> ) up to the end of the line is typed by the user, whereas the prompt and line without a prompt are program output.

> maxShapes 3

New database: max shapes is 3

> create my_circle ellipse 50 65 20 20 Created my_circle: ellipse 50 65 20 20

> create my_square rectangle 100 150 60 60 Created my_square: rectangle 100 150 60 60

> create my_triangle triangle 40 75 -90 90 Error: invalid value

> create my_rectangle rectangle 100 275 90 180 Created my_rectangle: rectangle 100 275 90 180

> create ellipse rectangle 100 275 90 180 Error: invalid shape name

> create my_rectangle triangle 70 50 10 5 Error: shape my_rectangle exists

> create second_triangle triangle 70 50 10 5 Error: shape array is full

> move my_circle

Error: too few arguments

> m
Error: invalid command

> move my_circle 70 90

Moved my_circle to 70 90

> rotate my_rectangle 90 100 Error: too many arguments

> rotate my_rectangle 100

Rotated my_rectangle by 100 degrees

> rotate my_rectangle 400 Error: invalid value

> draw my_trinagle

Error: shape my_trinagle not found

> draw my_circle Drew my_circle

my_circle: ellipse 70 90 20 20

> draw all

Drew all shapes

my_circle: ellipse 70 90 20 20

my_square: rectangle 100 150 60 60

my_rectangle: rectangle 100 275 90 180

> delete my_square Deleted shape my_square

> draw all

Drew all shapes

my_circle: ellipse 70 90 20 20

my_rectangle: rectangle 100 275 90 180

> delete all Deleted: all shapes

> draw all

Drew all shapes

>

7 Procedure

Create a sub-directory called lab3 in your ece244 directory, and set its permissions so no one else can read it. Download the lab3 release.zip le, un-zip it and place the resulting les in the lab3 directory. There are two .h les, which you must not modify and two .cpp les in which you will add your code. You must not rename these les or add more les. Create a NetBeans project to build a program called main from the les in lab3. Write and test the program to conform to the speci cations laid out in Section 4. The hints in Section 5 may help get you started, and the example sessions in Section 6 may be used for testing.

The ~ece244i/public/exercise command will also be helpful in testing your program. In this lab, you should submit the executable, i.e., main, to exercise:

~ece244i/public/exercie 3 main

As with previous assignments, some of the exercise test cases will be used by the autotester during marking of your assignment. We will not provide all the autotester test cases in exercise,
however, so you should create additional test cases yourself and ensure you fully meet the speci – cation listed above.

8 Deliverables

Submit the main.cpp, shape.cpp, shape.h and globals.h les (even though the last two have not changed) as lab 3 using the command

~ece244i/public/submit 3

The programming style (structure, descriptive variable names, useful comments, consistent inden-tation, use of functions to avoid repeated code and general readability) shown in your code will be examined and graded as well.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Page 12 of 12