“Assignment 0” is not a real assignment; you don’t get any points for completing it (because I’m going to give you the complete solution in class), which also means you don’t lose any points for not doing it. Still, it may be helpful for you to do it, as it introduces a lot of things that will be common to all the future assignments.
Starting out
Every assignment must be “submitted” on the FCCSCI server, in a subdirectory
of your home directory named cs133/assignN
, where N is the assignment
number. (Note that both cs133
and assign
are all in lower-case.) So the
first thing we need to do is create this directory. Run these commands after you
login to the server:
mkdir -p ~/cs133/assign0
cd ~/cs133/assign0
Each time we log back into the server to work on assignment 0, we’ll run the command
cd ~/cs133/assign0
which will place us in the assign0
subdirectory, ready to work on the
assignment.
Like many assignments, this one has a template source code file you can start with. You can copy this file into the above directory with this command:
cp /usr/local/class/src/cs133/bag.hpp ~/cs133/assign0
You only need to do this once at the beginning, to start the assignment. After that, we want to edit this file, so we’ll run the command
micro bag.hpp
to open the source file bag.hpp
in the Micro editor. At this point, we can
start writing our code.
You can also download a copy of the bag.hpp
file here.
The Bag data structure
The topic of assignment 0 (and of the first few lectures) is to review some core C++ concepts by implementing a “bag” data structure. A “bag” is a things which stores some values such that values can be added and removed, and you can loop over all the values currently in the bag. The bag can store duplicates (you can insert the same value more than once). What makes a bag interesting is not so much what it does, but what it doesn’t do: a bag does not guaranteed that the order of the values will stay the same as things are added and removed.
For example, suppose we create a bag (of capacity 10) and then insert the values 1, 2, 3 into it, in that order. The C++ code to do this with our not-yet-written bag class is
bag b(10); // Capacity = 10
b.insert(1);
b.insert(2);
b.insert(3);
If we now loop over the contents of the bag, we will get out the values
1, 2, 3, but not necessarily in that order. They might come out in any
order. The bag
class is not required to preserve the order the values were
inserted in.
Contents of bag.hpp
The bag
class we will be implementing looks like this (this is the contents
of the bag.hpp
file):
#pragma once
/*
* bag.hpp
* Definition of the `bag` class.
*/
class bag {
public:
/* bag(capacity)
Constructs a bag with the given capacity (max. size).
*/
bag(int c)
{
// Your code here
}
/* size()
Returns the current size (number of elements) in the bag.
NOTE: The minimum possible size is 0, the maximum is the capacity of the
bag.
*/
int size()
{
// Your code here (replace the return)
return 0;
}
/* capacity()
Returns the capacity (max. size) of the bag.
NOTE: Capacity is set at creation time and never changes after that.
*/
int capacity()
{
// Your code here (replace the return)
return 0;
}
/* empty()
Returns true if the bag is empty (size is 0).
*/
bool empty()
{
// Your code here (replace the return)
return false;
}
/* full()
Returns true if the bag is full (size equals capacity).
*/
bool full()
{
// Your code here (replace the return)
return false;
}
/* at(i)
Returns the value of the i-th (index) element of the bag. i must be
between 0 and size()-1, inclusive. If i is out of range, the function
should return -10000000.
*/
int at(int i)
{
// Your code here (replace the return)
return -10000000;
}
/* exists(x)
Returns true if the value `x` exists in the bag.
NOTE: `x` is a *value*, not an *index*.
*/
bool exists(int x)
{
// Your code here (replace the return)
return false;
}
/* find(x)
Returns the index at which the value `x` exists in the bag, if it
exists. If `x` does not exist, returns -1.
*/
int find(int x)
{
// Your code here (replace the return)
return -1;
}
/* insert(x)
Adds `x` to the bag, unless the bag is full (if the bag is full, then
`insert(x)` should do nothing).
*/
void insert(int x)
{
// Your code here
}
/* erase(i)
Erase the value in the bag at index `i`. If i is out-of-range, then
`erase(i)` should do nothing.
*/
void erase(int i)
{
// Your code here
}
/* remove(x)
Remove the *value* `x` from the bag, if it exists. If `x` does not
exist in the bag, then `remove(x)` should do nothing. If there is
more than one copy of `x` in the bag, then only one should be removed.
*/
void remove(int x)
{
// Your code here
}
private:
// Add whatever private members you need
};
You can find most of the details about how to implement the bag data structure in the lecture notes from the first two days of class.
Tracking changes in Git
Git is a version control system; it tracks different versions of a
project’s source code files as they change. If you have a directory
containing cpp
and hpp
source files, you can create a Git repository
for that directory, and then every time you make a change, create a commit in Git. Later, you can view the complete history of all your commits, go back to any previous commit (no more commenting out code because you “might need it later”), etc.
All assignments are required to have their history tracked in Git; this is how I know that you actually wrote the assignment. When you start a new assignment, one of the first things you should do is create a new Git repository to track your work:
cd ~/cs133/assign0
git init
Then, whenever we add a file or make a change to our program, we want to do
git add bag.hpp
git commit -m "Message"
For the git add
command, list the name(s) of the files you added or changed.
You can also just do git add "*.[hc]pp
to add all hpp/cpp files. git commit
saves the current state of these files, along with a message describing what’s
happening. Generally speaking, you should make a commit every time you do
“something”: finish writing a function, pass an additional test, etc.
(One “feature” of Git that sometimes surprises people is that you have to
re-add
files every time you do a commit
. If you do a git commit
without
doing a previous git add
it will just complain that your commit is empty.)
Git is often associated with GitHub, but we’re not using GitHub here. Your assignment history is kept entirely local on the FCCSCI server. Thus you don’t need to worry about creating a GitHub account or pushing to remote, etc.
Remember: assignments without Git history will receive 0 credit, and you cannot add it after you’ve finished; you must commit your changes to Git as you work.
Compiling and testing our code
All assignments (except assignment 5) include a test runner, a provided
.cpp
file
which defines int main()
and tests your code (in the bag.hpp
) to see if
it does what it’s supposed to. This means that in most cases you should not
write a main()
function; one will be provided for you.
You can copy the assignment 0 test-runner source file into the assign0
directory with this command:
cp /usr/local/class/src/cs133/assign0_test.cpp ~/cs133/assign0
Or you can download a copy to your personal computer here.
To test your assignment submission, you have two options:
Run the command
test-assignment
. This will compile and run your code together with the test-runner and report any failures to you. This is how I’ll test your code, so whatevertest-assignment
says is the grade you’ll get.Compile and run the test-runner manually. If you do this, you must be careful to compile and run the test-runner with the same compiler flags and debugging options that the
test-assignment
script uses.
The test-assignment
script compiles all files with the following options:
-g -Werror=vla -Werror=return-type
The -g
flag just adds debugging info to the program, so that if you need to
look at in the debugger, you can. The other two flags turn things that would
normally be compiler warnings into hard errors:
-Werror=vla
makes it an error to use a variable-length array, something likeint x = ...; int arr[x];
. Variable-length arrays are a G++ extension and are not allowed in standard C++.-Werror=return-type
makes it an error for a function with a non-void
return type to “forget” to return a value. For example, normally this functionint f(int x) { if(x < 0) return 12; }
would just print a warning when compiled; for us, this is a hard error.
(You might wonder what
f
returns whenx ≥ 0
: the answer is, whatever the computer feels like.)
When the test-assignment
script runs the test runner, it does two additional
checks to look for memory errors:
It runs the program with
MALLOC_CHECK_=2
set. This enables a few extra runtime checks for memory leaks and the like.It runs the program inside Valgrind; any Valgrind errors or memory leaks cause the assignment to fail.
All assignments except assignment 1 must be memory-safe: it is a fail for an assignment to leak memory, access memory not belong to the program (outside array bounds, etc.), etc.
The test-assignment
script will also check for the presence of a Git history,
and report the assignment as failing if it doesn’t have one.
Obviously, it’s easier to just run test-assignment
than to try to replicate
all that, but if you’re working on your own computer, that’s what you need to
do to make sure the pass/fail results you see are consistent with what I’ll
see when I go my grading.
Summary
If you’re following along in lecture, you should be able to write the bag
class (two different versions of it) in bag.hpp
and then get it to pass
all the tests in the test-runner. And you should have a Git history which
reflects the process of getting the bag
class to the point where it passes
all the tests. (You can view your Git history by running git log
.)