cis3990-25fa (Fall 2025) Home Schedule Assignments Tools & Refs HW 00: Introduction to C++

This assignment serves as both a refresher on core C topics, the unix development environment, and as a way to “dip your toes” into the realm of C++ programming.

Goals

Collaboration

For assignments in CIS 3990, you will complete each of them on your own. However, you may discuss high-level ideas with other students, but any viewing, sharing, copying, or dictating of code is forbidden. If you are worried about whether something violates academic integrity, please post on Ed or contact the instructor(s).

Setup

If you haven’t already, you need to follow the Environment Setup. We recommend you try and figure this out ASAP. This includes setting up a linux environment, setting up and cloning your course github repository, and choosing a text editor to code with.

Once you have your environment setup, that is all that is a necessity, however we will give some suggestions on how to use the environment here:

Refresher on the Terminal and Shell

One of the goals of this course is to continue developing your experience with the shell. We expect that you have used the shell some before in CIS 2400, but we know you may not remember it all, and there is still more to learn than what is covered in that course. If you took CIS 2400 w/ Travis then this section may be familiar.

Most people learn the basics of a shell in school and slowly learn more as needed, but if you want to learn more we recommend two things:

Most computers have some sort of a “Terminal” application that runs a program called a “shell”. The “shell” is a program that prompts the user for a command, runs the command, prints any output and then repeats by asking the user for another command. This makes a shell effectively a REPL (Read Eval Print Loop), but it also supports a lot more features.

A shell’s main job is to provide an interface for users to the system they are interacting with. For us this primarily involves:

Shell programs are somewhat dependent on the system they are implemented for, but this means shell programs for the same system (e.g. bash, zsh, fish, etc.) have a lot in common. This course uses the shell bash and many of what you learn in bash can be applied to shells used in other UNIX-like systems (UNIX-like systems are pretty much everything that is not Windows). bash is one of the most popular shells in existence and getting familiar with it (and thus other UNIX-shells) is very beneifical for most who do any programming.

When you open the shell, you should see something like this:

moya@4d98e91a6924:~/shared$ 

docker_shell

This is the prompt that gets printed to let you know you can type in a command for the shell to execute.

There are a couple parts to this line, lets break this down.

The last bullet point from above is particularly important. The shell has a “Current Directory” similar to how File Explorer or Finder has a notion of “Current Directory” where those programs need to go in and out of directories to see different files. What this means is that when you are interacting with the shell, there is always a directory you are “in” and you can change what directory you are in to interact with different files.

Setting up our files

Since you have cloned your git repository already, some of the files have already been setup for us!

From here we can use another command ls which “Lists” the contents of the current directory. If you cloned your repo correctly you should see a directory with the same name as your repo. After running the ls command your terminal should look something like:

moya@4d98e91a6924:~/shared$ ls
25fa-cis3990-xyz
moya@4d98e91a6924:~/shared$ 

where xyz is instead your github username.

The ls command only gives us information and does not change anything in the system, but is one of the most commonly used commands. It helps to do run ls to remind you of what else is in the directory you are currently working in.

from here we can use the command cd to “Change Directory” to move our current directory into the repo’s directory:

cd 25fa-cis3990-xyz

Note that after you do this, your shell prompt will look different. It will probably look something like:

moya@4d98e91a6924:~/shared/25fa-cis3990-xyz$ 

Note that the path in the prompt changed to include the directory we are now in. If we now create any files, they will show up in this directory.

if we type in ls again then we should be able to see two directories:

moya@4d98e91a6924:~/shared/25fa-cis3990-xyz$ ls
check_setup  cowsay

We need to change into our cowsay directory as that is the current project. Afterwards our shell prompt should look something like this:

moya@4d98e91a6924:~/shared/25fa-cis3990-xyz/cowsay$ 

If you ls from within here you should notice all of the files we provide:

moya@4d98e91a6924:~/shared/25fa-cis3990-xyz/cowsay$ ls
Makefile  catch.cpp  catch.hpp  SimpleString.hpp  test_simple_string.cpp  test_suite.cpp

For this assignment, we will need to create a new file cowsay.cpp. To create a file in the shell we can run the following command:

touch cowsay.cpp

touch creates a file of the specified name if it does not already exist. Note that it does not create directories, that is what mkdir is for.

From here we can use ls to see that you have created the file with the touch command. If you did everything properly, you should see something similar to the below. If something went wrong, see further below where we talk about the mv command.

moya@4d98e91a6924:~/shared/25fa-cis3990-xyz/cowsay$ ls
Makefile  catch.cpp  catch.hpp  cowsay.cpp  SimpleString.hpp  test_simple_string.cpp  test_suite.cpp

Once you have done the above steps to create our cowsay.cpp you must run the following commands to download the starter files for the project.

From here you can start working the assignment by opening the files we just created with vim, VSCode or another editor if you have one you prefer.

More Basic Shell Commands (Optional)

If you want to rename a file (because you accidentally spelt it wrong) or you want to move it to another directory, you can use the mv command which stands for ““Move”. For example, if I wanted to rename the file example.txt to renamed.txt (assuming that example.txt already exists) then I could type:

