Skip to main content

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:
Listing 12.13.1.
TEST_CASE("daysBeforeMonth") {
    CHECK(daysBeforeMonth(1) == 0);
    CHECK(daysBeforeMonth(2) == 31);
    CHECK(daysBeforeMonth(4) == 90);
    //...
    CHECK(daysBeforeMonth(12) == 334);
}

Checkpoint 12.13.1.

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:
Listing 12.13.2.
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:
Listing 12.13.3.
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.