Source Code:
#include // for regular expression support
#include
#include // for std::make_shared and std::shared_ptr
#include
#include // for C's strcmp (see below)
#include // for std::optional
#include
#include // for filesystem support
//Abstract Class
//Represents an input to the program
class program_input
{
public:
virtual ~program_input() { } // necessary to properly deallocate memory
virtual bool read() = 0;
};
using all_inputs_type = std::vector<:shared_ptr>>;
std::vector read_all_inputs(all_inputs_type& /*ai*/)
{
// NOTE: For now the ai function argument is commented out to avoid a compiler warning about ai being unused.
return {}; // i.e., return a default constructed std::vector
}
std::ostream& output_usage(std::ostream& os, int /*argc*/, char *argv[])
{
os ";
return os;
}
output_message(std::cerr, argc, argv)
int main(int argc, char *argv[]) {
namespace fs = std::filesystem;
using namespace std;
optional scan_directory;
for (int args_pos=1; args_pos
{
// ...
}
all_inputs_type all_inputs; // this variable will hold all discovered program input files
for (auto& entry : fs::recursive_directory_iterator(scan_directory.value()))
{
if (!fs::is_regular_file(entry))
continue;
read_all_inputs(all_inputs); // i.e., process all discovered input (files)
// determine if file anme is of the form yobYYYY.txt...
static regex const baby_name_file_regex( R"(yob(\d{4}).txt)" );
smatch mr; // variable to hold regex matched results
string const fname = entry.path().filename(); // only match the filename
if (regex_match(fname, mr, baby_name_file_regex))
{
// regex_match() only returns true when the match is successful
// you will be writing code in here below
// for now use the following cout to see that it actually matches...
cout
}
}
}
Writing The yob_baby_name_file Class Write a class called "yob_baby_name_file" that inherits publicly from the "program_input" class. (This inheritance is not virtual: this code will only exploit the polymorphism of virtual functions.) The yob_baby_name_file" class is to be written as follows: It must store the file name it is associated with as a private std::string member called fname_. It must store the year it is associated with as a private unsigned member called year It must have a single constructor with the following prototype: o yob_baby_name_file(std:string fname, unsigned year) O NOTE: Its constructor must not have anything inside its body (i.e., within 0)! It must override the read) pure virtual function in program_input. o Hint: Start by writing "bool read) override". Writing yob_baby_name_file:read0) The yob_baby_name_file: read() member must do the following: Output to std::cout: o "Reading yob_baby_name_file "followed by fname_ followed by o "for year "followed by oyear_ followed by n' (i.e., a newline character). Return true when done. Back To main(): Writing The Code When The Regex Matches With the yob_ baby_name file class defined, the code can now be written when the regex matches asa single C++ statement: Call the variable "all_inputs" push_back) function passing in the result of this function call: o Call make_shared
(...) where .. are the arguments o fname, i.e., the file name that was matched), and, o mr[1].str0 converted to an unsigned number using stoul(0) NOTE 1: mr[1].str0 is the string corresponding to the (ldt4)) match NOTE 2: Look up stoul) at cppreference.com to see how to convert a C++ std::string to an unsigned long. Pass in nullptr for the second argument. What does this do? Recall all inputs is a vector of shared_ptr. * shared_ptr's store pointers to the type specified in its template parameter. Since yob_baby_name_file inherits from program input, a yob_baby_name_file* can be assigned to a program_input* (i.e., implicit upcasting). The call to make_shared ensures yob_baby_name_file is dynamically allocated and returns a std::shared_ptr. The latter is then passed to the constructor of a std::shared ptr (as that is what is stored in the all_input type vector!) which simply implicitly upcasts the derived pointer to its base class properly adjusting the reference counters appropriately When the statement finishes the temporary shared_ptr is destroyed but it no longer the only instance pointing to the memory that was allocated (because it was added to the vector) so the memory is not freed. Only when the last instance (i.e., when the all input vector is destroyed) does the memory associated with the various program_input-derived instances get destroyed Last Step: Writing The Code For read_all_inputs() Earlier you were given a trivial code stub for the read_all_inputs() function (which is called in the last line in main). Now that you are actually populating the all inputs variable with data, it makes sense to write the code for this function! Do the following in the order listed: Declare a variable called "retval" of type std::vector. (This will be the returned from the function.) Invoke std:vector's "reserve)"function passing in ai.size). o ai is the argument passed to read_all_inputs (see earlier stub) o The reserve calls tells the vector to ensure enough memory has been dynamically allocated to hold ai.size() elements. Use a range for loop to iterate over all elements in ai (i.e., the argument passed to the function) and within the loop o ASIDE: Below "" is used to refer to the current element involved with the for loop iteration. o Write try block with the single line of code Invoke the push_back() function on retval and pass what i->read) returns ASIDE: i is an instance of std::shared_ptr. The ->is the indirection operator which allows one to invoke the read() function on the internally stored program_input* object. In the catch(..) handler write the single line of code Call the push_back() function on retval passing in false. ASIDE: Since reserve() was used to pre-allocate all memory, push_back() should not throw. Even if push_back throws, the C++ Standard guarantees that push_back() has no effect if something is thrown from it. Thus, if an exception is thrown, then it must have come from std::shared_ptr or read). Either way, you want the code to continue processing recording exactly one bool for each element in ai. Thus, this catch(..) handler calls push_back(false) to ensure there is a (false) bool added to ai when an exception is thrown. Finally, return std:move(retval); Writing The yob_baby_name_file Class Write a class called "yob_baby_name_file" that inherits publicly from the "program_input" class. (This inheritance is not virtual: this code will only exploit the polymorphism of virtual functions.) The yob_baby_name_file" class is to be written as follows: It must store the file name it is associated with as a private std::string member called fname_. It must store the year it is associated with as a private unsigned member called year It must have a single constructor with the following prototype: o yob_baby_name_file(std:string fname, unsigned year) O NOTE: Its constructor must not have anything inside its body (i.e., within 0)! It must override the read) pure virtual function in program_input. o Hint: Start by writing "bool read) override". Writing yob_baby_name_file:read0) The yob_baby_name_file: read() member must do the following: Output to std::cout: o "Reading yob_baby_name_file "followed by fname_ followed by o "for year "followed by oyear_ followed by n' (i.e., a newline character). Return true when done. Back To main(): Writing The Code When The Regex Matches With the yob_ baby_name file class defined, the code can now be written when the regex matches asa single C++ statement: Call the variable "all_inputs" push_back) function passing in the result of this function call: o Call make_shared (...) where .. are the arguments o fname, i.e., the file name that was matched), and, o mr[1].str0 converted to an unsigned number using stoul(0) NOTE 1: mr[1].str0 is the string corresponding to the (ldt4)) match NOTE 2: Look up stoul) at cppreference.com to see how to convert a C++ std::string to an unsigned long. Pass in nullptr for the second argument. What does this do? Recall all inputs is a vector of shared_ptr. * shared_ptr's store pointers to the type specified in its template parameter. Since yob_baby_name_file inherits from program input, a yob_baby_name_file* can be assigned to a program_input* (i.e., implicit upcasting). The call to make_shared ensures yob_baby_name_file is dynamically allocated and returns a std::shared_ptr. The latter is then passed to the constructor of a std::shared ptr (as that is what is stored in the all_input type vector!) which simply implicitly upcasts the derived pointer to its base class properly adjusting the reference counters appropriately When the statement finishes the temporary shared_ptr is destroyed but it no longer the only instance pointing to the memory that was allocated (because it was added to the vector) so the memory is not freed. Only when the last instance (i.e., when the all input vector is destroyed) does the memory associated with the various program_input-derived instances get destroyed Last Step: Writing The Code For read_all_inputs() Earlier you were given a trivial code stub for the read_all_inputs() function (which is called in the last line in main). Now that you are actually populating the all inputs variable with data, it makes sense to write the code for this function! Do the following in the order listed: Declare a variable called "retval" of type std::vector. (This will be the returned from the function.) Invoke std:vector's "reserve)"function passing in ai.size). o ai is the argument passed to read_all_inputs (see earlier stub) o The reserve calls tells the vector to ensure enough memory has been dynamically allocated to hold ai.size() elements. Use a range for loop to iterate over all elements in ai (i.e., the argument passed to the function) and within the loop o ASIDE: Below "" is used to refer to the current element involved with the for loop iteration. o Write try block with the single line of code Invoke the push_back() function on retval and pass what i->read) returns ASIDE: i is an instance of std::shared_ptr. The ->is the indirection operator which allows one to invoke the read() function on the internally stored program_input* object. In the catch(..) handler write the single line of code Call the push_back() function on retval passing in false. ASIDE: Since reserve() was used to pre-allocate all memory, push_back() should not throw. Even if push_back throws, the C++ Standard guarantees that push_back() has no effect if something is thrown from it. Thus, if an exception is thrown, then it must have come from std::shared_ptr or read). Either way, you want the code to continue processing recording exactly one bool for each element in ai. Thus, this catch(..) handler calls push_back(false) to ensure there is a (false) bool added to ai when an exception is thrown. Finally, return std:move(retval)