The Shapes Program

I currently writing a C++ program that calculates area, perimeter, surface area, and volume of common 2D and 3D shapes. Then, the program outputs the results on the console and in a separate text file. The program takes input from a text file called Shapes.input.txt. However, I'm having trouble getting the right calculations outputted as well as correct invalid objects to display. I'll attach my code so far. I'm still new to C++, but if anybody can give me advice on how to figure this out, that would be great!

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
#include <iomanip>
#define PI 3.14159
using namespace std;

vector<string> parseString(string);

int main()
{
    ifstream inFile;
    ofstream outFile;
    inFile.open("Shapes.input.txt");
    outFile.open("Shapes.output.txt");

    cout.precision(2);
    cout << fixed;
    outFile.precision(2);
    outFile << fixed;

    while(!inFile.eof())
    {
        string s;
        getline(inFile, s);
        vector<string> tokens = parseString(s);
        float length = 0, width = 0, height = 0, area, peri, volume;

        for(unsigned int i = 0; i < tokens.size(); i++)
        {
            // cout << "token["<< i <<"] = " << tokens[i] << endl;
            if(tokens[i] == "SQUARE")
            {
                area = length * length;
                peri = 4.0 * length;

                cout << tokens[i] << ": side = " << length << " area = " << area
                     << " perimeter = " << peri << endl;

                outFile << tokens[i] << ": side = " << length << " area = " << area
                        << " perimeter = " << peri << endl;

            }
            else if(tokens[i] == "RECTANGLE")
            {
                area = length * width;
                peri = 2.0 * (length + width);

                cout << tokens[i] << ": length = " << length << " width = " << width
                     << " area = " << area << " perimeter = " << peri << endl;

                outFile << tokens[i] << ": length = " << length << " width = " << width
                        << " area = " << area << " perimeter = " << peri << endl;
            }
            else if(tokens[i] == "Circle")
            {
                area = PI * (length * length);
                peri = 2 * PI * length;

                cout << tokens[i] << ": radius = " << length << " area = " << area
                     << " perimeter = " << peri << endl;

                outFile << tokens[i] << ": radius = " << length << " area = " << area
                        << " perimeter = " << peri << endl;
            }
            else if(tokens[i] == "TRIANGLE")
            {
                area = (1.732 * length * length) / 4.0;
                peri = 3.0 * length;

                cout << tokens[i] << ": side = " << length << " area = " << area
                     << " perimeter = " << peri << endl;

                outFile << tokens[i] << ": side = " << length << " area = " << area
                        << " perimeter = " << peri << endl;
            }
            else if(tokens[i] == "CUBE")
            {
                area = 6 * (length * length);
                volume = length * length * length;

                cout << tokens[i] << ": side = " << length << " surface area = " << area
                     << " volume = " << volume << endl;

                outFile << tokens[i] << ": side = " << length << " surface area = " << area
                        << " volume = " << volume << endl;
            }
            else if(tokens[i] == "BOX")
            {
                area = 2.0 * (length * width) + (length * height) + (width * height);
                volume = length * width * height;

                cout << tokens[i] << ": length = " << length << " width = " << width
                     << " height = " << height << " surface area = " << area
                     << " volume = " << volume << endl;

                outFile << tokens[i] << ": length = " << length << " width = " << width
                        << " height = " << height << " surface area = " << area
                        << " volume = " << volume << endl;
            }
            else if(tokens[i] == "CYLINDER")
            {
                area = 2.0 * PI * (length * length + height);
                volume = PI * length * (length * height);

                cout << tokens[i] << ": radius = " << length << " height = " << height
                     << " surface area = " << area << " volume = " << volume << endl;

                outFile << tokens[i] << ": radius = " << length << " height = " << height
                        << " surface area = " << area << " volume = " << volume << endl;
            }
            else if(tokens[i] == "PRISM")
            {
                area = 3.0 * (length * height) + (1.732 * length * length) / 2.0;
                volume = (1.732 * length * length * height) / 4.0;

                cout << tokens[i] << ": side = " << length << " height = " << height
                     << " surface area = " << area << " volume = " << volume << endl;
            }
            else
            {
                cout << tokens[i] << ": invalid object" << endl;
            }
        }
    }
    inFile.close();
    outFile.close();

    return 0;
}

