Section 12.13 Case Study: Building a Multi-File Program - Part 2
After
getMonth
is implemented, we are ready to tackle another function. getYear
or getDay
would be logical choices. But they are going to look a lot like getMonth
, so we will skip them for now.
Instead, we will skip ahead and try to implement our
int daysBeforeMonth(int month)
function.
Subsection 12.13.1 Testing daysBeforeMonth
Again, we should start by figuring out how we will know that
daysBeforeMonth
works correctly. To do this, we need to figure out what the correct answer for the various months are. (Remember that we are pretending leap years do not exist.)
Month | Days Before Month | Days In Month |
---|---|---|
January | 0 | 31 |
February | 31 (Days before January + Days in January)
|
28 |
March | 59 (Days before February + Days in February)
|
31 |
April | 90 (Days before March + Days in March)
|
30 |
... | ...
|
... |
Written as unit tests in
dateTests.cpp
, this might look like the following:
TEST_CASE("daysBeforeMonth") {
CHECK(daysBeforeMonth(1) == 0);
CHECK(daysBeforeMonth(2) == 31);
CHECK(daysBeforeMonth(4) == 90);
//...
CHECK(daysBeforeMonth(12) == 334);
}
How many months need to be tested? This is a judgement call. There are only 12 values, which is not an obnoxious number. And although the answer for the cases follow a pattern, it is not something that is calculated with a simple formula, so knowing that
daysBeforeMonth(8)
works, doesnβt guarantee that daysBeforeMonth(7)
is correct. So it might be reasonable to test all 12 cases.
If there was a simple formula like
30 * (month - 1)
there would be little value in testing all 12 cases. And if there were 120 different cases, it might be impractical to test them all individually. At some point, we would probably just start copy/pasting the values from tests into our implementation, which would mean we are not really testing anything other than βdid I put the same number in both filesβ.
As mentioned earlier, the goal is not always to exhaustively test all possible inputs, but rather to test enough interesting cases that we have some degree of confidence in our implementation.
Subsection 12.13.2 Implementing daysBeforeMonth
Once we add new test(s) to
dateTests.cpp
, we are ready to implement the daysBeforeMonth
function in DateFunctions.cxx
.
The function is relatively straightforward. It is really just a big set of if/elses or a switch statement. For invalid months, we should either return some sentinel value like
-1
or throw an exception. Here is one possible implementation:
int daysBeforeMonth(int month) {
if (month == 1) {
return 0;
} else if (month == 2) {
return 31;
} else if (month == 3) {
return 59;
//... more code here
} else if (month == 12) {
return 334;
} else {
throw std::logic_error("Invalid month: " + std::to_string(month));
}
Checkpoint 12.13.2.
Complete the compiler recipe to build our module file with the test file. Although the compiler provides flexibility in parameter ordering, use the order we have been using:
COMPILER_NAME FLAGS LIST_OF_FILES -o OUTPUT_NAME
. You will not need all the blocks.Subsection 12.13.3 Next steps
There really is not much point in adding code to
main.cpp
at this point. The main
function should not directly call daysBeforeMonth
. Instead, we might as well move on to implementing the dateToDays
function.
Again, we should start by writing tests for the
dateToDays
function. This will involve doing some math by hand to figure out how many days have elapsed to get us to a date like 3/14/2025
. Given our βno leap yearsβ assumption, and our existing daysBeforeMonth()
function,we can calculate the total number of days for 3/14/2025
as daysBeforeMonth(3) + 14 + 365 * 2025
.
Checkpoint 12.13.3.
The code itself should be very simple. Something like:
int dateToDays(string date) {
int month = getMonth(date);
int day = getDay(date);
int year = getYear(date);
int totalDays = daysBeforeMonth(month) + day + 365 * year;
return totalDays;
}
Insight 12.13.1.
Well designed helper functions should make implementing functions that use them easy. If you find yourself struggling to implement a function, it is maybe a sign you need to add more helper functions or redesign the ones you have.
You have attempted of activities on this page.