CS253 HW7: Iteration!                
Changes                
It is not defined whether or not calling .set_wanted()
has any
effect on game play. That is, a call to .set_wanted()
might
restrict the words available for .select_target()
, or it might not.
                
Description                
You will add iteration to your work in HW5. Iterating over a
Game
via Game::iterator
or a
for-each loop,
will produce a list of words read from the supplied
dictionaries, filtered by .set_length()
, and additionally filtered
by a user-supplied function. This is a
forward iterator.
                
Methods                
The following methods & operators must work, where git is a
Game::iterator
object:
                
-
Game::set_wanted(wanted)
-
The argument, called the wanted-function, is a function (not a
method) that takes a std::string by value, and returns true
iff the word is to appear in for-each loop
output. If this method is not called on a given
Game
object, then
it is if the default wanted-function always returns true. Each call
to .set_wanted()
replaces any previous calls—the effects are not
cumulative.
Write a wanted-function like this:
// Accept only words that start with “th”:
bool th(string s) {
return s[0]=='t' && s[1]=='h';
}
and use it like this:
Game g;
g.set_wanted(th);
g.add_dict("data177777.txt");
for (string s : g)
cout << s << '\n'; // only words starting with “th”.
-
Game::begin()
-
Return an object of type
Game::iterator
that “points” to the first
word in the dictionaries of the proper length (per .set_length()
or its default value) for which the wanted-function returns true.
-
Game::end()
-
Like
.begin()
, but “points” past the last such qualifying word.
-
++
git -
Increment the iterator, returning the new value.
- git
++
-
Increment the iterator, returning the old value, before it was
incremented.
-
*
git -
Yields a string that corresponds to the iterator’s position
in the words.
- git
==
git -
- git
!=
git -
Compares two iterators for equality or inequality.
Any other comparisons are undefined operations.
- copy, assignment
-
Iterators are copy-constructable, and assignable.
The types and names in the method 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.
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 .begin()
on a
const Game
, use *
on a const iterator
, or to copy a
const iterator
to a non-const iterator
.
                
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
Game
or Game::iterator
classes, the user need only
#include "Game.h"
, not any other header files.
                
Hints                
- A nested (inner) class is not a derived class, and so has no
access to the data or methods of the outer class. That is, a given
Game::iterator
object doesn’t intrinsically know which Game
object it’s associated with.
- Your
Game::iterator
object really should contain a pointer to the
associated Game
object, so it has access to everything in the
Game
object. That way, you won’t be tempted to copy things from
the Game
object into the iterator
object.
Debugging                
If you encounter “STACK FRAME LINK OVERFLOW”, then try this:
export STACK_FRAME_LINK_OVERRIDE=ffff-ad921d60486366258809553a3db49a4a
Libraries                
libhw7.a
is a library file. It contains a number of
*.o
(object) files. It must contain Game.o
, but it may also
contain whatever other *.o
files you need. The CMakeLists.txt
shown creates libhw7.a
. It does not contain main().
                
To be explicit, the provided CMakeLists.txt
does:
- creates
Game.o
by compiling Game.cc
- creates a library
libhw7.a
from Game.o
- creates
test.o
by compiling test.cc
- creates an executable called
test
by linking test.o
with libhw7.a
.
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 libhw7.a
.
Particularly, do not put main() in Game.h
or Game.cc
. You
will also have to create Game.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 -std=c++17 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.
                
This is the Colorado State University CS253 web page
https://cs.colostate.edu/~cs253/Fall22/HW7
fetched by unknown <unknown> with Linux UID 65535
at 2024-08-30T16:20:34 from IP address 18.223.213.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(hw7)
# 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} Game.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 greek.txt
alpha
beta
gamma
delta
epsilon
zeta
eta
theta
iota
kappa
lambda
mu
nu
xi
omicron
pi
rho
sigma
tau
upsilon
phi
chi
psi
omega
% cat test.cc
#include "Game.h"
#include <cassert>
#include <iostream>
using namespace std;
// Reject words containing the letter ‘e’:
bool no_e(string s) {
return s.find('e') == s.npos;
}
int main() {
Game g;
g.add_dict("greek.txt");
for (auto s : g)
cout << "all: " << s << "\n";
g.set_wanted(no_e);
for (auto s : g)
cout << "no e: " << s << "\n";
g.set_wanted([](string s) { return s[0] == 'z';});
for (auto s : g)
cout << "z: " << s << "\n";
g.set_wanted([](string s) { return s[3] == 'm';});
const auto g2=g;
// Should only get gamma and sigma:
for (auto s : g2)
cout << "m: " << s << "\n";
Game::iterator it = g2.begin();
assert(it == g2.begin());
assert(!(it != g2.begin()));
assert(it != g2.end());
assert(!(it == g2.end()));
assert(g2.begin() != g2.end());
assert(*it == "gamma");
assert(*it == "gamma");
assert(*it != "YMP");
assert(!(*it == "ffff"));
auto it2 = it++;
assert(*it2 == "gamma");
assert(*it == "sigma");
it2 = ++it;
assert(it2 == it);
assert(it == g2.end());
cout << "Done!\n";
return 0;
}
% ./test
all: alpha
all: gamma
all: delta
all: theta
all: kappa
all: sigma
all: omega
no e: alpha
no e: gamma
no e: kappa
no e: sigma
m: gamma
m: sigma
Done!
Requirements                
- All requirements from HW5 apply.
- The words must come out of an iterator in the same order
that they were added by
.add_dict
, and in the order inside
each file.
- The words, matching or not, must exist only once in memory, in a
container in the
Game
object. Your iterator must not copy
all of the words, or even just the matching words, into another
vector, array, or any other container. Each word must exist only
once in the Game
object itself, with only a single word or two
copied to individual scalars.
- It must be an compile-time error or warning to ignore the return value
of
.begin()
or .end()
, or the value from *
applied to a Game::iterator
.
- An iterator is invalidated if the associated
Game
object:
- has
.add_dict()
or .set_wanted()
called
- falls out of scope
- is overwritten by assignment
- Undefined behavior results if:
.set_length()
is called after .add_dict()
*
is applied to an .end()
iterator
- an iterator is incremented past the
.end()
value
- an uninitialized iterator is used in any way except assigning to it
- an invalidated iterator is used.
- More than one iterator can be associated with a
Game
object
at the same time. The iterators have independent positions.
Tar file                
- The tar file for this assignment must be called:
hw7.tar
- It must contain:
- source files (
*.cc
), including Game.cc
- header files (
*.h
), including Game.h
CMakeLists.txt
, which will create the library file
libhw7.a
.
- These commands must produce the library lib
hw7.a
:
cmake . && make
- Your
CMakeLists.txt
must use at least -Wall
when compiling.
How to submit your work:                
In Canvas, check in the
file
hw7.tar
to the assignment “HW7”.
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.