PPP2 Chapter 10 Reading example code operator<<

Pages: 123
There's a Year vector, but it's not being passed to print_year(). That function only has access to one Year object rather than a Year vector.

This is far as I can get it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void print_year(ostream &os, const Year &y)
{
	constexpr double abs_zero = -459.67;
	for (size_t i = 0; i < y.month.size(); ++i)
	{
		try
		{
			if (y.month[i].day[i].hour[i] <= abs_zero)
			{
				error("bad temperature reading");
			}
			os << '{' << "Year: " << y.year
				<< " Month: " << int_to_month(y.month[i].month)
				<< " Temperature: " << y.month[i].day[i].hour[i] << "}\n";
		}
		catch (const runtime_error &e)
		{
			cerr << "error: " << e.what() << '\n';
		}
	}
}


Until I can get at least this to work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Month::operator int() const
{
	auto days = this->day;
	int day = 0;
	for (size_t i = 0; i < days.size(); ++i)
	{
		day = days[i];
	}
	return day;
}

Day::operator int() const
{
	
}


I wonder if it's possible to initialize a Reading object in print_year() to have the same values as the Year object's values (e.g. if r is a Reading object and y is a Year object, r.temperature == y.month[i].day[i].hour[i]).

This is what the program's main() function looks like:
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
int main()
{
	try
	{
		// open an input file:
		cout << "Please enter input file name\n";
		string iname;
		cin >> iname;
		ifstream ist{ iname };
		if (!ist)
		{
			error("can't open input file ", iname);
		}

		ist.exceptions(ist.exceptions() | ios_base::badbit);

		// open an output file:
		cout << "Please enter output file name\n";
		string oname;
		cin >> oname;
		ofstream ost{ oname };
		if (!ost)
		{
			error("can't open output file ", oname);
		}

		// read an arbitrary number of years:
		vector<Year> ys;
		while (true)
		{
			// get a freshly initialized Year each time around
			Year y;
			if (!(ist >> y))
			{
				break;
			}
			ys.push_back(y);
		}
		cout << "read " << ys.size() << " years of readings\n";

		for (const Year &y : ys)
		{
			print_year(ost, y);
		}
	}
	catch (const runtime_error &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 1;
	}
	catch (const exception &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 2;
	}
	keep_window_open();
	return 0;
}


I can't pass that Year vector, ys, to the printing function because it takes one Year object as its second argument and it's being used in a for-each loop to print out years of readings.
Last edited on
I can't pass that Year vector, ys, to the printing function because it takes one Year object as its second argument and it's being used in a for-each loop to print out years of readings.

So? You're iterating over your years vector in main which is what you want. Now in your print function you need to iterate trough the months, days, and hours of that year.

This is far as I can get it:

Why? And by the way that "as far as you can get" is wrong. You are only iterating over the mounts, but not the days or hours.

Until I can get at least this to work:

I have no idea what you're trying to do there, but I doubt that it is necessary.

Start with something like:

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
void print_hours(ostream &os, const Day& day)
{
    // Iterate through the hours.
    for(size_t hour_index = 0; hour_index < day[day_index].size(); ++day_index)
    {
        // Print the hour of the day.
        //  Print the temperature reading.
    }
    return os;
}