vector<string> parseString(string str)
{
    stringstream s(str);
    istream_iterator<string> begin(s), end;
    return vector<string> (begin,end);
}


Input File:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SQUARE 14.5 344         
SQUARE                             
RECTANGLE 14.5    4.65           
DIMENSIONS
CIRCLE 14.5

BOX x 2 9


CUBE 13
BOX 1 2 3
CYLINDER 2.3 4 56
CANDY
                                   
SPHERE 2.4                         
CYLINDER 1.23                      
CYLINDER 50 1.23
TRIANGLE 1.2 3.2    
PRISM 2.199 5
               
EOF


Current Output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
SQUARE: side = 0.00 area = 0.00 perimeter = 0.00
14.5: invalid object
344: invalid object
SQUARE: side = 0.00 area = 0.00 perimeter = 0.00
RECTANGLE: length = 0.00 width = 0.00 area = 0.00 perimeter = 0.00
14.5: invalid object
4.65: invalid object
DIMENSIONS: invalid object
CIRCLE: invalid object
14.5: invalid object
BOX: length = 0.00 width = 0.00 height = 0.00 surface area = 0.00 volume = 0.00
x: invalid object
2: invalid object
9: invalid object
CUBE: side = 0.00 surface area = 0.00 volume = 0.00
13: invalid object
BOX: length = 0.00 width = 0.00 height = 0.00 surface area = 0.00 volume = 0.00
1: invalid object
2: invalid object
3: invalid object
CYLINDER: radius = 0.00 height = 0.00 surface area = 0.00 volume = 0.00
2.3: invalid object
4: invalid object
56: invalid object
CANDY: invalid object
SPHERE: invalid object
2.4: invalid object
CYLINDER: radius = 0.00 height = 0.00 surface area = 0.00 volume = 0.00
1.23: invalid object
CYLINDER: radius = 0.00 height = 0.00 surface area = 0.00 volume = 0.00
50: invalid object
1.23: invalid object
TRIANGLE: side = 0.00 area = 0.00 perimeter = 0.00
1.2: invalid object
3.2: invalid object
PRISM: side = 0.00 height = 0.00 surface area = 0.00 volume = 0.00
2.199: invalid object
5: invalid object
EOF: invalid object


Expected Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SQUARE: side = 14.5 area = 210.25 perimeter = 58.00
SQUARE: side = 0 area = 0.00 perimeter = 0.00
RECTANGLE: length = 14.5 width = 4.65 area 67.43 perimeter = 38.30
DIMENSIONS: invalid object
CIRCLE: radius = 14.5 area = 660.52 perimeter = 91.11
BOX: length = 0 width = 2 height = 9 surface area = 36.00 volume = 0.00
CUBE: side = 13 surface area = 1014.00 volume = 2197.00
BOX: length = 1 width = 2 height = 3 surface area = 22.00 volume = 6.00
CYLINDER: radius = 2.3 height = 4 surface area = 91.04 volume = 66.48
CANDY: invalid object 
SPHERE: invalid object
CYLINDER: radius = 1.23 height = 0 surface area = 9.51 volume = 0.00
CYLINDER: radius = 50 height = 1.23 surface area = 16094.37 volume = 9660.39
TRIANGLE: side = 1.2 area = 0.62 perimeter = 3.60
PRISM: side = 2.199 height = 5 surface area = 37.17 volume = 10.47
Last edited on
It seems there is one object per line. So the loop on line 31 is wrong. You do not have a loop per token just per line. And you never set any value for length or width.

I would suggest that you make one function per object where you pass tokens. [These functions return bool for success or not and] know how to handle the parameter.
Perhaps consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
#include <map>
#include <cmath>
#include <iomanip>

using Process = void(*)(std::istream&, std::ostream&);

constexpr double sqrt3 {1.732};

void Rect(std::istream& iss, std::ostream& os)
{
	double length {}, width {};

	if (iss >> length >> width)
		os << "length = " << length << " width = " << width << " area = " << length * width << " perimeter = " << 2.0 * (length + width) << '\n';
	else
		os << "Invalid side(s)\n";
}

