CS253: Software Development with C++

Spring 2020

HW 5

CS253 HW5: Two classes: This time it’s classier!                

Description                

For this assignment, you will write two classes: Event and Schedule. Event represents a single date, per HW3, and Schedule is a chronologically-ordered collection of Event objects.                 

For this assignment, you will provide:

To use class Event, the user must #include "Event.h". To use class Schedule, the user must #include "Schedule.h".                 

Methods                

One method is forbidden:                

no default ctor
The default (no-argument) ctor for Event must fail to compile. This is not a run-time error; it’s a compile-time error.

Event must have the following methods:                

Event(C string)
Event(C++ string)
The string must contain an event, in one of the five HW3 formats. Initialize the Event to that date. Throw an explanatory runtime_error, including the entire bad date argument string, if the date is bad in any way, syntactically (e.g., 12-cucumber) or semantically (e.g., 2020-02-30).
Copy ctor
Assignment operator
Copy the information from the other Event.
Dtor
Destroy.
.set(int year, int month, int day)
Set the date for this Event to that date. Throw a runtime_error, containing the bad data, and leave the object unchanged, if the date is bad in any way. .set(2024,4,24) would set the date to today.
.year()
.month()
.day()
Return the int year, month, or day associated with this Event. They have the same values that would be passed to .set().

Schedule must have the following public methods:                

Schedule()
Create a Schedule containing no Events.
Schedule(istream)
Read Events from the given stream. All the requirements of .read() apply.
Schedule(C string)
Schedule(C++ string)
Read Events from the given filename. Throw a runtime_error, including the filename, if the file can’t be opened. All the requirements of .read() apply.
Copy constructor
Takes another object of the same class, and deep-copies the information, replacing any previous information.
Assignment operator
Takes another object of the same class, and deep-copies the information, replacing any previous information.
Destructor
Destroys this object, including all the Events associated with it.
.read(istream)
Read all Events from the istream, separated by whitespace, into the Schedule. This method does not replace previous contents—it adds to them.
Upon syntactic or semantic error:
  • set the istream to a failed state
  • the Schedule must contain all the previously-encountered Events in the istream up to the point or the error, and no Events from after that
  • the Schedule must not contain a half-constructed Event.
  • optionally: throw a runtime_error describing the problem
.clear()
Make this Schedule empty. If it’s already empty, then make it extremely empty.
.size()
Return the number of Events in this object, as a size_t.
.empty()
Return true iff this object has no Events.
[size_t]
Given a zero-based index, return the corresponding Event by constant reference. If the index is out of range, throw a range_error (not a runtime_error), including the erroneous index and the number of Events in this Schedule.
For a Schedule s, a zero index must return the Event with the earliest date, s[s.size()-1] must return the Event with the last date, and the Events in between must be in nondescending order. It is not guaranteed that the subscript operator will be called in any particular order.

Non-methods                

ostream << Event
Write this Event to the ostream in YYYY-MM-DD format. Write exactly ten characters–nothing else.
ostream << Schedule
Write all the Events in this Schedule to the ostream in YYYY-MM-DD format, each followed by a newline. Write exactly eleven characters per Event–nothing else. They must be written in chronological order, oldest events first, as described in the [] operator.

For both of the output operators above, use of I/O manipulators such as setw or showpos may add extra characters, and hex will break it entirely. That’s ok.                 

Const-correctness, both arguments & methods, is your job. For example, it must be possible to call .size() on a const Schedule, or to pass a const string to the Schedule constructor.                 

You may define other methods, data, or classes, public or private, as you see fit. You may create other source & header files, but we will only #include "Schedule.h" to use a class Schedule, and #include "Event.h" to use a class Event. Note that it is possible to manipulate an Event without a Schedule, and it’s also possible to manipulate a Schedule without dealing with Events.                 

Debugging                

If you encounter “STACK FRAME LINK OVERFLOW”, then try this:

    export STACK_FRAME_LINK_OVERRIDE=ffff-ad921d60486366258809553a3db49a4a

