Making my student grades program more dynamic

The book I am using to learn C++ is called: Accelerated C++, practical programming by example and in section 4.2 we are improving upon this student grades program. The older version can only calculate one student grade at a time. So the idea is to make it more dynamic by creating a Sudent_info data structure. The book is a bit old so I can imagine some minor details may have changed since it was written. Perhaps this is why I am getting an error, I am not sure. The portion of the code that is giving an error is line 22. The error is (Console output):

1
2
  ../src/Student Grades.cpp: In function ‘std::istream& read(std::istream&, Student_info&)’:
../src/Student Grades.cpp:30:2: error: ‘read_hw’ was not declared in this scope


Full program:

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
 #include <algorithm>
#include <iomanip>
#include <iostream>
#include <ios>
#include <string>
#include <vector>

using namespace std;

struct Student_info
{
	string name;
	double midterm, final;
	vector<double> homework;
};

istream& read(istream& is, Student_info& s)
{

	is >> s.name >> s.midterm >> s.final;

	read_hw(is, s.homework);
	return is;
}

double median(vector<double> vec)
{
	typedef vector<double>::size_type vec_sz;

	vec_sz size = vec.size();
	if(size == 0)
	{
		throw domain_error("median of an empty vector");
	}

	sort(vec.begin(), vec.end());

	vec_sz mid = size/2;

	return size % 2 == 0 ? (vec[mid] + vec[mid-1]) / 2 : vec[mid];
}

double grade(double midterm, double final, double homework)
{
	return 0.2 * midterm + 0.4 * final + 0.4 * homework;
}

double grade(double midterm, double final, const vector<double>& hw)
{
	if(hw.size() == 0)
	{
		throw domain_error("student has done no homework");
	}

	return grade(midterm, final, median(hw));
}

istream& read_hw(istream& in, vector<double>& hw)
{
	if(in)
	{
		hw.clear();

		double x;
		while(in >> x)
		{
			hw.push_back(x);
		}

		in.clear();
	}

	return in;
}


int main()
{

	cout << "Please enter your first name: ";
	string name;
	cin >> name;
	cout << "Hello, " << name << endl;

	cout << "Please enter your midterm and final exam grades: ";
	double midterm, final;
	cin >> midterm >> final;

	cout << "Enter all your homework grades, followed by the end-of-file: ";

	vector<double> homework;

	read_hw(cin, homework);

	try
	{

		double final_grade = grade(midterm, final, homework);
		streamsize prec = cout.precision();

		cout << "Your final grade is " << setprecision(3) 
                << final_grade << setprecision(prec) << endl;

	}catch(domain_error)
	{
		cout << endl << "You must enter your grades. Please try again." << endl;

		return 1;
	}

	return 0;
}


However, if I highlight the error in the code there are two listed. The first one which I detailed above and this one:

1
2
3
4
  Problem description: Invalid arguments ' Candidates are: 
 std::basic_istream<char,std::char_traits> & 
 read_hw(std::basic_istream<char,std::char_traits> &, 
 std::vector<double,std::allocator> &) '
Last edited on
The C++ compiler reads from the top down, and needs to know the prototype of a function before it sees the actual call to the function.

In your code, it first finds the call to read_hw on line 22, but the function isn't first defined until line 58.

So you need to add a prototype before line 22.

istream& read_hw(istream& in, vector<double>& hw);

1
2
3
4
5
6
7
8
9
10
istream& read_hw(istream& in, vector<double>& hw);

istream& read(istream& is, Student_info& s)
{

	is >> s.name >> s.midterm >> s.final;

	read_hw(is, s.homework);
	return is;
}


Furthermore, std::domain_error is defined in header <stdexcept>, so you need to #include that.
Last edited on
Order matters. You're trying to call the function "read_hw" but it's declared after the function your trying to call it from.

Simply copy and paste the "read_hw" function above the "read" function and the program will compile.

A function can only see functions declared above it (before it). This is why people sometimes use function prototypes, so that they don't have to worry about this.

This site will explain:

http://www.cplusplus.com/articles/yAqpX9L8/

Basically, code like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
	double a = 3.5;
	int b = 2;
	double c;
	
	c = someFunction( a, b );
}

double someFunction( double x, int y )
{
	return x * y;
}


^Will not work because int main() can't see "someFunction". What a Function Prototype will do is let the compiler know that the function is somewhere in the code and not to freak out just because it hasn't found it yet. It's done like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
double someFunction( double, int ); //This is the function prototype

int main()
{
	double a = 3.5;
	int b = 2;
	double c;
	
	c = someFunction( a, b );
}

double someFunction( double x, int y )
{
	return x * y;
}



However, this (in my opinion) is really only for when you have several functions that rely on each other and you can't possibly put them in an order where they can all see each other properly - like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

int A(int a)
{
	B(a);
}

int B(int b)
{
	C(b);
}

int C(int c)
{
	A(c);
}

int main()
{
	A(2);
	return 0;
}


No matter how you reorder those functions, they will never be able to see each other as needed. In this case, you'll definitely need function prototypes. However, in your case, you can simply reorder the function and your program will work fine.
Last edited on
Ah crap, I should have seen that, thanks guys.
Topic archived. No new replies allowed.