CS253: Software Development with C++

Spring 2021

HW 7

CS253 HW7: Looping!                

They’re Froot Loops. Loops—get it?

Changes                

Comparing iterators with different rejection strings is undefined behavior.                 

Description                

For this final assignment, you will improve on your HW5 class, adding iteration. That is, this must work:                 

Words w("Belize Costa\\ Rica Panama");
for (auto p : w)
    cout << p.first << (p.second ? " was escaped\n" : " was not escaped\n");

This implies the existence of the type Words::iterator, and the methods Words::begin() and Words::end().                 

Methods and operations                

The following methods & operators must work, where wit is of type Words::iterator.                 

Words::begin()
Returns a value of type Words::iterator that corresponds to the first word/escaped pair in the Words that is allowed by .reject().
Words::end()
Returns a value of type Words::iterator that corresponds to one past the last word/escaped pair allowed by .reject() in the Words. Past, I say! It does not correspond to the last item, since begin() & end() form a half-open interval.
Words::reject(string)
Set the rejection string. If not called, then no words are rejected.
++wit
wit++
--wit
wit--
Increments or decrements the iterator, going to the next/previous word, skipping those rejected by .reject(). Preincrement/predecrement return the new iterator value, and postincrement/postdecrement return the previous value, in the same manner as ++ and -- work on integers.
*wit
Yields, by value, the same sort of word/escaped pair that Words::operator[] returns. If .reject() is in effect for this iterator, then words containing any characters from the given string are skipped.
wit == wit
wit != wit
Compares two iterators for equality or inequality.
copy, assignment
Iterators are copy-constructable, and assignable. Copying/assigning preserves all state of the iterator, including current position and what it rejects.

The types and names in the descriptions, above, do not determine the C++ declarations of those methods. They only serve to informally describe what sort of arguments a method might take.                 

Const-correctness, for methods, arguments, and operators, is your job. For example, it must be possible call .begin() on a const Words objects.                 

You may define other methods or data, public or private, as you see fit. You may define other classes, as you see fit. However, to use the Words class, the user need only #include "Words.h", not any other header files.                 

Rejection                

Rejection of certain word/escaped pairs is controlled by .reject(). When an iterator is created via .begin() or .end(), the most recent argument from .reject() is passed along to the iterator. That iterator will ignore string/bool pairs that contain any character from the .reject() argument.                 

Changing the rejection string for a Words object does not change the rejection strings for any iterators that already exist.                 

This is a fine opportunity for you to learn about the various string methods, as opposed to doing the string-searching work yourself.                 

Iterator Invalidation                

A Words::iterator can be invalidated by:

Debugging                

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

    export STACK_FRAME_LINK_OVERRIDE=ffff-ad921d60486366258809553a3db49a4a

Sample Run                

This focuses on the features added in this assignment. This does not imply that the previous features are abandoned.                 



















































% cat CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project(hw7)

# Are we in the wrong directory?
if(CMAKE_SOURCE_DIR MATCHES "[Hh][Ww]([0-9])$")
   if(PROJECT_NAME MATCHES "[^${CMAKE_MATCH_1}]$")
      message(FATAL_ERROR "Building ${PROJECT_NAME} in ${CMAKE_SOURCE_DIR}")
   endif()
endif()

# 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
    -fstack-protector-all -g -O3 -std=c++17 -Walloc-zero -Walloca
    -Wctor-dtor-privacy -Wduplicated-cond -Wduplicated-branches
    -Werror -Wextra-semi -Wfatal-errors -Winit-self -Wlogical-op
    -Wold-style-cast -Wshadow -Wunused-const-variable=1
    -Wzero-as-null-pointer-constant)

# add_compile_options must be BEFORE add_executable.

# Create the executable from the source file main.cc:
add_library(${PROJECT_NAME} Words.cc)
add_executable(test test.cc)
target_link_libraries(test ${PROJECT_NAME})

# Create a tar file every time:
add_custom_target(${PROJECT_NAME}.tar ALL COMMAND
    tar -cf ${PROJECT_NAME}.tar *.cc *.h CMakeLists.txt)
% cmake . && make
… cmake output appears here …
… make output appears here …
Scanning dependencies of target test
[ 75%] Building CXX object CMakeFiles/test.dir/test.cc.o
[100%] Linking CXX executable test
[100%] Built target test
% cat test.cc
#include "Words.h"
#include "Words.h"      // I meant to do that.
#include <iostream>
#include <cassert>

using namespace std;

int main() {
    const Words a(R"(Raiders Temple\ of\ Doom Last\ Crusade)");
    cout << "Normal:\n";
    for (auto [word, esc] : a)
        cout << '\t' << word << (esc ? " (escaped)\n" : "\n");

    cout << "Reverse:\n";
    const Words::iterator beg = a.begin();
    for (Words::iterator it = a.end(); beg != it; ) {
        it--;
        auto p = *it;
        cout << '\t' << p.first << '\n';
    }

    Words b(a);
    b.reject("D");
    cout << "Without D:\n";
    for (auto [word, esc] : b)
        cout << '\t' << word << '\n';

    cout << "Without ca%b\n";
    b.reject("ca%b");
    for (auto [word, esc] : b)
        cout << '\t' << word << '\n';

    cout << "Without 123:\n";
    b.reject("123");
    for (auto [word, esc] : b)
        cout << '\t' << word << '\n';

    cout << "All:\n";
    b.reject("");
    auto begin_it = b.begin(), end_it = b.end();  // We like everything!
    // This .reject() will NOT affect the iterators begin_it & end_it:
    b.reject("aeiouy");
    for (auto it=begin_it; it!=end_it; ++it)
        cout << '\t' << (*it).first << '\n';      // Should really use ->

    return 0;
}
% ./test
Normal:
        Raiders
        Temple of Doom (escaped)
        Last Crusade (escaped)
Reverse:
        Last Crusade
        Temple of Doom
        Raiders
Without D:
        Raiders
        Last Crusade
Without ca%b
        Temple of Doom
Without 123:
        Raiders
        Temple of Doom
        Last Crusade
All:
        Raiders
        Temple of Doom
        Last Crusade

Libraries                

libhw7.a is a library file. It contains a number of *.o (object) files. It must contain Words.o, but it may also contain whatever other *.o files you need. The CMakeLists.txt shown creates libhw7.a. It does not contain main().                 

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 libhw7.a. Particularly, do not put main() in Words.h or Words.cc. You will also have to create Words.h, and put it into hw7.tar. We will test your program by doing something like this:                 

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

We will supply a main program to do the testing that we want. You should do something similar. It’s your choice whether to include your test program in your hw7.tar file. However, cmake . && make must work. If it fails because you didn’t package test.cc, but your CMakeLists.txt requires test.cc, then your build failed, and you get no points. Test your tar file, not just your code.                 

Requirements                

The HW5 requirements all apply, plus:

Tar file                

    cmake . && make

How to submit your work:                

In Canvas, check in the file hw7.tar to the assignment “HW7”. It’s due 10:00:00ᴘᴍ MT Saturday, with a 24-hour late period for a 25% penalty.                 

How to receive negative points:                

Turn in someone else’s work.