void Square(std::istream& iss, std::ostream& os)
{
	double side {};

	if (iss >> side)
		os << "side = " << side << " area = " << side * side << " perimeter = " << 4.0 * side << '\n';
	else
		os << "Invalid side\n";
}

void Circle(std::istream& iss, std::ostream& os)
{
	double radius {};

	if (iss >> radius)
		os << "radius = " << radius << " area = " << M_PI * radius * radius << " perimeter = " << 2 * M_PI * radius << '\n';
	else
		os << "Invalid radius\n";
}

void Triangle(std::istream& iss, std::ostream& os)
{
	double side {};

	if (iss >> side)
		os << "side = " << side << " area = " <<  sqrt3 * side * side / 4.0 << " perimeter = " << 3.0 * side << '\n';
	else
		os << "Invalid side\n";
}

void Cube(std::istream& iss, std::ostream& os)
{
	double side {};

	if (iss >> side)
		os << "side = " << side << " surface area = " << 6.0 * side * side << " volume = " << side * side * side << '\n';
	else
		os << "Invalid side\n";
}

void Box(std::istream& iss, std::ostream& os)
{
	double l {}, w {}, h {};

	if (iss >> l >> w >> h) {
		const double area {2.0 * ((l * w) + (l * h) + (w * h))};

		os << "length = " << l << " width = " << w << " height = " << " surface area = " << area << " volume is " << l * w * h << '\n';
	} else
		os << "Invalid side(s)\n";
}

void Cylinder(std::istream& iss, std::ostream& os)
{
	double radius {}, height {};

	if (iss >> radius >> height) {
		const double area {2.0 * radius * M_PI * (height + radius)};
		const double volume {M_PI * radius * radius * height};

		os << "radius = " << radius << " height = " << height << " surface area = " << area << " volume = " << volume << '\n';
	} else
		os << "Invalid dimensions\n";
}

void Prism(std::istream& iss, std::ostream& os)
{
	double length {}, height {};

	if (iss >> length >> height) {

		const double area {3.0 * (length * height) + (sqrt3 * length * length) / 2.0};
		const double volume {(sqrt3 * length * length * height) / 4.0};

		os << "length = " << length << " height = " << height << " surface area = " << area << " volume = " << volume << '\n';
	} else
		os << "Invalid sides\n";
}

const std::map<std::string, Process> shapes {{"SQUARE", Square}, {"RECTANGLE", Rect}, {"CIRCLE", Circle}, {"TRIANGLE", Triangle}, {"CUBE", Cube},
	{"BOX", Box}, {"CYLINDER", Cylinder}, {"PRISM", Prism}};

int main()
{
	std::ifstream inFile("Shapes.input.txt");
	std::ofstream outFile("Shapes.output.txt");

	if (!inFile || !outFile)
		return (std::cout << "Cannot open files\n"), 1;

	outFile << std::setprecision(2) << std::fixed;

	for (std::string line; std::getline(inFile, line); )
		if (!line.empty()) {
			std::istringstream iss(line);
			std::string shape;

			if (iss >> shape) {
				if (shape == "EOF")
					break;

				outFile << shape << ": ";

				if (const auto ret {shapes.find(shape)}; ret != shapes.end())
					ret->second(iss, outFile);
				else
					outFile << "Invalid object\n";
			}
		}
}



SQUARE: side = 14.50 area = 210.25 perimeter = 58.00
SQUARE: Invalid side
RECTANGLE: length = 14.50 width = 4.65 area = 67.43 perimeter = 38.30
DIMENSIONS: Invalid object
CIRCLE: radius = 14.50 area = 660.52 perimeter = 91.11
BOX: Invalid side(s)
CUBE: side = 13.00 surface area = 1014.00 volume = 2197.00
BOX: length = 1.00 width = 2.00 height =  surface area = 22.00 volume is 6.00
CYLINDER: radius = 2.30 height = 4.00 surface area = 91.04 volume = 66.48
CANDY: Invalid object
SPHERE: Invalid object
CYLINDER: Invalid dimensions
CYLINDER: radius = 50.00 height = 1.23 surface area = 16094.38 volume = 9660.40
TRIANGLE: side = 1.20 area = 0.62 perimeter = 3.60
PRISM: length = 2.20 height = 5.00 surface area = 37.17 volume = 10.47

