Project 1

Introduction and Visitor Pattern

The purpose of this project is to introduce concepts and tools that will be used throughout the quarter in later projects, specifically the debugger and the visitor design pattern.

Specification and Description Changelog

A list of all the changes made to this project description is below:

  • No changes yet.

Files

You can find the provided code files required for step 3 of the project in p1.zip. You will modify the files main.cpp, sum.cpp, max.hpp, and max.cpp. All other files do not need to be modified.

You can run your program on all the tests with make run. The expected outputs are provided in the file output.txt.

If you encounter any mysterious behaviours, such as non-updating executables even after you change the source code, run make clean before running the compile process. In fact, the safest way to go is to always run make clean && make. Always check whether your program is compiling and executing well before turning it in.

Description

This project consists of three steps:

  1. Sign up for the course on Piazza.
  2. Familiarize yourself with GDB (debugger).
  3. Complete two visitors for a integer tree structure.

Step 1: Piazza

We will use Piazza for announcements and course information, as well as student questions. When you have a question that does not include your individual solutions for a project, we ask that you post the question on Piazza so that all other students may see the answer as well. This saves time by preventing questions from being asked repeatedly, and greatly improves question response time.

You should sign up for the course on Piazza now, if you have not already done so. You can enroll at https://piazza.com/ucsb/Spring2022/cs160/home.

Step 2: Introduction to GDB

A debugger will be a very helpful tool throughout the projects for this course. You may encounter many null-pointer errors or other invalid memory access problems, which can be very difficult to resolve without debugging. This step is designed to give a very brief overview of gdb (the Gnu DeBugger) and give you a functional knowledge of how to apply it to C++ programs. If you are already familiar with gdb or another debugger for C++, you may skip this step.

A debugger is a program which allows examination of the state of other programs as they execute. This can be used to inspect their data, control flow, or other properties during a real session. gdb is the standard debugger for Linux operating systems and supports a wide variety of languages such as C++. To demonstrate its usage, consider the following C++ program:

#include <iostream>

void otherFunction() {
	int* x = NULL;
	std::cout << *x << std::endl;
}

int main(int argc, char* argv[]) {
	otherFunction();
	return 0;
}

Notice the segmentation fault on line 5 (the *x dereference).

To debug this program, we first compile it with the "produce debugging output" flag. For the g++ compiler, this flag is -g. An example is:

g++ -g -o gdbtest gdbtest.cpp
Assuming the above C++ program is saved to gdbtest.cpp.

Next, we need to run the debugger and specify that we want to debug the gdbtest executable. You can do this by running the following statement:

gdb ./gdbtest
Which will run gdb specifying gdbtest. Alternatively, you can run gdb with no arguments, then type file ./gdbtest at the gdb prompt to specify the executable.

Now we want to debug the program! You can run the program inside the debugger by typing run at the gdb prompt. This should run the executable and debug it. If we are running an executable that needs input from a file, you can type run < inputfile to run the program with input redirection (we don't need to for this tiny program).

At this point your program should have terminated, and you should see output resembling the following:

Program received signal SIGSEGV, Segmentation fault.
0x000000000000004c in otherFunction () at gdbtest.cpp:5
5			std::cout << *x << std::endl;
Which is telling you that the program received a segmentation fault signal (which makes sense, due to the null-pointer dereference), where the error occurred in your program, and showing that line of code. We now know a lot more about our error, but we're not quite done. Next, type bt at the gdb prompt. This will run a backtrace, which will show you full call stack at this point including all arguments to functions (bt stands for backtrace). You can also print the x variable at this point to see that it is NULL. To do this type p x. p is the print command and is followed by an expression you want to be executed and printed at the current program point.

The last thing we want to do is set a breakpoint. A breakpoint is a place where the debugger will pause the program and return to you so you can decide what to do next (print variables, continue, etc.). To set a breakpoint at the beginning of the otherFunction code, type break otherFunction. Then run the program again by typing run. It may ask you if you want to restart the program from the beginning, to which you should answer yes. Once the program runs, you should see output similar to:

Breakpoint 1, otherFunction () at gdbtest.cpp:4
4		int* x = NULL;
Which is telling you that the program was paused at the beginning of otherFunction on line 4. At this point you can print variables, run backtraces, or do other inspection tasks. When you are ready for the program to continue type c at the prompt. This will tell the debugger to continue running the program normally (c stands for continue). You could have typed s instead, which tells the debugger to run one line of code and then pause before the next line (s stands for step). After you have stepped as many times as you like, you can always continue to keep running the rest of the program.

You can also set breakpoints for specific statements with the form

break sourcefile.cpp:linenum
which will set a breakpoint for that specific line in that specific file. To set the same breakpoint as we set above, you could have done break gdbtest.cpp:4. To clear a breakpoint type clear followed by the same expression you used to set the breakpoint. For example:

clear otherFunction

Finally to quit gdb, type quit at the gdb prompt. If any executables are running it will ask you for confirmation before quitting.

Step 3: Visitor Pattern

The final part of the project is to implement two visitors for a tree data structure. A visitor which prints the tree elements in pre-order has been completely provided for your reference, and you must complete a visitor which sums the elements and write a visitor which finds the maximum element. Some code for the sum visitor has been provided, and you will need to fill in the visitNode function. You need to write the visitor to find the maximum element in its entirety (but you can refer to the other two visitors).

The visitor pattern is a software design pattern which separates the algorithms which you want to run on a structure from the structure itself. You can find a lot of information on the pattern on its Wikipedia page. Another, more in-depth, writeup of the visitor pattern can be found on this page (this example is in Java rather than C++).

To complete this step you should read the provided source code and understand the print visitor and the tree structure and their interaction, and then complete all the sections of code that are missing. These sections are noted by a comment with the text "WRITEME".

The trees you will be working with in this step contain only integer values and the values may be positive or negative.

Requirements

To obtain full credit for this project, you will need to do the following:

  • Join the course on Piazza.
  • Step 3 (visitor) solution must produce correct output for every test case.
  • Step 3 (visitor) solution must compile and run on the CSIL server without errors.

Make sure that you compile, run, and test your program on the CSIL server. Especially if you write your program on your own machine.

Deliverables

You should submit the files main.cpp, sum.cpp, max.hpp, and max.cpp with complete implementation to the GradeScope. (You need to create an account and add the code "RZVDNN" to join this class).

You also must include a README file which includes your name, perm number, email address, any issues with your solution and explanations, and your display name for the course on Piazza (if different from your legal name).

You do not need to submit the test cases, makefile, or other source files.

Grading

Your grade will be based on the proportion of test cases for which your program produces correct output. We have provided these test cases along with the code so that you can be certain of your grade before submission.

You will receive a score of zero if you do not join the course on Piazza before you submit.