mv example.txt renamed.txt

Similar to the mv command there is the cp command for “Copying” a file. If I wanted to make a copy of a file I would do: If I wanted to rename example.txt to copied_example.txt, I would do:

cp example.txt copied_example.txt

Lastly, we should let you know about . and .. (referred to as “dot” and “dot dot” in lecture). These two are “special” names that change based off of which directoy you are currently in.

. (dot) refers to the current directory, so the following two names refer to the same file: ./hello.txt and hello.txt.

.. (dot dot) refers to the directory that is “above” or the “parent” of the current directory (The current directory is held within its “parent” directory). This can be used to leave a directory that we have entered. For example, if i have a directory called: intro, I can run: cd intro to enter that directory and later type cd .. to leave it and go “up” a level in the directory layout.

For another example, lets say I am in a directory called “example” that has the file blah.txt. I can refer to that file with ../example/blah.txt ./blah.txt or blah.txt and all three would be the same.

More information on the shell that is likely to be useful:

Lastly, we have a quick reference on some information you may find useful when using the terminal. Feel free to come back to it at any time: bash-reference

Instructions

Once you have followed the setup instructions, you should have a folder that contains the files for this assignment.

Required Knowledge

This homework has you writing a little bit of C++ code from scratch. For now this should be very similiar to code you have written before in C, but with only a few minor differences. The differences you need to know should have been covered in the first lecture or two. These being:

For this assignment, you will be writing some code to implement a string struct (which it turns out isn’t that far from being a proper C++ object) and slightly simplified version of the shell command cowsay, but in C++.

SimpleString

For the first part of the assignment you will have to implement a simpel string “object” in C++. An important goal of this class is gaining a better understanding of how the computer works, and part of that is trying to build a better understanding for how fundamental types (e.g. string’s) work! This also should be on the lighter side for workload :)

This “object” still follows a lot of C style conventions, so you should not follow everything done in this section as “good practice”. This assignment is designed this way to help people refresh on C and transition into C++. Knowing the fundamentals of C is important for understanding C++, but don’t worry if it takes some time to figure this out!

For this part of the assignment we suggest you start by opening SimpleString.hpp and reading the code and comments. Once you have familiarized yourself with what is going on, you should open the empty SimpleString.cpp you created earlier in the assignment and provide the implementation for every function descrived in the SimpleString.hpp header files. Your cpp file should #include the corresponding hpp file. If you need advice for how to layout your file, you should check some of the provided lecture code.

cowsay

Your second task is to write a C++ program that acts like the shell command cowsay!

We aren’t going to give you too many details on what cowsay does, this is something we want you to figure out!

You may need to run the following command to install cowsay

sudo apt install cowsay

If it asks you for a password, try typing in systemsRcool. It may not show the password as you type it, that is normal.

You can then run the command to see what it does! Try running:

cowsay hello how are you?

If for some reason it is not working for you. instead of cowsay try running /usr/games/cowsay

Your job is to write your cowsay.cpp to replicate some of the behaviour of the cowsay command.

To make your life a little easier, you can work with the following assumptions:

In lecture we showcased how to use command line args if you need a refresh on that.

You are not required to make use of your SimpleString for this part of the assignment. You can use it if you like, though some find it a little clunky, especially since it isn’t quite a proper “string object” yet. If you want to use it, you should check how we include header files in some of the provided lecture code and do something similar for SimpleString.hpp. You will also need to modify the makefile to test your code. See the compilation section below for how to do this.

Your code should compile to an executable called cowsay so that you can run it like: ./cowsay.

Running the command ./cowsay hello how are you should do the same as running the shell command cowsay hello how are you.

Other requirements:

printing in C++

You will need to do some printing in this code, and since we are writing C++, you should print like we do in C++. We may not have talked about it in lecture, but to print a string in C++ we just do:

// prints "VALID" to the terminal
cout << "VALID" << endl;

You can also print numbers and characters this way if it would help with debugging:

char c = 'A';
// prints "A" to the terminal
cout << c << endl;

We also provided a function for you so that you can print SimpleString “objects”

SimpleString str = from_cstring("Hello!");
// prints "Hello!" to the terminal
cout << str << endl;

Just add this code to your SimpleString.cpp file:

std::ostream& operator<<(std::ostream& os, const SimpleString to_print) {
  os << to_print.data;
  return os;
}

Allowed / suggested functions & headers

For this assingment you are allowed to write any helper functions you need, but you are restricted to using the following headers and the following functions. You do not need to use all of these, do what you think would be best for your code. If you do not see a function listed that you think should be ok to use, please ask and we can allow it or disallow it.

You are also allowed to use SimpleString and any functions you write yourselves.

Compilation

Header Files and Header Guards

For this assignment we gave you SimpleString.hpp, which is a header file. Header files contain the declarations of types and declarations of functions that are defined in the corresponding cpp file. If some other file wanted to use our SimpleString code, they would need to “import” our code by doing #include "SimpleString.hpp" to see what functions and types there are. Later in the compilation process is when all the .cpp files are combined and can see the definitions for everything.

