CS253: Software Development with C++

Fall 2022

HW 4

CS253 HW4: Document Class                

Changes                

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

Description                

For this assignment, you will write a class called Doc that represents a simple document. This document is a collection of lines of text, indented by varying amounts. Documents can be read from or written to I/O streams, and manipulated programatically.                 

Methods                

Doc must have the following public methods:                

default constructor
The default ctor creates an empty Doc.
Copy constructor
Assignment operator
Copy all information from another object of the same class.
Destructor
Destroy.
.size()
Return a size_t indicating the number of lines in the object.
.empty()
Return true iff the document contains no lines.

The following operators must work:                

istream >> Doc
Read all remaining data in the istream as a document, replacing any existing data. For each line, strip trailing spaces and trailing carriage return ('\r') characters. The newline does not become part of the Doc object. Any remaining leading spaces become the indentation amount for this line. The input line "   x     " must be stored as the number 3 and the string "x". If a tab character ('\t') is read, throw a runtime_error.
Leading & trailing (at the start or the end of the document) empty (after stripping trailing space/return) lines are ignored, and do not participate in any other methods.
ostream << Doc
Write the document to the ostream. The indentation for each line is written as the appropriate number of spaces. Every line is terminated by a newline.
Doc + Doc
Build & return another Doc that contains all the data from the left-hand Doc, followed by all the data from the right-hand Doc. This operator does not modify either argument.
Doc + string
Build & return another Doc that contains all the data from the left-hand Doc, plus the right-hand string. It is assumed that the string doesn’t contain a newline. Treat leading spaces in the string as the indentation, which are not stored as part of the string. The trailing space/return stripping of operator<<, ignoring leading/trailing empty lines, and tab detection, do not apply. This operator does not modify either argument.
string + Doc
Build & return another Doc that contains the left-hand string, plus all the data from the right-hand Doc. It is assumed that the string doesn’t contain a newline. Treat leading spaces in the string as the indentation, which are not stored as part of the string. The trailing space/return stripping of operator<<, ignoring leading/trailing empty lines, and tab detection, do not apply. This operator does not modify either argument.
Doc += Doc
Adds all the data from the right-hand Doc to the end of the left-hand Doc. Return a reference to the left-hand object. The right-hand argument is not modified.
Doc += string
Add a line to the end of the Doc. It is assumed that the line doesn’t contain a newline. Treat leading spaces as the indentation, which are not stored as part of the string. The trailing space/return stripping of operator<<, ignoring leading/trailing empty lines, and tab detection, do not apply. The right-hand argument is not modified.
Doc[size_t]
Return a struct object that represents a given line of the Doc. The first line is line zero. The struct object contains two fields: a size_t indent which contains the indentation of the line, and a std::string data which contains the data for the line, without the indentation. If the number given is too large to indicate a line, throw an out_of_range error mentioning the bad line number and how many lines the object has.
Doc in a boolean context
Evaluating a Doc in a boolean context returns false iff the Doc contains no lines.

The types and names in the method & operator descriptions, above, do not determine the C++ declarations of those functions. They only serve to informally describe what sort of arguments a method might take. You might pass certain arguments by reference, use const, declare return types, etc.                 

Const-correctness, for arguments, methods, and operators, is your job. For example, it must be possible to call .size() on a const object, or to copy a const object to a non-const object.                 

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 Doc class, the user need only #include "Doc.h", not any other header files.                 

Hints                

Dynamic memory (new, new[], delete, delete[]) is not allowed. Really.                 

Debugging                

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

    export STACK_FRAME_LINK_OVERRIDE=ffff-ad921d60486366258809553a3db49a4a

Libraries                

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

To be explicit, the provided CMakeLists.txt does:

The tar file must contain at least all the files required to do this.                 

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

    mkdir a-new-directory
    cd the-new-directory
    tar -x </some/where/else/hw4.tar
    cmake . && make
    cp /some/other/place/test-program.cc .
    g++ -Wall test-program.cc libhw4.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 hw4.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.                 

This is the Colorado State University CS253 web page https://www.cs.colostate.edu/~cs253/Fall22/HW4 fetched by unknown <unknown> with Linux UID 65535 at 2024-04-25T17:22:40 from IP address 3.135.216.174. Registered CSU students are permitted to copy this web page for personal use, but it is forbidden to repost the information from this web page to the internet. Doing so is a violation of the rules in the CS253 syllabus, will be considered cheating, and will get you an F in CS253.

Sample Run                

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

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

# Are we in the wrong directory?
if (CMAKE_SOURCE_DIR MATCHES "[Hh][Ww]([0-9])$")
    if (NOT 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
    -Wconversion -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} Doc.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 "Doc.h"
#include <cassert>
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main() {
    ofstream("data") << "\r \r  \n\n Peter  \n   Ray\r \nEgon  \n \n\r\n\n";
    Doc d;
    assert(d.empty() && !d);
    ifstream in("data");
    in >> d;
    assert(d && !d.empty());
    assert(d.size()==3);
    assert(d[0].indent == 1);
    assert(d[1].indent == 3);
    assert(d[2].indent == 0);
    assert(d[0].data == "Peter");
    assert(d[1].data == "Ray");
    assert(d[2].data == "Egon");

    d += "      Winston     ";
    assert(d.size() == 4);
    assert(d[3].indent == 6);
    assert(d[3].data == "Winston     ");

    for (size_t n=0; n<d.size(); n++) {
        const auto stuff = d[n];
        cout << n << ": " << stuff.indent << ' ' << stuff.data << '\n';
    }
    cout << "---\n" << d << "---\n";

    d += "";
    assert(d.size() == 5);
    assert(d[4].indent == 0);
    assert(d[4].data == "");

    const Doc vacant;
    assert(vacant.empty() && !vacant && vacant.size()==0);
    d = vacant;
    assert(d.empty() && !d && d.size()==0);

    cout << d << "---\nDone.\n";

    return 0;
}
% ./test
0: 1 Peter
1: 3 Ray
2: 0 Egon
3: 6 Winston     
---
 Peter
   Ray
Egon
      Winston     
---
---
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:                

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

How to receive negative points:                

Turn in someone else’s work.