void print_day(ostream &os, const Month& month)
{

    // Iterate through the days of the current month, remember days start at 1 not zero.
    for(size_t day_index = 1; day_index < month.day.size(); ++day_index)
    {
        // Print the day of the current month.
        print_hours(os, day[day_index];
    }
    return os;
}


void print_year(ostream &os, const Year &year)
{
    //  Iterate through the months of the current year.
    for(size_t month_index = 0; month_index < year.month.size(); ++month_index)
    {
        // Print the month of the current year.
        print_month(os, month[month_index]);

    }
    return os;
}


You should get a lot of output but this should show you what is in your various vectors. Once you know that your vectors have the correct information you can reducing the data. Note: I didn't try the code but it should get you going in the correct direction.



Last edited on
I've seen that trying to print the hours really just prints the temperatures. And the program currently doesn't know how to print a Day, so if I try it gives me compiler error.

Look at this:
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
struct Day
{
	vector<double> hour{ vector<double>(24, not_a_reading) };
	operator int() const;
};

// a month of temperature readings
struct Month
{
	// [0:11] January is 0
	int month{ not_a_month };

	// [1:31] one vector of readings per day
	vector<Day> day{ 32 };

	operator int() const;
};

// a year of temperature readings, organized by month
struct Year
{
	// positive == A.D.
	int year;

	// [0:11] January is 0
	vector<Month> month{ 12 };
};

struct Reading
{
	int day;
	int hour;
	double temperature;
};


If I want to print hours, I have to tell C++ how to print an hour. If I want to print Days, I first have to tell C++ how to print a Day. Right now it'll give me a compiler error if I try it. Do you think there's a way I can do it without defining an operator<< for Days and hours?

This is the whole program right now:
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
// temp_readings.cpp : Defines the entry point for the console application.
// Osman Zakir
// 2 / 7 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 10 Sections 10.1.1 to 10.1.3
// This program takes temperature readings from an input file and prints it to
// output file.  The readings are represented in the form of readings for the
// year, month and hour.  Structs are used to represent Readings, Days, and Months.

#include "../../std_lib_facilities.h"

// not_a_reading == less than absolute zero
const int not_a_reading = -7777;

const int not_a_month = -1;

struct Day
{
	vector<double> hour{ vector<double>(24, not_a_reading) };
};

// a month of temperature readings
struct Month
{
	// [0:11] January is 0
	int month{ not_a_month };

	// [1:31] one vector of readings per day
	vector<Day> day{ 32 };
};

// a year of temperature readings, organized by month
struct Year
{
	// positive == A.D.
	int year;

	// [0:11] January is 0
	vector<Month> month{ 12 };
};

struct Reading
{
	int day;
	int hour;
	double temperature;
};

istream& operator>>(istream &is, Reading &r);
istream& operator>>(istream &is, Month &m);
istream& operator>>(istream &is, Year &y);
string int_to_month(int i);
int month_to_int(const string &s);
bool is_valid(const Reading &r);
void end_of_loop(istream &is, char term, const string &message);
void print_year(ostream &os, const Year &y);

int main()
{
	try
	{
		// open an input file:
		cout << "Please enter input file name\n";
		string iname;
		cin >> iname;
		ifstream ist{ iname };
		if (!ist)
		{
			error("can't open input file ", iname);
		}

		ist.exceptions(ist.exceptions() | ios_base::badbit);

		// open an output file:
		cout << "Please enter output file name\n";
		string oname;
		cin >> oname;
		ofstream ost{ oname };
		if (!ost)
		{
			error("can't open output file ", oname);
		}

		// read an arbitrary number of years:
		vector<Year> ys;
		while (true)
		{
			// get a freshly initialized Year each time around
			Year y;
			if (!(ist >> y))
			{
				break;
			}
			ys.push_back(y);
		}
		cout << "read " << ys.size() << " years of readings\n";

		for (const Year &y : ys)
		{
			print_year(ost, y);
		}
	}
	catch (const runtime_error &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 1;
	}
	catch (const exception &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 2;
	}
	keep_window_open();
	return 0;
}

istream& operator>>(istream &is, Reading &r)
// read a temperature reading from is into r
// format: ( 3 4 9.7 )
// check format, but don’t bother with data validity
{
	char ch1;
	if (is >> ch1 && ch1 != '(') 
	// could it be a Reading?
	{ 
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}
	char ch2;
	int d;
	int h;
	double t;
	is >> d >> h >> t >> ch2;
	if (!is || ch2 != ')')
	{
		// messed-up reading
		error("bad reading"); 
	}
	r.day = d;
	r.hour = h;
	r.temperature = t;
	return is;
}

istream& operator>>(istream &is, Month &m)
// read a month from is into m
// format: { month feb . . . }
{
	char ch = 0;
	if (is >> ch && ch != '{')
	{
		is.unget();

		// we failed to read a Month
		is.clear(ios_base::failbit);
		return is;
	}

	string month_marker;
	string mm;
	is >> month_marker >> mm;
	if (!is || month_marker != "month")
	{
		error("bad start of month");
	}
	m.month = month_to_int(mm);

	int duplicates = 0;
	int invalids = 0;
	for (Reading r; is >> r;)
	{
		if (is_valid(r))
		{
			if (m.day[r.day].hour[r.hour] != not_a_reading)
			{
				++duplicates;
			}
			m.day[r.day].hour[r.hour] = r.temperature;
		}
		else
		{
			++invalids;
		}
	}
	if (invalids)
	{
		error("invalid readings in month", invalids);
	}
	if (duplicates)
	{
		error("duplicate readings in month", duplicates);
	}
	end_of_loop(is, '}', "bad end of month");
	return is;
}

istream& operator>>(istream &is, Year &y)
// read a year from is into y
// format: { year 1972 . . . }
{
	char ch;
	is >> ch;
	if (ch != '{')
	{
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}

	string year_marker;
	int yy;
	is >> year_marker >> yy;
	if (!is || year_marker != "year")
	{
		error("bad start of year");
	}
	y.year = yy;

	while (true)
	{
		// get a clean m each time around
		Month m;
		if (!(is >> m))
		{
			break;
		}
		y.month[m.month] = m;
	}

	end_of_loop(is, '}', "bad end of year");
	return is;
}

int month_to_int(const string &s)
// is s the name of a month? If so return its index [0:11] otherwise – 1
{
	vector<string> month_input_tbl =
	{"jan", "feb", "mar", "apr", "may", "jun", 
		"jul", "aug", "sep", "oct", "nov", "dec"};

	for (int i = 0; i < 12; ++i)
	{
		if (month_input_tbl[i] == s)
		{
			return i;
		}
	}
	return -1;
}

string int_to_month(int i)
// months [0:11]
{
	vector<string> month_print_tbl =
	{ "January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December" };

	if (i < 0 || i >= 12)
	{
		cout << i << ":\n";
		error("bad month index");
	}
	return month_print_tbl[i];
}

bool is_valid(const Reading &r)
// a rough test
{
	constexpr int implausible_min = -200;
	constexpr int implausible_max = 200;

	if (r.day < 1 || r.day > 31)
	{
		return false;
	}
	if (r.hour < 0 || r.hour > 23)
	{
		return false;
	}
	if (r.temperature < implausible_min || r.temperature > implausible_max)
	{
		return false;
	}
	return true;
}

void end_of_loop(istream &ist, char term, const string &message)
{
	// use term as terminator and/or separator
	if (ist.fail())
	{
		ist.clear();
		char ch;
		if (ist >> ch && ch == term)
		{
			// all is fine
			return;
		}
		error(message);
	}
}

void print_year(ostream &os, const Year &y)
{
	constexpr double abs_zero = -459.67;
	for (size_t i = 0; i < y.month.size(); ++i)
	{
		try
		{
			if (y.month[i].day[i].hour[i] <= abs_zero)
			{
				error("bad temperature reading");
			}
			os << '{' << "Year: " << y.year
				<< " Month: " << int_to_month(y.month[i].month)
				<< " Temperature: " << y.month[i].day[i].hour[i] << "}\n";
		}
		catch (const runtime_error &e)
		{
			cerr << "error: " << e.what() << '\n';
		}
	}
}
Last edited on
If I want to print hours, I have to tell C++ how to print an hour. If I want to print Days, I first have to tell C++ how to print a Day.

Why are you trying to print a "Day"? A "Day" contains a vector<double>, you need to print the individual elements of that vector, not a "Day".

Do you think there's a way I can do it without defining an operator<< for Days and hours?

Yes! Did you even study the code I provided? What exactly don't you understand?

You don't seem to understand that you can access an individual hour by using the proper "array notation". For example, let's consider that your file has a entry for the year 2015 ( { year 2015 } ). After you read the file with just that one entry you can now access hour 16 of day 20 of the month June like: "year[0].month[5].day[20].hour[15]"

You use year[0] because 2015 is the only year in the years vector.

You use month[5] because months start at zero for January, so June would be 5.

You use day[20] because days start at one. Every month will have a vector of size 32 ( 0 - 31), the first element (0) is actually used but it is still there.

You use hour[15] because hours start at zero and stop at element 23.

If you try to print that month, day, and hour you should end up with your "default" values because no actual month, day, or hour values were entered.



There is no Year vector. Didn't I already say that? There is certainly one in main, but print_year() doesn't have access to it.

I can't do year[0] or anything, I can only do y.year << y.month[i].day[i].hour[i], which is exactly what I'm doing here:
1
2
3
os << '{' << "Year: " << y.year
				<< " Month: " << int_to_month(y.month[i].month)
				<< " Temperature: " << y.month[i].day[i].hour[i] << "}\n";


Do you not see that? There is no Year vector anywhere, so forget about that. I only have y.year for that. Then y.month[5].day[20].hour[15] would just directly print whatever temperature is stored there. y.month[5].day[20].hour[15] isn't going to give me the 15th hour of the 20th day of the 5th month, it's only going to give me the temperature stored for the 15th hour of the 20th day of the 5th month. That's what I've been trying to tell you.

The output file from doing that is like this:

{Year: 1992 Month: February Temperature: 64}
{Year: 2000 Month: February Temperature: 68}


And again, I've already tried to do
 
os << y.month[i].day[i];


It doesn't work. I keep getting a compiler error because the computer doesn't know how to print a day. And this
 
os << y.month[i].day[i].hour;


Also wouldn't work because hour is a vector and Day doesn't have an "hour" member that isn't a vector. And the hour vector stores temperature readings for each hour.
Last edited on
Notice that "year[0].month[5].day[20].hour[15]" is exactly what I'm doing here:


Yes you are indeed printing one hour for one day for each month. But what you're not doing is iterating over every day and every hour for every month.

The output file from doing that is like this:

What happened to the rest of the readings? February 2000 has three readings.
"{year 2000
{ month feb (1 1 68 ) (2 3 66.66 ) ( 1 0 67.2)}"
Day 1 hour 0, Day 1 hour 1, and Day 2 hour 3.

Look at your code:
1
2
3
4
5
	for (size_t i = 0; i < y.month.size(); ++i)
	{
			os << '{' << "Year: " << y.year
				<< " Month: " << int_to_month(y.month[i].month)
				<< " Temperature: " << y.month[i].day[i].hour[i] << "}\n";

Do you see all of those [i]? That is the problem. for every month ([i]) you need to print every day ([1] to [31]) and every hour ([0] to [23]).

So with your code for month[0], you try to print only day[0] (which is an invalid value because [0] is not used. And you try to print only hour[0], which is also invalid.

For month[1] you try to print only day[1] and only hour[1].

For month[2] you try to print only day[2] and only hour[2].

And so on.

But what you need to do is for each month print every day and every hour of every day.


There is no Year vector anywhere, so forget about that.

Yes there is a vector<Year> call years in main, so no you can't forget about that!


Again did you even study and try to implement the code I supplied a couple of posts back?

With the supplied input file:
1
2
3
4
5
6
7
{ year 1990 }
{year 1991 { month jun }}
{ year 1992 { month jan ( 1 0 61.5) }  {month feb (1 1 64) (2 2 65.2) } }
{year 2000
	{ month feb (1 1 68 ) (2 3 66.66 ) ( 1 0 67.2)}
	{month dec (15 15 -9.2 ) (15 14 -8.8) (14 0 -2) }
}


You should have an output of something like:

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
read 4 years of readings
1990 
1991 
    June
1992 
    January
        1
            0:00 61.5 F
    February
        1
            1:00 64 F
        2
            2:00 65.2 F
2000 
    February
        1
            0:00 67.2 F
            1:00 68 F
        2
            3:00 66.66 F
    December
        14
            0:00 -2 F
        15
            14:00 -8.8 F
            15:00 -9.2 F



The way that the author himself calls print_year() in main() is
1
2
3
4
for (const Year &y : ys)
{
    print_year(ofs, y);
}


Which means that y, which is not a vector of Years, is what is to be passed to the function. So the function doesn't have access to a vector of Years. Am I wrong? If I'm not, then that means I have no way to get at the Years vector.

And your code from a couple of posts back is flawed. Your trying to return something from a void function.

Edit: Anyway, I changed i in print_year()'s loop to start at 1 instead of 0.

I'll post the whole thing again. This time, please notice what I've been trying to get you notice for so long now: the print_year(), as per the book itself even, and also as it's called in main(), doesn't take in a Years vector as an argument at all. It only takes an ostream object and a single Year object.
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
// temp_readings.cpp : Defines the entry point for the console application.
// Osman Zakir
// 2 / 7 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 10 Sections 10.1.1 to 10.1.3
// This program takes temperature readings from an input file and prints it to
// output file.  The readings are represented in the form of readings for the
// year, month and hour.  Structs are used to represent Readings, Days, and Months.

#include "../../std_lib_facilities.h"

// not_a_reading == less than absolute zero
const int not_a_reading = -7777;

const int not_a_month = -1;

struct Day
{
	vector<double> hour{ vector<double>(24, not_a_reading) };
};

// a month of temperature readings
struct Month
{
	// [0:11] January is 0
	int month{ not_a_month };

	// [1:31] one vector of readings per day
	vector<Day> day{ 32 };
};

// a year of temperature readings, organized by month
struct Year
{
	// positive == A.D.
	int year;

	// [0:11] January is 0
	vector<Month> month{ 12 };
};

struct Reading
{
	int day;
	int hour;
	double temperature;
};

istream& operator>>(istream &is, Reading &r);
istream& operator>>(istream &is, Month &m);
istream& operator>>(istream &is, Year &y);
string int_to_month(int i);
int month_to_int(const string &s);
bool is_valid(const Reading &r);
void end_of_loop(istream &is, char term, const string &message);
void print_year(ostream &os, const Year &y);

int main()
{
	try
	{
		// open an input file:
		cout << "Please enter input file name\n";
		string iname;
		cin >> iname;
		ifstream ist{ iname };
		if (!ist)
		{
			error("can't open input file ", iname);
		}

		ist.exceptions(ist.exceptions() | ios_base::badbit);

		// open an output file:
		cout << "Please enter output file name\n";
		string oname;
		cin >> oname;
		ofstream ost{ oname };
		if (!ost)
		{
			error("can't open output file ", oname);
		}

		// read an arbitrary number of years:
		vector<Year> ys;
		while (true)
		{
			// get a freshly initialized Year each time around
			Year y;
			if (!(ist >> y))
			{
				break;
			}
			ys.push_back(y);
		}
		cout << "read " << ys.size() << " years of readings\n";

		for (const Year &y : ys)
		{
			print_year(ost, y);
		}
	}
	catch (const runtime_error &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 1;
	}
	catch (const exception &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 2;
	}
	keep_window_open();
	return 0;
}

istream& operator>>(istream &is, Reading &r)
// read a temperature reading from is into r
// format: ( 3 4 9.7 )
// check format, but don’t bother with data validity
{
	char ch1;
	if (is >> ch1 && ch1 != '(') 
	// could it be a Reading?
	{ 
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}
	char ch2;
	int d;
	int h;
	double t;
	is >> d >> h >> t >> ch2;
	if (!is || ch2 != ')')
	{
		// messed-up reading
		error("bad reading"); 
	}
	r.day = d;
	r.hour = h;
	r.temperature = t;
	return is;
}

istream& operator>>(istream &is, Month &m)
// read a month from is into m
// format: { month feb . . . }
{
	char ch = 0;
	if (is >> ch && ch != '{')
	{
		is.unget();

		// we failed to read a Month
		is.clear(ios_base::failbit);
		return is;
	}

	string month_marker;
	string mm;
	is >> month_marker >> mm;
	if (!is || month_marker != "month")
	{
		error("bad start of month");
	}
	m.month = month_to_int(mm);

	int duplicates = 0;
	int invalids = 0;
	for (Reading r; is >> r;)
	{
		if (is_valid(r))
		{
			if (m.day[r.day].hour[r.hour] != not_a_reading)
			{
				++duplicates;
			}
			m.day[r.day].hour[r.hour] = r.temperature;
		}
		else
		{
			++invalids;
		}
	}
	if (invalids)
	{
		error("invalid readings in month", invalids);
	}
	if (duplicates)
	{
		error("duplicate readings in month", duplicates);
	}
	end_of_loop(is, '}', "bad end of month");
	return is;
}

istream& operator>>(istream &is, Year &y)
// read a year from is into y
// format: { year 1972 . . . }
{
	char ch;
	is >> ch;
	if (ch != '{')
	{
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}

	string year_marker;
	int yy;
	is >> year_marker >> yy;
	if (!is || year_marker != "year")
	{
		error("bad start of year");
	}
	y.year = yy;

	while (true)
	{
		// get a clean m each time around
		Month m;
		if (!(is >> m))
		{
			break;
		}
		y.month[m.month] = m;
	}

	end_of_loop(is, '}', "bad end of year");
	return is;
}

int month_to_int(const string &s)
// is s the name of a month? If so return its index [0:11] otherwise – 1
{
	vector<string> month_input_tbl =
	{"jan", "feb", "mar", "apr", "may", "jun", 
		"jul", "aug", "sep", "oct", "nov", "dec"};

	for (int i = 0; i < 12; ++i)
	{
		if (month_input_tbl[i] == s)
		{
			return i;
		}
	}
	return -1;
}

string int_to_month(int i)
// months [0:11]
{
	vector<string> month_print_tbl =
	{ "January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December" };

	if (i < 0 || i >= 12)
	{
		cout << i << ":\n";
		error("bad month index");
	}
	return month_print_tbl[i];
}

bool is_valid(const Reading &r)
// a rough test
{
	constexpr int implausible_min = -200;
	constexpr int implausible_max = 200;

	if (r.day < 1 || r.day > 31)
	{
		return false;
	}
	if (r.hour < 0 || r.hour > 23)
	{
		return false;
	}
	if (r.temperature < implausible_min || r.temperature > implausible_max)
	{
		return false;
	}
	return true;
}

void end_of_loop(istream &ist, char term, const string &message)
{
	// use term as terminator and/or separator
	if (ist.fail())
	{
		ist.clear();
		char ch;
		if (ist >> ch && ch == term)
		{
			// all is fine
			return;
		}
		error(message);
	}
}

void print_year(ostream &os, const Year &y)
{
	constexpr double abs_zero = -459.67;
	for (size_t i = 1; i < y.month.size(); ++i)
	{
		if (y.month[i].day[i].hour[i] > abs_zero)
		{
			os << '{' << "Year: " << y.year
				<< " Month: " << int_to_month(y.month[i].month)
				<< " Temperature: " << y.month[i].day[i].hour[i] << "}\n";
		}
	}
}


How can I iterate over the vectors I have access to and print out all the data I need to print out when I don't have access to the Years vector in print_year()? Or should I just change print_year() to make it take a vector of Years along with the parameters it already takes?
Last edited on
> How can I iterate over the vectors I have access to and print out all the data I need to print out
> when I don't have access to the Years vector in print_year()?

To print information for all the years, write a loop: for( const Year &y : ys ) print_year( ost, y );

In print_year(), print the year number,
and then iterate over the month vector and call print_month() for each month in the year.

In print_month(), print the month name,
and then iterate over the day vector and call print_day() for each day in the month.

In print_day(), print the day number,
and then iterate over the hour vector and print information for each hour in the day.
How can I iterate over the vectors I have access to and print out all the data I need to print out when I don't have access to the Years vector in print_year()?

You don't seem to understand that you're already iterating through the years vector, that what that loop in main() is doing.

Or should I just change print_year() to make it take a vector of Years along with the parameters it already takes?

No, print_year() already has the correct parameters you just need to use the parameters correctly.

And your code from a couple of posts back is flawed. Your trying to return something from a void function.

Okay, I told you I didn't actually try the code, but it should be close to getting you pointed in the correct direction. Do you understand what that code is illustrating?

Edit: Anyway, I changed i in print_year()'s loop to start at 1 instead of 0.

Why did you do that? The months start at 0 for January, not 1.

This time, please notice what I've been trying to get you notice for so long now: the print_year(), as per the book itself even, and also as it's called in main(), doesn't take in a Years vector as an argument at all. It only takes an ostream object and a single Year object.

So, what does that have to do with your problem of not iterating through each month, each day, and each hour for every year that is passed into that function? You are being passed a Year from that that loop in main(). That year contains 12 months (0 - 11) that contain 31 days ( 1 - 31), that contain 24 hours ( 0 - 23).




So the months and hours have to go from 0 and the days have to go from 1. How do I make the days start from 1, though? Add 1 to the loop variable where the days are indexed into?

@JLBorges: Thanks. I'll try.

Edit: How do I print the day number in print_day()? The Day struct only has an hour vector. I'm thinking I should write a function that converts Days to ints, like how the month_to_int() function converts a Month to an int, but I'm not sure how to do the mapping between a vector of ints and the Day object.

For now I'll just make do with iterating over the hours without printing the day number.

Edit2: Alright, I got it to work.
This is the output file:

{1992
January
61.5 F
February
64 F
65.2 F
}
{2000
February
67.2 F
68 F
66.66 F
December
-2 F
-8.8 F
-9.2 F
}


And here's the full 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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
// temp_readings.cpp : Defines the entry point for the console application.
// Osman Zakir
// 2 / 7 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 10 Sections 10.1.1 to 10.1.3
// This program takes temperature readings from an input file and prints it to
// output file.  The readings are represented in the form of readings for the
// year, month and hour.  Structs are used to represent Readings, Days, and Months.

#include "../../std_lib_facilities.h"

// not_a_reading == less than absolute zero
const int not_a_reading = -7777;

const int not_a_month = -1;

struct Day
{
	vector<double> hour{ vector<double>(24, not_a_reading) };
};

// a month of temperature readings
struct Month
{
	// [0:11] January is 0
	int month{ not_a_month };

	// [1:31] one vector of readings per day
	vector<Day> day{ 32 };
};

// a year of temperature readings, organized by month
struct Year
{
	// positive == A.D.
	int year;

	// [0:11] January is 0
	vector<Month> month{ 12 };
};

struct Reading
{
	int day;
	int hour;
	double temperature;
};

istream& operator>>(istream &is, Reading &r);
istream& operator>>(istream &is, Month &m);
istream& operator>>(istream &is, Year &y);
string int_to_month(int i);
int month_to_int(const string &s);
bool is_valid(const Reading &r);
void end_of_loop(istream &is, char term, const string &message);
void print_year(ostream &os, const Year &y);
void print_month(ostream &os, const Month &m);
void print_day(ostream &os, const Day &d);

int main()
{
	try
	{
		// open an input file:
		cout << "Please enter input file name\n";
		string iname;
		cin >> iname;
		ifstream ist{ iname };
		if (!ist)
		{
			error("can't open input file ", iname);
		}

		ist.exceptions(ist.exceptions() | ios_base::badbit);

		// open an output file:
		cout << "Please enter output file name\n";
		string oname;
		cin >> oname;
		ofstream ost{ oname };
		if (!ost)
		{
			error("can't open output file ", oname);
		}

		// read an arbitrary number of years:
		vector<Year> ys;
		while (true)
		{
			// get a freshly initialized Year each time around
			Year y;
			if (!(ist >> y))
			{
				break;
			}
			ys.push_back(y);
		}
		cout << "read " << ys.size() << " years of readings\n";

		for (const Year &y : ys)
		{
			print_year(ost, y);
		}
	}
	catch (const runtime_error &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 1;
	}
	catch (const exception &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 2;
	}
	keep_window_open();
	return 0;
}

istream& operator>>(istream &is, Reading &r)
// read a temperature reading from is into r
// format: ( 3 4 9.7 )
// check format, but don’t bother with data validity
{
	char ch1;
	if (is >> ch1 && ch1 != '(') 
	// could it be a Reading?
	{ 
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}
	char ch2;
	int d;
	int h;
	double t;
	is >> d >> h >> t >> ch2;
	if (!is || ch2 != ')')
	{
		// messed-up reading
		error("bad reading"); 
	}
	r.day = d;
	r.hour = h;
	r.temperature = t;
	return is;
}

istream& operator>>(istream &is, Month &m)
// read a month from is into m
// format: { month feb . . . }
{
	char ch = 0;
	if (is >> ch && ch != '{')
	{
		is.unget();

		// we failed to read a Month
		is.clear(ios_base::failbit);
		return is;
	}

	string month_marker;
	string mm;
	is >> month_marker >> mm;
	if (!is || month_marker != "month")
	{
		error("bad start of month");
	}
	m.month = month_to_int(mm);

	int duplicates = 0;
	int invalids = 0;
	for (Reading r; is >> r;)
	{
		if (is_valid(r))
		{
			if (m.day[r.day].hour[r.hour] != not_a_reading)
			{
				++duplicates;
			}
			m.day[r.day].hour[r.hour] = r.temperature;
		}
		else
		{
			++invalids;
		}
	}
	if (invalids)
	{
		error("invalid readings in month", invalids);
	}
	if (duplicates)
	{
		error("duplicate readings in month", duplicates);
	}
	end_of_loop(is, '}', "bad end of month");
	return is;
}

istream& operator>>(istream &is, Year &y)
// read a year from is into y
// format: { year 1972 . . . }
{
	char ch;
	is >> ch;
	if (ch != '{')
	{
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}

	string year_marker;
	int yy;
	is >> year_marker >> yy;
	if (!is || year_marker != "year")
	{
		error("bad start of year");
	}
	y.year = yy;

	while (true)
	{
		// get a clean m each time around
		Month m;
		if (!(is >> m))
		{
			break;
		}
		y.month[m.month] = m;
	}

	end_of_loop(is, '}', "bad end of year");
	return is;
}

int month_to_int(const string &s)
// is s the name of a month? If so return its index [0:11] otherwise – 1
{
	vector<string> month_input_tbl =
	{"jan", "feb", "mar", "apr", "may", "jun", 
		"jul", "aug", "sep", "oct", "nov", "dec"};

	for (int i = 0; i < 12; ++i)
	{
		if (month_input_tbl[i] == s)
		{
			return i;
		}
	}
	return -1;
}

string int_to_month(int i)
// months [0:11]
{
	vector<string> month_print_tbl =
	{ "January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December" };

	if (i < 0 || i >= 12)
	{
		cout << i << ":\n";
		error("bad month index");
	}
	return month_print_tbl[i];
}

bool is_valid(const Reading &r)
// a rough test
{
	constexpr int implausible_min = -200;
	constexpr int implausible_max = 200;

	if (r.day < 1 || r.day > 31)
	{
		return false;
	}
	if (r.hour < 0 || r.hour > 23)
	{
		return false;
	}
	if (r.temperature < implausible_min || r.temperature > implausible_max)
	{
		return false;
	}
	return true;
}

void end_of_loop(istream &ist, char term, const string &message)
{
	// use term as terminator and/or separator
	if (ist.fail())
	{
		ist.clear();
		char ch;
		if (ist >> ch && ch == term)
		{
			// all is fine
			return;
		}
		error(message);
	}
}

void print_year(ostream &os, const Year &y)
{
	os << '{' << y.year << '\n';
	for (const auto &m : y.month)
	{
		if (m.month != not_a_month)
		{
			print_month(os, m);
		}
	}
	os << "}\n";
}

void print_month(ostream &os, const Month &m)
{
	os << '\t' << int_to_month(m.month) << '\n';
	for (const auto &d : m.day)
	{
		print_day(os, d);
	}
}

void print_day(ostream &os, const Day &d)
{
	constexpr double abs_zero = -459.67;
	for (const auto &temp : d.hour)
	{
		if (temp != not_a_reading || temp > abs_zero)
		{
			os << temp << " F\n";
		}
	}
}


Is this about right? How do I print the day number and the hour of day?
Last edited on
Your print_year() looks okay. I wouldn't necessarily print the braces and I'd probably using some of the manipulators to pretty up the print out though.

Your print_month() is not correct however. Remember that the days start at 1 not zero therefore you can't use the ranged based loop. You'll need to use a regular old for() loop based on the size of the vector.

Your print_day() function is okay unless you want to actually print the day. To print the day you'll either need to print it in the print_month() function or pass the day into this function as a parameter. I suggest printing the day in the print_month() function.


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
//------------------------------------------------------------------------------

void print_day(ostream& ost, const Day& d)
{
    for(const auto &temp : d.hour)
    {
        // You only need to test for not_a_reading, your input validation already did everything else.
        if(temp != not_a_reading)
        {
            ost << setw(10) << temp << " F\n";
        }
    }
}

// Return true only if there is at least one actual temperature reading.
bool is_valid_day(const Day& day)
{
    // I'll leave the logic as an exercise for you.
    return false;
}

void print_month(ostream& ost, const Month& m)
{
    // Can't use a ranged based loop because days start at 1 not zero.
    for(size_t i = 1; i < m.day.size(); ++i)
    {
        // Only print a day if the day has at least one valid temperature.
        if(is_valid_day(m.day[i]))
        {
            ost << setw(10) << int_to_month(m.month) << ' ';
            ost << i << '\n';
            print_day(ost, m.day[i]);
        }
    }
}

void print_year(ostream &os, const Year &y)
{
    os << fixed << setprecision(2) << showpoint;

    os << y.year << '\n';

    for(const auto &m : y.month)
    {
        if(m.month != not_a_month)
        {
            print_month(os, m);
        }
    }

    os << "\n";
}

//------------------------------------------------------------------------------


> How do I print the day number and the hour of day?

Something like this, perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void print_day( std::ostream& stm, const Day& d, int day_number )
{
    // you may want to add this check; if so, uncomment the next line
    // if( std::any_of( d.hour.begin(), d.hour.end(), [] ( double d ){ return d > not_a_reading ; } ) )
    {
        stm << "day: " << day_number << '\n' ; // print day number
        for( std::size_t i = 0 ; i < d.hour.size() ; ++i )
            if( d.hour[i] > not_a_reading ) stm << "hour: " << i << " reading: " << d.hour[i] << '\n' ;
    }
}

void print_month( std::ostream& stm, const Month& m )
{
    if( m.month != not_a_month )
    {
        stm << "month: " << int_to_month(m.month) << '\n' ; // print month name
        for( std::size_t i = 1 ; i < m.day.size() ; ++i ) print_day( stm, m.day[i], i ) ;
    }
}
This the file now:

1992
January
February
February 1
64 F
February 2
65.2 F

2000
February
February 1
67.2 F
68 F
February 2
66.66 F
December
December 15
-8.8 F
-9.2 F


Is this right? And if so, why are January and February just printed with nothing there and then the first two temperatures are both for February?

Here's the 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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
// temp_readings.cpp : Defines the entry point for the console application.
// Osman Zakir
// 2 / 7 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 10 Sections 10.1.1 to 10.1.3
// This program takes temperature readings from an input file and prints it to
// output file.  The readings are represented in the form of readings for the
// year, month and hour.  Structs are used to represent Readings, Days, and Months.

#include "../../std_lib_facilities.h"

// not_a_reading == less than absolute zero
const int not_a_reading = -7777;

const int not_a_month = -1;

struct Day
{
	vector<double> hour{ vector<double>(24, not_a_reading) };
};

// a month of temperature readings
struct Month
{
	// [0:11] January is 0
	int month{ not_a_month };

	// [1:31] one vector of readings per day
	vector<Day> day{ 32 };
};

// a year of temperature readings, organized by month
struct Year
{
	// positive == A.D.
	int year;

	// [0:11] January is 0
	vector<Month> month{ 12 };
};

struct Reading
{
	int day;
	int hour;
	double temperature;
};

istream& operator>>(istream &is, Reading &r);
istream& operator>>(istream &is, Month &m);
istream& operator>>(istream &is, Year &y);
string int_to_month(int i);
int month_to_int(const string &s);
bool is_valid(const Reading &r);
bool is_valid_day(const Day &d);
void end_of_loop(istream &is, char term, const string &message);
void print_year(ostream &os, const Year &y);
void print_month(ostream &os, const Month &m);
void print_day(ostream &os, const Day &d);

int main()
{
	try
	{
		// open an input file:
		cout << "Please enter input file name\n";
		string iname;
		cin >> iname;
		ifstream ist{ iname };
		if (!ist)
		{
			error("can't open input file ", iname);
		}

		ist.exceptions(ist.exceptions() | ios_base::badbit);

		// open an output file:
		cout << "Please enter output file name\n";
		string oname;
		cin >> oname;
		ofstream ost{ oname };
		if (!ost)
		{
			error("can't open output file ", oname);
		}

		// read an arbitrary number of years:
		vector<Year> ys;
		while (true)
		{
			// get a freshly initialized Year each time around
			Year y;
			if (!(ist >> y))
			{
				break;
			}
			ys.push_back(y);
		}
		cout << "read " << ys.size() << " years of readings\n";

		for (const Year &y : ys)
		{
			print_year(ost, y);
		}
	}
	catch (const runtime_error &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 1;
	}
	catch (const exception &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 2;
	}
	keep_window_open();
	return 0;
}

istream& operator>>(istream &is, Reading &r)
// read a temperature reading from is into r
// format: ( 3 4 9.7 )
// check format, but don’t bother with data validity
{
	char ch1;
	if (is >> ch1 && ch1 != '(') 
	// could it be a Reading?
	{ 
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}
	char ch2;
	int d;
	int h;
	double t;
	is >> d >> h >> t >> ch2;
	if (!is || ch2 != ')')
	{
		// messed-up reading
		error("bad reading"); 
	}
	r.day = d;
	r.hour = h;
	r.temperature = t;
	return is;
}

istream& operator>>(istream &is, Month &m)
// read a month from is into m
// format: { month feb . . . }
{
	char ch = 0;
	if (is >> ch && ch != '{')
	{
		is.unget();

		// we failed to read a Month
		is.clear(ios_base::failbit);
		return is;
	}

	string month_marker;
	string mm;
	is >> month_marker >> mm;
	if (!is || month_marker != "month")
	{
		error("bad start of month");
	}
	m.month = month_to_int(mm);

	int duplicates = 0;
	int invalids = 0;
	for (Reading r; is >> r;)
	{
		if (is_valid(r))
		{
			if (m.day[r.day].hour[r.hour] != not_a_reading)
			{
				++duplicates;
			}
			m.day[r.day].hour[r.hour] = r.temperature;
		}
		else
		{
			++invalids;
		}
	}
	if (invalids)
	{
		error("invalid readings in month", invalids);
	}
	if (duplicates)
	{
		error("duplicate readings in month", duplicates);
	}
	end_of_loop(is, '}', "bad end of month");
	return is;
}

istream& operator>>(istream &is, Year &y)
// read a year from is into y
// format: { year 1972 . . . }
{
	char ch;
	is >> ch;
	if (ch != '{')
	{
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}

	string year_marker;
	int yy;
	is >> year_marker >> yy;
	if (!is || year_marker != "year")
	{
		error("bad start of year");
	}
	y.year = yy;

	while (true)
	{
		// get a clean m each time around
		Month m;
		if (!(is >> m))
		{
			break;
		}
		y.month[m.month] = m;
	}

	end_of_loop(is, '}', "bad end of year");
	return is;
}

int month_to_int(const string &s)
// is s the name of a month? If so return its index [0:11] otherwise – 1
{
	vector<string> month_input_tbl =
	{"jan", "feb", "mar", "apr", "may", "jun", 
		"jul", "aug", "sep", "oct", "nov", "dec"};

	for (int i = 0; i < 12; ++i)
	{
		if (month_input_tbl[i] == s)
		{
			return i;
		}
	}
	return -1;
}

string int_to_month(int i)
// months [0:11]
{
	vector<string> month_print_tbl =
	{ "January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December" };

	if (i < 0 || i >= 12)
	{
		cout << i << ":\n";
		error("bad month index");
	}
	return month_print_tbl[i];
}

bool is_valid(const Reading &r)
// a rough test
{
	constexpr int implausible_min = -200;
	constexpr int implausible_max = 200;

	if (r.day < 1 || r.day > 31)
	{
		return false;
	}
	if (r.hour < 0 || r.hour > 23)
	{
		return false;
	}
	if (r.temperature < implausible_min || r.temperature > implausible_max)
	{
		return false;
	}
	return true;
}

bool is_valid_day(const Day &d)
{
	for (size_t i = 1; i < d.hour.size(); ++i)
	{
		if (d.hour[i] != not_a_reading)
		{
			return true;
		}
	}
	return false;
}

void end_of_loop(istream &ist, char term, const string &message)
{
	// use term as terminator and/or separator
	if (ist.fail())
	{
		ist.clear();
		char ch;
		if (ist >> ch && ch == term)
		{
			// all is fine
			return;
		}
		error(message);
	}
}

void print_year(ostream &os, const Year &y)
{
	os << y.year << '\n';
	for (const auto &m : y.month)
	{
		if (m.month != not_a_month)
		{
			print_month(os, m);
		}
	}
	os << '\n';
}

void print_month(ostream &os, const Month &m)
{
	os << '\t' << int_to_month(m.month) << '\n';
	for (size_t i = 1; i < m.day.size(); ++i)
	{
		if (is_valid_day(m.day[i]))
		{
			os << setw(10) << int_to_month(m.month) << ' '
				<< i << '\n';
			print_day(os, m.day[i]);
		}
	}
}

void print_day(ostream &os, const Day &d)
{
	for (const auto &temp : d.hour)
	{
		if (temp != not_a_reading)
		{
			os << setw(10) << temp << " F\n";
		}
	}
}


Edit: Is it not printing January's reading from the year 1992 because it's an invalid reading?

Edit: Update to output file:

1990

1991
June

1992
January
February
February 1
1 64
February 2
2 65.2

2000
February
February 1
0 67.2
1 68
February 2
3 66.66
December
December 15
14 -8.8
15 -9.2



Tabs and spaces aren't preserved. Just to let you know.

I also made a change to print_day():
1
2
3
4
5
6
7
8
9
10
void print_day(ostream &os, const Day &d)
{
	for (size_t i = 0; i < d.hour.size(); ++i)
	{
		if (d.hour[i] != not_a_reading)
		{
			os << setw(10) << i << ' ' << d.hour[i] << '\n';
		}
	}
}


So yeah, why isn't the reading from January of 1992 printed?
Last edited on
Have you tried stepping through it in a debugger? That will allow you to see which conditional code is actually being run, and will allow you to examine the contents of the memory at that point to see why conditions are or aren't being met.

EDIT: I notice you've been asked this twice before in this thread, and didn't answer. If you're going to ignore the things people say here, then what's the point in us saying anything?
Last edited on
I didn't answer the question, but I did try using the debugger. I don't always answer the question in words. Sorry about that. I'll try using the debugger for this, but I'm not actually that good at using the debugger.

Edit: I tried doing it, but I couldn't see what was wrong. I set breakpoints at the call to print_year() in main, and at the functions it calls. I kept being taken into the #included files, though, so I really couldn't tell what's wrong in my code.

By the way, I tried exercise 6 of this chapter, the Roman_int class exercise. I got a debug assertion error when I ran it, though, apparently because "std::vector iterator can't be incremented". I'll make another thread for this later, but I thought I'd give you guys a heads-up.
Last edited on
So yeah, why isn't the reading from January of 1992 printed?

Please re-post your current input file.
Here:

{ year 1990 }
{year 1991 { month jun }}
{ year 1992 { month jan ( 1 0 61.5) } {month feb (1 1 64) (2 2 65.2) } }
{year 2000
{ month feb (1 1 68 ) (2 3 66.66 ) ( 1 0 67.2)}
{month dec (15 15 -9.2 ) (15 14 -8.8) (14 0 -2) }
}
I wonder what makes the exercise so difficult? What does the input file possibly tell you? And who created this input file?
I wonder what makes the exercise so difficult?

It was intentionally designed to be so difficult to show a more realistic "real world" type of problem.

And who created this input file?

The author of the book "Programming Principles and Practice Using C++", Bjarne Stroustrup.

So yeah, why isn't the reading from January of 1992 printed?

This is where you need to either learn to use your debugger or learn to add copious print statements in the various parts of the code you're currently trying to debug. There is nothing wrong with the entry for January 1 1992 at hour 0 with a temperature of 61.5 degrees. I suspect that there is something wrong in your one of your print statements, or perhaps your day validation routine.

This is how I've defined is_valid_day():
1
2
3
4
5
6
7
8
9
10
11
bool is_valid_day(const Day &d)
{
	for (size_t i = 1; i < d.hour.size(); ++i)
	{
		if (d.hour[i] != not_a_reading)
		{
			return true;
		}
	}
	return false;
}


I'll try to debug on my end, too. Please also tell me if there's a problem in the code logic of the above function.

Edit: Okay, never mind. I was able to fix it.
Output file:

1990

1991
June

1992
January
January 1
0 61.5 F
February
February 1
1 64 F
February 2
2 65.2 F

2000
February
February 1
0 67.2 F
1 68 F
February 2
3 66.66 F
December
December 14
0 -2 F
December 15
14 -8.8 F
15 -9.2 F



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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
// temp_readings.cpp : Defines the entry point for the console application.
// Osman Zakir
// 2 / 7 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 10 Sections 10.1.1 to 10.1.3
// This program takes temperature readings from an input file and prints it to
// output file.  The readings are represented in the form of readings for the
// year, month and hour.  Structs are used to represent Readings, Days, and Months.

#include "../../std_lib_facilities.h"

// not_a_reading == less than absolute zero
const int not_a_reading = -7777;

const int not_a_month = -1;

struct Day
{
	vector<double> hour{ vector<double>(24, not_a_reading) };
};

// a month of temperature readings
struct Month
{
	// [0:11] January is 0
	int month{ not_a_month };

	// [1:31] one vector of readings per day
	vector<Day> day{ 32 };
};

// a year of temperature readings, organized by month
struct Year
{
	// positive == A.D.
	int year;

	// [0:11] January is 0
	vector<Month> month{ 12 };
};

struct Reading
{
	int day;
	int hour;
	double temperature;
};

istream& operator>>(istream &is, Reading &r);
istream& operator>>(istream &is, Month &m);
istream& operator>>(istream &is, Year &y);
string int_to_month(int i);
int month_to_int(const string &s);
bool is_valid(const Reading &r);
bool is_valid_day(const Day &d);
void end_of_loop(istream &is, char term, const string &message);
void print_year(ostream &os, const Year &y);
void print_month(ostream &os, const Month &m);
void print_day(ostream &os, const Day &d);

int main()
{
	try
	{
		// open an input file:
		cout << "Please enter input file name\n";
		string iname;
		cin >> iname;
		ifstream ist{ iname };
		if (!ist)
		{
			error("can't open input file ", iname);
		}

		ist.exceptions(ist.exceptions() | ios_base::badbit);

		// open an output file:
		cout << "Please enter output file name\n";
		string oname;
		cin >> oname;
		ofstream ost{ oname };
		if (!ost)
		{
			error("can't open output file ", oname);
		}

		// read an arbitrary number of years:
		vector<Year> ys;
		while (true)
		{
			// get a freshly initialized Year each time around
			Year y;
			if (!(ist >> y))
			{
				break;
			}
			ys.push_back(y);
		}
		cout << "read " << ys.size() << " years of readings\n";

		for (const Year &y : ys)
		{
			print_year(ost, y);
		}
	}
	catch (const runtime_error &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 1;
	}
	catch (const exception &e)
	{
		cerr << "error: " << e.what() << '\n';
		keep_window_open();
		return 2;
	}
	keep_window_open();
	return 0;
}

istream& operator>>(istream &is, Reading &r)
// read a temperature reading from is into r
// format: ( 3 4 9.7 )
// check format, but don’t bother with data validity
{
	char ch1;
	if (is >> ch1 && ch1 != '(') 
	// could it be a Reading?
	{ 
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}
	char ch2;
	int d;
	int h;
	double t;
	is >> d >> h >> t >> ch2;
	if (!is || ch2 != ')')
	{
		// messed-up reading
		error("bad reading"); 
	}
	r.day = d;
	r.hour = h;
	r.temperature = t;
	return is;
}

istream& operator>>(istream &is, Month &m)
// read a month from is into m
// format: { month feb . . . }
{
	char ch = 0;
	if (is >> ch && ch != '{')
	{
		is.unget();

		// we failed to read a Month
		is.clear(ios_base::failbit);
		return is;
	}

	string month_marker;
	string mm;
	is >> month_marker >> mm;
	if (!is || month_marker != "month")
	{
		error("bad start of month");
	}
	m.month = month_to_int(mm);

	int duplicates = 0;
	int invalids = 0;
	for (Reading r; is >> r;)
	{
		if (is_valid(r))
		{
			if (m.day[r.day].hour[r.hour] != not_a_reading)
			{
				++duplicates;
			}
			m.day[r.day].hour[r.hour] = r.temperature;
		}
		else
		{
			++invalids;
		}
	}
	if (invalids)
	{
		error("invalid readings in month", invalids);
	}
	if (duplicates)
	{
		error("duplicate readings in month", duplicates);
	}
	end_of_loop(is, '}', "bad end of month");
	return is;
}

istream& operator>>(istream &is, Year &y)
// read a year from is into y
// format: { year 1972 . . . }
{
	char ch;
	is >> ch;
	if (ch != '{')
	{
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}

	string year_marker;
	int yy;
	is >> year_marker >> yy;
	if (!is || year_marker != "year")
	{
		error("bad start of year");
	}
	y.year = yy;

	while (true)
	{
		// get a clean m each time around
		Month m;
		if (!(is >> m))
		{
			break;
		}
		y.month[m.month] = m;
	}

	end_of_loop(is, '}', "bad end of year");
	return is;
}

int month_to_int(const string &s)
// is s the name of a month? If so return its index [0:11] otherwise – 1
{
	vector<string> month_input_tbl =
	{"jan", "feb", "mar", "apr", "may", "jun", 
		"jul", "aug", "sep", "oct", "nov", "dec"};

	for (int i = 0; i < 12; ++i)
	{
		if (month_input_tbl[i] == s)
		{
			return i;
		}
	}
	return -1;
}

string int_to_month(int i)
// months [0:11]
{
	vector<string> month_print_tbl =
	{ "January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December" };

	if (i < 0 || i >= 12)
	{
		cout << i << ":\n";
		error("bad month index");
	}
	return month_print_tbl[i];
}

bool is_valid(const Reading &r)
// a rough test
{
	constexpr int implausible_min = -200;
	constexpr int implausible_max = 200;

	if (r.day < 1 || r.day > 31)
	{
		return false;
	}
	if (r.hour < 0 || r.hour > 23)
	{
		return false;
	}
	if (r.temperature < implausible_min || r.temperature > implausible_max)
	{
		return false;
	}
	return true;
}

bool is_valid_day(const Day &d)
{
	for (size_t i = 0; i < d.hour.size(); ++i)
	{
		if (d.hour[i] != not_a_reading)
		{
			return true;
		}
	}
	return false;
}

void end_of_loop(istream &ist, char term, const string &message)
{
	// use term as terminator and/or separator
	if (ist.fail())
	{
		ist.clear();
		char ch;
		if (ist >> ch && ch == term)
		{
			// all is fine
			return;
		}
		error(message);
	}
}

void print_year(ostream &os, const Year &y)
{
	os << y.year << '\n';
	for (const auto &m : y.month)
	{
		if (m.month != not_a_month)
		{
			print_month(os, m);
		}
	}
	os << '\n';
}

void print_month(ostream &os, const Month &m)
{
	os << '\t' << int_to_month(m.month) << '\n';
	for (size_t i = 1; i < m.day.size(); ++i)
	{
		if (is_valid_day(m.day[i]))
		{
			os << setw(10) << int_to_month(m.month) << ' '
				<< i << '\n';
			print_day(os, m.day[i]);
		}
	}
}

void print_day(ostream &os, const Day &d)
{
	for (size_t i = 0; i < d.hour.size(); ++i)
	{
		if (d.hour[i] > not_a_reading)
		{
			os << setw(10) << i << ' ' << d.hour[i] << " F\n";
		}
	}
}
Last edited on
Pages: 123