Testing                

You will have to write a main() function to test your code. Put it in a separate file, and do not make it part of libhw5.a. We will test your program by doing something like this:                 

    mkdir a-new-directory
    cd the-new-directory
    tar -x < /some/where/else/hw5.tar
    cmake . && make
    cp /some/other/place/test-program.cc .@@
    g++ -Wall test-program.cc libhw5.a
    ./a.out

We will supply a main() program to do the testing that we want. You should do something similar. I don’t mind if your CMakeLists.txt builds your test program, as mine does. However, if it does, then be sure to include the source to your test program in hw5.tar, or your build will fail.                 

Sample Run                

Here is a sample run, where % is my shell prompt:

% cat CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(Homework)

# Using -Wall is required:
add_compile_options(-Wall)

# These compile flags are highly recommended, but not required:
add_compile_options(-Wextra -Wpedantic)

# Optional super-strict mode:
add_compile_options(-fmessage-length=80 -fno-diagnostics-show-option)
add_compile_options(-fstack-protector-all -g -O3 -std=c++14 -Walloc-zero)
add_compile_options(-Walloca -Wctor-dtor-privacy -Wduplicated-cond)
add_compile_options(-Wduplicated-branches -Werror -Wfatal-errors -Winit-self)
add_compile_options(-Wlogical-op -Wold-style-cast -Wshadow)
add_compile_options(-Wunused-const-variable=1 -Wzero-as-null-pointer-constant)

# add_compile_options must be BEFORE add_executable or add_library.

add_library(hw5 Event.cc Schedule.cc translate.cc)
add_executable(test test.cc)
target_link_libraries(test hw5)

# Create a tar file every time:
add_custom_target(hw5.tar ALL COMMAND tar cf hw5.tar Event.cc Event.h Schedule.cc Schedule.h translate.cc test.cc CMakeLists.txt)

% cat test.cc
#include "Schedule.h"
#include "Event.h"
#include "Schedule.h"       // I meant to do that.
#include "Event.h"
#include <exception>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>

using namespace std;

int main() {
    cout << boolalpha;  // need this for several things
    Schedule s;
    ifstream in("data");
    try {
        s.read(in);
    }
    catch (const exception &e) {
        cout << "Caught: " << e.what() << '\n';
    }
    // That stream should now be failed, but not at eof:
    cout << "fail: " << in.fail() << " eof: " << in.eof() << '\n';

    stringstream iss("   tomoRRow  ");
    s.read(iss);
    auto e = s[0];
    cout << "Oldest: |" << e << "|"
         << " year=" << e.year() << " month=" << e.month()
         << " day=" << e.day() << '\n';
    cout << "size=" << s.size() << " empty=" << s.empty() << '\n' << s;
    s.clear();
    cout << "size=" << s.size() << " empty=" << s.empty() << '\n' << s;
    s.clear();
    cout << "size=" << s.size() << " empty=" << s.empty() << '\n' << s;

    // A poor implementation might have altered cout’s fill character:
    cout << left << setw(15) << "All done." << '\n';

    return 0;
}

% cat data
today            yEsTeRdAy
     000000000008.254
2020-01-01 trout 2020.366

% cmake .
… cmake output appears here …
% make
… make output appears here …
% ./test
Caught: Bad date “trout”
fail: true eof: false
Oldest: |0008-09-10| year=8 month=9 day=10
size=5 empty=false
0008-09-10
2020-01-01
2024-04-23
2024-04-24
2024-04-25
size=0 empty=true
size=0 empty=true
All done.      

Requirements                

If you have any questions about the requirements, ask. In the real world, your programming tasks will almost always be vague and incompletely specified. Same here.                 

Tar file                

    cmake . && make

How to submit your work:                

Use web checkin, or Linux checkin:                 

    ~cs253/bin/checkin HW5 hw5.tar

How to receive negative points:                

Turn in someone else’s work.