CS253: Software Development with C++

Spring 2021

HW 5

CS253 HW5: Operations!                

Changes                

Updates to the assignment will be noted here. None yet!                 

Description                

Surprisingly, management is delighted with your work in HW3. Unsurprisingly, they want more. One could argue that they’ve gone way overboard, but that’s management. For this assignment, you will improve your class Words from HW3, adding several operations.                 

Methods                

Words must have all the required methods (including no default ctor) and operations from HW3, and:                 

Words + Words
Combine the words from the two objects. For example, Words("a dog") + Words("is your\\ friend") will yield a Words object containing the same four words, and the same escaped flags, in the same order, that Words("a dog is your\\ friend") would produce. This operation must not change either of its operands.
Words += Words
Add all the words in the second object to the end of the first object. For example, after Words w("a b"), x("c d"); w += x; the object w would contain the four words a, b, c, and d, in that order. The escapedness of the words are also copied. This operation must not change the second object. This operation must return a reference to the first object.
Words == Words
Return true iff the two objects contain exactly the same words, in the same order. Escapedness does not matter.
Words != Words
Return false iff the two objects contain exactly the same words, in the same order. Escapedness does not matter.
Boolean context
Evaluating a Words object in boolean context will return true iff its .size() is more than zero.
predecrement
Predecrementing a Words makes each of its words shorter, by removing the last byte. A string that was already zero-length will be removed from the object. Escapedness must be preserved, even if the character that was escaped is deleted. A reference to the modified object is returned.
postdecrement
Has the same effect on the object as predecrement, but returns the old value (by value), not the new value.
Words[size_t]
The subscript operation must return a struct that contains the two values returned by .get(), given the same index. The struct must have a .first member that contains the string for the word, and .second member that contains true iff the word was escaped.
If the index is out of range, throw an std::out_of_range error, whose message must contain the erroneous index, and the .size() of the Words object.

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 arguments and operators, is your job. For example, it must be possible use + on two const Words objects, to use [] on a const Words.                 

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.                 

Debugging                

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

    export STACK_FRAME_LINK_OVERRIDE=ffff-ad921d60486366258809553a3db49a4a

Sample Run                

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

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

# 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 …
% cat test.cc
#include "Words.h"
#include "Words.h"      // I meant to do that.
#include <iostream>
#include <cassert>

using namespace std;

int main() {
    Words a("");
    assert(a.size() == 0);
    assert(a.empty());
    assert(!a);
    a.analyze("Applejack Pinkie\\ Pie");
    assert(a.size() == 2);
    assert(a);
    auto p = a[0];
    assert(p.first == "Applejack" && !p.second);
    p = a[1];
    assert(p.first == "Pinkie Pie" && p.second);


    assert(a == Words("\v\\Applejack\nPinkie\\ Pie \f\t\r"));
    assert(a != Words("Applejack Pinkie\\ Pie ajbaxah"));
    assert(!(a != Words("Applejack Pinkie\\ Pie")));
    assert(!(a == Words("Applejack Pinkie\\ Pie ffff")));

    const Words b(" Fluttershy "), c(" Rarity "), d(b+c);
    assert(b.size() == 1);
    assert(c.size() == 1);
    assert(d.size() == 2);

    Words e(a += d);
    assert(a == e);
    assert(e.size() == 4);
    assert(e[0].first == "Applejack");
    assert(e[1].first == "Pinkie Pie");
    assert(e[2].first == "Fluttershy");
    assert(e[3].first == "Rarity");
    assert(!e[0].second);
    assert( e[1].second);
    assert(!e[2].second);
    assert(!e[3].second);

    while (e) {
        cout << "size=" << e.size() << " ";
        cout << e-- << '\n';
    }

    cout << "Done.\n";

    return 0;
}
% ./test
size=4 Applejack,Pinkie Pie,Fluttershy,Rarity
size=4 Applejac,Pinkie Pi,Fluttersh,Rarit
size=4 Appleja,Pinkie P,Flutters,Rari
size=4 Applej,Pinkie ,Flutter,Rar
size=4 Apple,Pinkie,Flutte,Ra
size=4 Appl,Pinki,Flutt,R
size=4 App,Pink,Flut,
size=3 Ap,Pin,Flu
size=3 A,Pi,Fl
size=3 ,P,F
size=2 ,
Done.

Hints                

Libraries                

libhw5.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 libhw5.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 libhw5.a. Particularly, do not put main() in Words.h or Words.cc. You will also have to create Words.h, and put it into hw5.tar. 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. It’s your choice whether to include your test program in your hw5.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 HW3 requirements all apply.                 

Tar file                

    cmake . && make

How to submit your work:                

In Canvas, check in the file hw5.tar to the assignment “HW5”. 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.