Hello Orion98,

I have always believed that knowing what is wrong with you code helps.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>  // <--- You did not include this file.
#include <iomanip>
//#include <limits>  // <--- Used with "std::cin.ignore(...)". "std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');"
#include <string>
#include <vector>
//#include <cctype>  // <--- For "std::tolower() and std::toupper()" + others.
//#include <corecrt_math_defines.h>  // <--- For "M_PI" and other predefined values of PI. Plus other values.

#include <fstream>
#include <iterator>
#include <sstream>

#define PI 3.14159
//constexpr double PI{ 3.14159 };

using namespace std;

vector<string> parseString(string);

int main()
{
    ifstream inFile("Shapes.input.txt");
    ofstream outFile("Shapes.output.txt");
    //inFile.open("Shapes.input.txt");
    //outFile.open("Shapes.output.txt");

    // <--- How do you know that the files are open and usable?

    std::cout << std::fixed << std::showpoint << std::setprecision(3);  // <--- "showpoint" is optional if "setprecision" is > 0.
    outFile << std::fixed << std::showpoint << std::setprecision(3);

    //cout.precision(2);
    //cout << fixed;
    //outFile.precision(2);
    //outFile << fixed;

    while (!inFile.eof())  // <--- Does not work the way that you are thinking. Does not catch "eof" when it should.
    {
        string s;

        getline(inFile, s);

        vector<string> tokens = parseString(s);

        float length = 0, width = 0, height = 0, area, peri, volume;

For the most part I tend to follow the following quote. I also found that the alphabetical order helps to remind you if you miss a header file.

seeplus once wrote:

any required #define statements
std:: headers (eg iostream)
c headers (eg conio.h)
windows headers (eg windows.h)
3rd party headers
user headers


Lines 3 and 6 are commented because I do not know yet if the would be useful at this point.

Line 7 is a suggestion that you may not know about. Its use is up to you.

Although line 13 works line 14 is the updated way of defining this.

Lines 22 - 25 can be combined into 1 line for each and is the more preferred method of defining a file stream variable and opening the file at the same time. The ".open(...)" can be used when you close the file stream and then need to reopen it.

Doing something for line 27 is a must. Especially for the input stream.

Lines 29 and 30 does the same as lines 32 - 35.

On line 45 you define the variables as "float"s. You should prefer to use "double" over "float".

For the line peri = 4.0 * length;. The "4.0" is considered a "double" and thus the "length" variable will be promoted to a "double" B4 the calculation. Then the problem is you are trying to store a "double" in a "float" which has less decimal places available, so there will be data loss when the end of the "double" is cut off to fit into the "float". This could impact future calculations and produce a result that you are not expecting.

That is a start until I have a chance to look over the rest of the program, but that should help.

Andy
You may not have learned everything below yet, but here's a way to make this generic. I got it working for a square and a box. Now all you have to do is create the classes for the other shapes and main() will deal with them. This method ensures perfectly consistent output and it that adding a shape is pretty easy.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include <string>
#include <vector>
#include <iomanip>
#include <iostream>
#include <limits>

using namespace std;

constexpr double PI = 3.141592653989792;

class Shape {
public:
    // The name of the shape as it appears in the input
    virtual string name() = 0;

    // Is it a 2D shape? False means it's 3D
    virtual bool is2D() = 0;

    // Read the shape info from the input. Note that the info does NOT
    // include the name. Main() reads the name
    virtual bool parse(istream &) = 0;

    // Describe the shape. This is the first part of the output line.
    virtual void describe(ostream &) = 0;
};

// A 2D shape includes methods to compute the area and perimeter.
class TwoDShape : public Shape {
public:
    bool is2D() { return true; };
    virtual double area() = 0;
    virtual double perimeter() = 0;
};

// A 3D shape includes methods to compute the surface area and volume.
class ThreeDShape : public Shape {
public:
    bool is2D() { return false; };
    virtual double surfaceArea() = 0;
    virtual double volume() = 0;
};


/////////////////////////////////////////////////////////////////////
//                       TWO DIMENSIONAL SHAPES                    //
// After defining a new shape, add a line in main() to push an     //
// instance onto the "shapes" vector                               //
/////////////////////////////////////////////////////////////////////

class Square : public TwoDShape {
    double side;
public:
    virtual string name() { return "SQUARE"; }
    virtual double area() { return side * side; }
    virtual double perimeter() { return 4*side; }
    virtual bool parse(istream &is) {
	side = 0.0;
	is >> side;
	return bool(is);
    }
    virtual void describe(ostream &os) {
	os << name() << ": side = " << side;
    }
};
    
/////////////////////////////////////////////////////////////////////
//                       THREE DIMENSIONAL SHAPES                  //
// After defining a new shape, add a line in main() to push an     //
// instance onto the "shapes" vector                               //
/////////////////////////////////////////////////////////////////////

class Box : public ThreeDShape {
    double len, width, height;
public:
    virtual string name() { return "BOX"; }
    virtual double surfaceArea() { return 2 * (len * width +
					       len * height +
					       width * height); }
    virtual double volume() { return len * height * width; }
    virtual bool parse(istream &is) {
	len = width = height = 0;
	is >> len >> width >> height;
	return bool(is);
    }
    virtual void describe(ostream &os) {
	os << name() << ": length = " << len
	   << " width = " << width
	   << " height = " << height;
    }
};
    
int main()
{
    // This version uses cin/cout for the I/O
    cout.precision(2);
    cout << fixed;
    cout.precision(2);
    cout << fixed;

    // A vector of pointers to each of the different shapes.
    vector<unique_ptr<Shape>> shapes;

    // Add an instance of each shape to the vector
    shapes.push_back(unique_ptr<Shape>(new Square));
    shapes.push_back(unique_ptr<Shape>(new Box));
    
    string name;

    // Loop through each input line
    while(cin >> name) {	// read the name of the shape

	// EOF is special. It means end of the file
	if (name == "EOF") break;
	
	bool found = false;	// indicates whether we found the shape name

	// Go through the known shapes look for the name. If you find it then
	// parse the rest of the line and print the shape's info
	for (auto & shape : shapes) {

	    if (shape->name() == name) {
		// Found it. Now parse and print the description
		shape->parse(cin);
		shape->describe(cout);

		// Two-D shapes print their area and perimeter. 3D shapes
		// print their surface area and volume.
		if (shape->is2D()) {
		    TwoDShape *p = static_cast<TwoDShape*>(shape.get());
		    cout << " area = " << p->area()
			 << ",  perimeter = " << p->perimeter()
			 << '\n';
		} else {
		    ThreeDShape *p = static_cast<ThreeDShape*>(shape.get());
		    cout << ", surface area = " << p->surfaceArea()
			 << ", volume = " << p->volume() << '\n';
		}
		found = true;
		break;
	    }
	}

	if (!found) {
	    cout << name << ": illegal shape\n";
	}
	// Clear any error and skip past the end of line
	cin.clear();
	cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
    }
}

Hello Orion98,

I have been working on your program for awhile.
coder777 wrote:

So the loop on line 31 is wrong.


I agree and for 2 reasons:
First the ".size()" function returns a "size_t" type variable. If you accept 1 guarantee that would be that "size_t" is an "unsigned" type. After that the actual type is implementation defined. So a "size_t" could be and "unsigned int" or "unsigned long" or maybe some other type. It all depends on your header files and compiler.

Second after working with it for awhile I realized that the for loop is not necessary as the following code and output will show.

Trying to keep with what you have done, for the most part, I came up with this. For now some of the original code I have left. Mostly as an idea of what is wrong. Other parts I have changed or added.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
int main()
{
    ifstream inFile("Shapes.input.txt");
    ofstream outFile("Shapes.output.txt");
    //inFile.open("Shapes.input.txt");
    //outFile.open("Shapes.output.txt");

    // <--- How do you know that the files are open and usable?

    std::cout << std::fixed << std::showpoint << std::setprecision(3);  // <--- "showpoint" is optional if "setprecision" is > 0.
    outFile << std::fixed << std::showpoint << std::setprecision(3);

    //cout.precision(2);
    //cout << fixed;
    //outFile.precision(2);
    //outFile << fixed;

    string s;  // <--- Had to move up to here, so the while loop would work.
    
    //while (!inFile.eof())  // <--- Does not work the way that you are thinking. Does not catch "eof" when it should.
    while (std::getline(inFile, s))
    {
        //getline(inFile, s);
        if (s == "")  // <--- Accounts for blank lines in the input file.
            continue;

        vector<string> tokens = parseString(s);

        constexpr size_t MIN_VEC_SIZE{ 2 };  // <--- Added.
        double length{}, width{}, height{}, area{}, peri{}, volume{};  // <--- Changed.
        const enum { OBJECT, LENGTH, WIDTH, HEIGHT };  // <--- Added.

        for (size_t i = 0; i < tokens.size(); i++)
        {
            // cout << "token["<< i <<"] = " << tokens[i] << '\n';

            if (tokens[OBJECT] == "SQUARE")  // <--- This does not work when "i" becomes 1. Same for the "else if"s.
            {
                if (tokens.size() < MIN_VEC_SIZE)
                {
                    std::cout << "\n     No length given. Set to (1).\n\n";  // <--- Just thought of this. Optional at your discretion.
                    length = 1.0;
                }
                else
                {
                    try
                    {
                        length = stod(tokens[LENGTH]);
                    }
                    catch (const std::exception& e)
                    {
                        std::cerr <<
                            "\n     " << tokens[OBJECT] <<
                            ":  " << e.what() << ". For length.";

                        std::cerr << "\n";  // <--- Used as a break point, but should be at the end of the above line.

                        length = 1.0;
                    }
                }

                area = length * length;

                peri = 4.0 * length;

                cout
                    << tokens[OBJECT] << ":\n side = " << length
                    << "\n area = " << area
                    << "\n perimeter = "
                    << peri << "\n\n";

                outFile << tokens[OBJECT] << ":\n side = " << length << "\n area = " << area
                    << "\n perimeter = " << peri << '\n';

                break;
            }

Line 37 is the first example why the for loop does not work.

Given the input line of SQUARE 14.5 344. First time through the loop "i" is (0) zero and everything is fine. You are comparing "SQUARE" to "SQUARE". 2nd time through the loop "i" is (1) and you are comparing "14.5" to "SQUARE". This will bypass everything until you reach the else at the end and display
cout << tokens[i] << ": invalid object" << "\n\n";.

Notice the use of the new line (\n)s over the "endl"s. Try to avoid using the "endl"s as this is a function that takes time to process.

Line 39 is to determine if the vector is large enough to hold a number that can be changed from a "string" to a "double". If not it sets "length" to (1) for the calculations.

I do not use the "try/catch" much, but with function like "sto?" it is very useful in preventing a runtime crash of the program. The "catch" part allows you to deal with the problem and then continue with the program.

As coder777 pointed out your if/else if statements never have a value for the variables "length", "width" and "height". This is a way of giving these variables a proper value to use.

B4 I forget I should mention it is always a good idea to initialize ALL your variable when defined. It saves on headaches later.

Not sure if you have studied "enum"s yet, but it is a way to give a number a name to help the code be more readable and understandable. The above code is 1 example of this.

The "break" statement on line 75 leaves the for loop when you are finished and ends up back at the while condition. Having to add this line is why the for loop is not needed.

This is part of the output I have managed so far:

SQUARE:
 side = 14.500
 area = 210.250
 perimeter = 58.000

SQUARE:
 side = 1.000
 area = 1.000
 perimeter = 4.000

RECTANGLE:
 length = 14.500
 width = 4.650
 area = 67.425
 perimeter = 38.300

14.5: invalid object

4.65: invalid object

DIMENSIONS: invalid object


In the first Square you are using the numbers from the file. In the 2nd Square it is using (1) because there is nothing from the input file to use.

For the Rectangle the "14.5: invalid object" is what happens when the for loop is allowed to continue. I missed the "break" for this the first time around.

The last line is what you should get when the "object" type does not match anything in the "if/else if" statements.

Another part I noticed is in the input file you have "CIRCLE", but your else if is:
else if (tokens[OBJECT] == "Circle"). Only the "C" is a match. After that it fails. Be careful of how you spell words. Case usually matters.

The only other object type I worked on is "BOX".
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
else if (tokens[i] == "BOX")
{
    try
    {
        length = std::stod(tokens[1]);
    }
    catch (const std::exception& e)
    {
        std::cerr <<
            "\n     " << tokens[0] <<
            ":  " << e.what() << ". For length.";

        std::cerr << "\n\n";

        length = 0;
    }

    try
    {
        width = std::stod(tokens[2]);
    }
    catch (const std::exception& e)
    {
        std::cerr <<
            "\n     " << tokens[0] <<
            ":  " << e.what() << ". For width.";

        std::cerr << "\n";

        width = 0;
    }

    try
    {
        height = std::stod(tokens[3]);
    }
    catch (const std::exception& e)
    {
        std::cerr <<
            "\n     " << tokens[0] <<
            ":  " << e.what() << ". For height.";

        std::cerr << "\n";

        height = 0;
    }

    //area = 2.0 * (length * width) + (length * height) + (width * height);
    area = (2.0 * (length * width)) + (2.0 * (length * height)) + (2.0 * (width * height));

I am not sure what you were thinking for line 48, but it does not work. Line 49 is the way I interpret the the formula that I found, which is SA = 2lw + 2lh + 2hw which seems to be correct.

I used 3 try/catch here, but you could combine it all into just 1 if you want, but that would be more difficult to figure out which variable is the problem.

It would be easy to remove the for loop and the break statements.

That should give you something to think about.

Andy
Hello Orion98,

Working on yur program I managed to get this output:

SQUARE:
 side = 14.500
 area = 210.250
 perimeter = 58.000

SQUARE:
 side = 1.000
 area = 1.000
 perimeter = 4.000

RECTANGLE:
 length = 14.500
 width = 4.650
 area = 67.425
 perimeter = 38.300

DIMENSIONS: invalid object

CIRCLE:
 radius = 14.500
 area = 660.519
 perimeter = 91.106


     BOX:  invalid stod argument for length. set to 1

BOX:
 length = 1.000
 width = 2.000
 height = 9.000
 surface area = 58.000
 volume = 18.000

CUBE:
 side = 13.000
 surface area = 1014.000
 volume = 2197.000

BOX:
 length = 1.000
 width = 2.000
 height = 3.000
 surface area = 22.000
 volume = 6.000

CYLINDER:
 radius = 2.300
 height = 4.000
 surface area = 91.043
 volume = 66.476

CANDY: invalid object

SPHERE: invalid object


     A cylinder requires 2 values. None or not enough provided.
     Setting radius and height to 1.

CYLINDER:
 radius = 1.000
 height = 1.000
 surface area = 12.566
 volume = 3.142

CYLINDER:
 radius = 50.000
 height = 1.230
 surface area = 16094.366
 volume = 9660.389


     A triangle needs 3 values to work with. Less than 3 are provides


   Not yet finished.

EOF: invalid object


Also I found the input file has some problems.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SQUARE 14.5 344         
SQUARE                             
RECTANGLE 14.5    4.65           
DIMENSIONS
CIRCLE 14.5

BOX x 2 9


CUBE 13
BOX 1 2 3
CYLINDER 2.3 4 56
CANDY
                                   
SPHERE 2.4                         
CYLINDER 1.23                      
CYLINDER 50 1.23
TRIANGLE 1.2 3.2    
PRISM 2.199 5
               
EOF

Lines 1, 2, 3, 15, 16 and 18 have extra spaces at the end.

Line 3 has extra space between the numbers, but not a problem that I noticed.

Line 14 is a line of spaces and causes the program to crash when running.

Lines 20 and 21 are unnecessary unless they are required. Even knowing of these problems, like line 14, that will be extra work to account for this.

Andy
Also I found the input file has some problems.


Yes. So?? Input data can have issues. You try to handle them. See my code above which processes this data file and produces a valid output file.
Here's another version. This one uses virtual methods to print so main() is simpler:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#include <string>
#include <vector>
#include <iomanip>
#include <iostream>
#include <limits>

using namespace std;

constexpr double PI = 3.141592653989792;

class Shape
{
  public:
    // The name of the shape as it appears in the input
    virtual string name() = 0;

    // Read the shape info from the input. Note that the info does NOT
    // include the name. Main() reads the name
    virtual bool parse(istream &) = 0;

    // Print the shape. The base class prints the first part of the
    // output line.
    virtual void print(ostream &) = 0;
};

ostream &operator <<(ostream &os, Shape &s)
{
    s.print(os);
    return os;
}

// A 2D shape includes methods to compute the area and perimeter.
class TwoDShape:public Shape
{
  public:
    virtual double area() = 0;
    virtual double perimeter() = 0;
    void print(ostream &os) {
	os << "area = " << area()
	   << ", perimeter = " << perimeter();
    }
};

// A 3D shape includes methods to compute the surface area and volume.
class ThreeDShape:public Shape
{
  public:
    virtual double surfaceArea() = 0;
    virtual double volume() = 0;
    void print(ostream &os) {
	os << "surface area = " << surfaceArea()
	   << ", volume = " << volume();
    }
};

/////////////////////////////////////////////////////////////////////
//                       TWO DIMENSIONAL SHAPES                    //
// After defining a new shape, add a line in main() to push an     //
// instance onto the "shapes" vector                               //
/////////////////////////////////////////////////////////////////////

class Square:public TwoDShape
{
    double side;
  public:
      virtual string name()    { return "SQUARE"; }
    virtual double area()      { return side * side; }
    virtual double perimeter() { return 4 * side; }
    virtual bool parse(istream & is)
    {
	side = 0.0;
	is >> side;
	return bool(is);
    }
    virtual void print(ostream & os)
    {
	os << name() << ": side = " << side << ", ";
	TwoDShape::print(os);
    }
};

/////////////////////////////////////////////////////////////////////
//                       THREE DIMENSIONAL SHAPES                  //
// After defining a new shape, add a line in main() to push an     //
// instance onto the "shapes" vector                               //
/////////////////////////////////////////////////////////////////////

class Box:public ThreeDShape
{
    double len, width, height;
  public:
      virtual string name()    { return "BOX"; }
    virtual double surfaceArea()
    {
	return 2 * (len * width + len * height + width * height);
    }
    virtual double volume()    { return len * height * width; }
    virtual bool parse(istream & is)
    {
	len = width = height = 0;
	is >> len >> width >> height;
	return bool(is);
    }
    virtual void print(ostream & os)
    {
	os << name()
	   << ": length = " << len
	   << ", width = " << width
	   << ", height = " << height << ", ";
	ThreeDShape::print(os);
    }
};

int
main()
{
    // This version uses cin/cout for the I/O
    cout.precision(2);
    cout << fixed;
    cout.precision(2);
    cout << fixed;

    // A vector of pointers to each of the different shapes.
    vector < unique_ptr < Shape >> shapes;

    // Add an instance of each shape to the vector
    shapes.push_back(unique_ptr < Shape > (new Square));
    shapes.push_back(unique_ptr < Shape > (new Box));

    string name;

    // Loop through each input line
    while (cin >> name) {			 // read the name of the shape

	// EOF is special. It means end of the file
	if (name == "EOF")
	    break;

	bool found = false;	// indicates whether we found the shape name

	// Go through the known shapes look for the name. If you find it then
	// parse the rest of the line and print the shape's info
      for (auto & shape:shapes) {

	    if (shape->name() == name) {
		// Found it. Now parse and print it
		shape->parse(cin);
		cout << *shape << '\n';
		found = true;
		break;
	    }
	}

	if (!found) {
	    cout << name << ": illegal shape\n";
	}
	// Clear any error and skip past the end of line
	cin.clear();
	cin.ignore(std::numeric_limits < streamsize >::max(), '\n');
    }
}

SQUARE: side = 14.50, area = 210.25, perimeter = 58.00
SQUARE: side = 0.00, area = 0.00, perimeter = 0.00
DIMENSIONS: illegal shape
CIRCLE: illegal shape
BOX: length = 0.00, width = 0.00, height = 0.00, surface area = 0.00, volume = 0.00
CUBE: illegal shape
BOX: length = 1.00, width = 2.00, height = 3.00, surface area = 22.00, volume = 6.00
CYLINDER: illegal shape
CANDY: illegal shape
SPHERE: illegal shape
CYLINDER: illegal shape
CYLINDER: illegal shape
TRIANGLE: illegal shape
PRISM: illegal shape

Topic archived. No new replies allowed.