However how “importing” modules in C++ works is rather outdated and the proper C++ “import” statements are still being developed. This means we need to add something to our .hpp files to make sure they are not “included” multiple times as this will cause compilation issues. This thing we add is called header guards. How they work is that at the top of your hpp file you need to do something like:

#ifndef MY_FILE_HPP_
#define MY_FILE_HPP_

then at the bottom of the hpp file you would write something like:

#endif  // MY_FILE_HPP_

The above examples are what you would write for MyFile.hpp or my_file.hpp. You can see an example of this in some of the lecture code.

For your code to get full credit, you will need to add header guards like this to your SimpleString.hpp.

Makefile

You must also modify the provided Makefile to compile SimpleString.o and compile your cowsay.cpp into a program called cowsay.

We provide a lof of the makefile for you, and you will also note that the makefile includes steps for building test_simple_string.o, catch.o and test_suite.o. It then uses these .o files to create an executable called test_suite.

You should use what we do in the Makefile with building catch.cpp into catch.o and using it to make SimpleString.o and cowsay.o.

You should also then compile your cowsay.o into an executable called cowsay (similar to what we did for compiling test_suite from test_suite.o).

This is not as hard as it may sound! It may look scary, but you can do it!

Here is everything you need to add to the makefile. Some of it is repeated, but this should help:

Testing your code

Valgrind

You need to test your submission on whether there are any memory errors or memory leaks. We will be using valgrind to do this. To do this, you should try running: valgrind --leak-check=full ./cowsay I am a cow MOOOOOOOOOOOOOOOOO

You should also test your string code by running: valgrind --leak-check=full ./test_suite

If everything is correct, you should see the following towards the bottom of the output:

 ==1620== All heap blocks were freed -- no leaks are possible
 ==1620==
 ==1620== For counts of detected and suppressed errors, rerun with: -v
 ==1620== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

If you do not see something similar to the above in your output, valgrind will have printed out details about where the errors and memory leaks occurred. We went over how to read valgrind errors some time in recitation or lecture in the first or second week.

Testing

Testing your SimpleString with catch2

As with mentioned above, you can compile the your implementation by using the make command. This will result in several output files, including an executable called test_suite that we are using to test your SimpleString implementation.

After compiling your solution with make, You can run all of the tests for the homwork by invoking:

$ ./test_suite

You can also run only specific tests by passing command line arguments into test_suite

For example, to only run the tests on the core operations of the hash table (inserting, finding, and removing) you can type in:

$ ./test_suite from_cstring

You can specify which tests are run for any of the tests in the assingment. You just need to know the names of the tests, and you can do this by running:

$ ./test_suite --list-tests

These settings can be helpful for debugging specific parts of the assignment, especially since test_suite can be run with these settings through valgrind and gdb!

Testing your cowsay

Your code should compile to an executable called cowsay so that you can run it like: ./cowsay.

To test your cowsay locally, all you need to do is compare it’s output to the real cowsay program, as long as the command line args follow the assumptions we setup for you (e.g. no word that is 40 characters or more).

Running the command ./cowsay hello how are you should do the same as running the shell command cowsay hello how are you.

Test your cowsay on various other inputs and try to match the real cowsay!

Clang Format and Clang Tidy

The makefile we provided with this assignment is configured to help make sure your code is properly formatted and follows good (modern) C++ coding conventions.

To do this, we make use of two tools: clang-format and clang-tidy

To make sure your code identation is nice, we have clang-format. All you need to do to use this utility, is to run make format, which will run the tool and indent your code propely. Code that is turned in is expected to follow the specified style, code that is ill-formated must be fixed and re-submitted.

clang-tidy is a more complicated tool. Part of it is checking for style and readability issues but that is not all it does. Examples of readability issues include:

Not using curly braces around if statements and loops:

if (condition)  // clang-tidy will complain about missing curly braces
  cout << "hello!" << endl;

Declaring variables or parmaters with names that are too short:

void foo(char c) { // clang-tidy will complain about the name `c`
  // does something
}

Having functions that are too complex and long. The tool calculates “cognitive complexity” of your code and will complain about anything that is too complex. This means you should think about how to break your code into helpers, because if you don’t, clang-tidy will complain and you will face a deduction. More on this specific error can be found here: Cognitive Complexity

clang-tidy is also useful for noticing some memory errors and pointing out bad practices when writing C++ code. Because of all this, we are enforcing that your code does not produce any clang-tidy errors. You can run clang-tidy on your code by running: make tidy-check. Whenever you compile your code using make then it should also re-run clang-tidy to check your code for errors. Note that you will have to fix any compiler errors before clang-tidy will run (and be useful).

If you have any questions about understanding an error, please ask on Ed discussion and we will happily assist you.

Submission

Please submit all of your completed files Makefile, SimpleString.cpp SimpleString.hpp and cowsay.cpp to Gradescope. Note that we expect you to submit through your github repository, and we expect your files to be within the cowsay directory of your github repository.