Attempting to understand seperate compilation in c++

I am working from a book called Accelerated C++ to learn the language. I am currently in the section which talks about how programs are structured to enable separate compilation. The book is confusing me at this point. We left off with this 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
113
114
115
116
117
118
119
120
121
  #include <algorithm>
#include <iomanip>
#include <iostream>
#include <ios>
#include <string>
#include <vector>
#include <stdexcept>

using namespace std;

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

bool compare(const Student_info& x, const Student_info& y)
{
	return x.name < y.name;
}

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));
}

double grade(const Student_info& s)
{
	return grade(s.midterm, s.final, s.homework);
}

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;
}

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

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

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

int main()
{

	vector<Student_info> students;
	Student_info record;
	string::size_type maxlen = 0;

	while(read(cin, record))
	{

		maxlen = max(maxlen, record.name.size());
		students.push_back(record);
	}

	sort(students.begin(), students.end(), compare);

	for(vector<Student_info>::size_type i = 0; i != students.size(); ++i)
	{

		cout << students[i].name << string(maxlen + 1 - students[i].name.size(), ' ');

		try
		{

			double final_grade = grade(students[i]);
			streamsize prec = cout.precision();
			cout << setprecision(3) << final_grade << setprecision(prec);
		} catch (domain_error &e)
		{
			cout << e.what();
		}
	}

	return 0;
}


Now, it seems to be suggesting that I should pull out functions such as median and place them into separate files such as median.cpp: "Most implementations require the names of C++ source files to end in .cpp, .C or .c, so we might put the median function in a file named median.cpp, median.C or median.c, depending on the implementation."

However, it then goes on to say: "The next step is to make the median function available to other users. Analogous with the standard library, which puts the names it defines into headers, we can write our own header file that will..."

So is the book trying to say that I should pull out functions like median and put them into their own source files, then, create header files for those functions, then, include those header files in the actual program? For instance:

median.cpp includes median.h

StudentGrades.cpp includes StudentGrades.h

StudentGrades.h includes median.h

is that the correct structure? Or should I just include median.h in StudentGrades.cpp and if that's what I am supposed to do then what's the point on StudentGrades.h?
Last edited on
Possibly this?

median.hpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef __HEADER_H__

#include <string>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include <iostream>

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

bool compare(const Student_info& x, const Student_info& y);
double median(std::vector<double> vec);
double grade(double midterm, double final, double homework);
double grade(double midterm, double final, const std::vector<double>& hw);
double grade(const Student_info& s);
std::istream& read_hw(std::istream& in, std::vector<double>& hw);
std::istream& read(std::istream& is, Student_info& s);

#endif // !__HEADER_H__ 


median.cpp:
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
#include "median.hpp"

bool compare(const Student_info& x, const Student_info& y)
{
   return x.name < y.name;
}

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

   vec_sz size = vec.size();

   if (size == 0)
   {
      throw std::domain_error("median of an empty vector");
   }

   std::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 std::vector<double>& hw)
{
   if (hw.size() == 0)
   {
      throw std::domain_error("student has done no homework");
   }
   return grade(midterm, final, median(hw));
}

double grade(const Student_info & s)
{
   return grade(s.midterm, s.final, s.homework);
}

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

      double x;

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

std::istream& read(std::istream& is, Student_info& s)
{
   is >> s.name >> s.midterm >> s.final;

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


main.cpp (or whatever you call it):
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
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <ios>
#include <string>
#include <vector>
#include <stdexcept>

#include "median.hpp"

int main()
{
   std::vector<Student_info> students;
   Student_info record;
   std::string::size_type maxlen = 0;

   while (read(std::cin, record))
   {
      maxlen = std::max(maxlen, record.name.size());
      students.push_back(record);
   }

   sort(students.begin(), students.end(), compare);

   for (std::vector<Student_info>::size_type i = 0; i != students.size(); ++i)
   {
      std::cout << students[i].name << std::string(maxlen + 1 - students[i].name.size(), ' ');

      try
      {
         double final_grade = grade(students[i]);
         std::streamsize prec = std::cout.precision();
         std::cout << std::setprecision(3) << final_grade << std::setprecision(prec);
      }
      catch (std::domain_error& e)
      {
         std::cout << e.what();
      }
   }
}
I jumped ahead and they have the final program looking like 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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  #include <algorithm>
#include <iomanip>
#include <ios>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include "grade.h"
#include "Student_info.h"

using namespace std;

int main()
{
	vector<Student_info> students;
	Student_info record;
	string::size_type maxlen = 0;
	
	while(read(cin, record))
	{
		
		maxlen = max(maxlen, record.name.size());
		students.push_back(record);
	}
	
	sort(students.begin(), students.end(), compare);
	
	for(vector<Student_info>::size_type i = 0; i != students.size(); ++i)
		{

			cout << students[i].name << string(maxlen + 1 - 
                        students[i].name.size(), ' ');

			try
			{

				double final_grade = grade(students[i]);
				streamsize prec = cout.precision();
				cout << setprecision(3) << final_grade << setprecision(prec);
			} catch (domain_error &e)
			{
				cout << e.what();
			}
			
			cout << endl;
		}

		return 0;
	}


So then grade.cpp includes grade.h, median.h and one called Student_info.h. I guess I was expecting only one .h file for this entire program, namely StudentGrades.h but apparently the book thinks having a couple different headers and no main header for the program itself is more effective. I think I understand what they want.

However, the book just glances over how to get header files to talk to source files. For instance, it tells me to create median.cpp and median.h. I think I get how to tell median.cpp how to talk to median.h (by including it) but how do I get median.h to look for median.cpp?
Last edited on
Topic archived. No new replies allowed.