├── .github └── workflows │ └── autograders.yml ├── .gitignore ├── .vscode └── settings.json ├── README.md ├── admin.py ├── assignment0 ├── README.md ├── autograder │ ├── autograder.py │ ├── requirements.txt │ ├── utils.cpp │ └── utils.py ├── docs │ ├── autograder.png │ ├── header.png │ ├── mac-zsh.png │ └── vscode-extensions.png └── main.cpp ├── assignment1 ├── README.md ├── autograder │ ├── autograder.py │ ├── courses_not_offered.bin │ ├── courses_offered.bin │ └── utils.py ├── courses.csv ├── docs │ └── autograder.png ├── main.cpp ├── student_output │ ├── courses_not_offered.csv │ └── courses_offered.csv └── utils.cpp ├── assignment2 ├── README.md ├── autograder │ ├── autograder.py │ ├── requirements.txt │ ├── student_output │ │ ├── match.txt │ │ └── set.txt │ ├── utils.hpp │ └── utils.py ├── docs │ └── marriage_pact.png ├── main.cpp ├── short_answer.txt └── students.txt ├── assignment3 ├── README.md ├── autograder │ ├── autograder.py │ ├── requirements.txt │ └── utils.py ├── class.cpp ├── class.h ├── docs │ └── bjarne.jpg ├── main.cpp ├── sandbox.cpp ├── short_answer.txt └── utils.hpp ├── assignment4 ├── README.md ├── autograder │ ├── autograder.py │ ├── gold │ │ ├── (kafka).txt │ │ ├── (marquez).txt │ │ ├── (morrison).txt │ │ ├── gibberish.txt │ │ ├── melville.txt │ │ ├── orwell.txt │ │ └── tolstoy.txt │ ├── utils.hpp │ └── utils.py ├── docs │ ├── header.png │ ├── mispelled.png │ └── spellcheck.png ├── examples │ ├── (kafka).txt │ ├── (marquez).txt │ ├── (morrison).txt │ ├── gibberish.txt │ ├── melville.txt │ ├── orwell.txt │ └── tolstoy.txt ├── main.cpp ├── spellcheck.cpp ├── spellcheck.h ├── utils.cpp └── words.txt ├── assignment5 ├── README.md ├── autograder │ ├── autograder.py │ ├── diagnostics.hpp │ ├── utils.hpp │ └── utils.py ├── docs │ └── logo.jpeg ├── main.cpp ├── user.cpp └── user.h ├── assignment6 ├── README.md ├── autograder │ ├── autograder.py │ ├── courses.csv │ ├── utils.hpp │ └── utils.py └── main.cpp ├── assignment7 ├── README.md ├── autograder │ ├── autograder.py │ ├── diagnostics.hpp │ ├── utils.hpp │ └── utils.py ├── docs │ └── art.png ├── main.cpp ├── short_answer.txt └── unique_ptr.h └── docs └── vscode-extensions.png /.github/workflows/autograders.yml: -------------------------------------------------------------------------------- 1 | # This job updates the autograders on every push to main. 2 | # Autograders are stored in `admin/autograders` in the CS106L AFS directory. 3 | 4 | name: Update Autograders 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | 15 | # This line prevents this action from running in forks of the repository 16 | if: github.repository == 'cs106l/cs106l-assignments' 17 | 18 | steps: 19 | - name: Deploy to AFS 20 | env: 21 | TOKEN: ${{ secrets.DEPLOY_TOKEN }} 22 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | run: | 24 | curl -X POST "https://web.stanford.edu/class/cs106l/cgi-bin/deploy/?name=autograders" \ 25 | -H "Token: $TOKEN" \ 26 | -H "Github-Token: $GH_TOKEN" \ 27 | --fail-with-body 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Solution files 2 | *.soln 3 | 4 | # Prerequisites 5 | *.d 6 | 7 | # Compiled Object files 8 | *.slo 9 | *.lo 10 | *.o 11 | *.obj 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Compiled Dynamic libraries 18 | *.so 19 | *.dylib 20 | *.dll 21 | 22 | # Fortran module files 23 | *.mod 24 | *.smod 25 | 26 | # Compiled Static libraries 27 | *.lai 28 | *.la 29 | *.a 30 | *.lib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | main 37 | 38 | # Python 39 | __pycache__/ 40 | *.whl 41 | 42 | # Virtualenv 43 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 44 | .Python 45 | [Bb]in 46 | [Ii]nclude 47 | [Ll]ib 48 | [Ll]ib64 49 | [Ll]ocal 50 | [Ss]cripts 51 | pyvenv.cfg 52 | .venv 53 | pip-selfcheck.json 54 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "C_Cpp.clang_format_fallbackStyle": "{ PointerAlignment: Left, ColumnLimit: 100 }", 3 | "files.associations": { 4 | "__bit_reference": "cpp", 5 | "__hash_table": "cpp", 6 | "__locale": "cpp", 7 | "__node_handle": "cpp", 8 | "__split_buffer": "cpp", 9 | "__threading_support": "cpp", 10 | "__tree": "cpp", 11 | "__verbose_abort": "cpp", 12 | "array": "cpp", 13 | "bitset": "cpp", 14 | "cctype": "cpp", 15 | "clocale": "cpp", 16 | "cmath": "cpp", 17 | "complex": "cpp", 18 | "cstdarg": "cpp", 19 | "cstddef": "cpp", 20 | "cstdint": "cpp", 21 | "cstdio": "cpp", 22 | "cstdlib": "cpp", 23 | "cstring": "cpp", 24 | "ctime": "cpp", 25 | "cwchar": "cpp", 26 | "cwctype": "cpp", 27 | "deque": "cpp", 28 | "execution": "cpp", 29 | "memory": "cpp", 30 | "fstream": "cpp", 31 | "initializer_list": "cpp", 32 | "iomanip": "cpp", 33 | "ios": "cpp", 34 | "iosfwd": "cpp", 35 | "iostream": "cpp", 36 | "istream": "cpp", 37 | "limits": "cpp", 38 | "locale": "cpp", 39 | "mutex": "cpp", 40 | "new": "cpp", 41 | "optional": "cpp", 42 | "ostream": "cpp", 43 | "print": "cpp", 44 | "queue": "cpp", 45 | "ratio": "cpp", 46 | "set": "cpp", 47 | "sstream": "cpp", 48 | "stack": "cpp", 49 | "stdexcept": "cpp", 50 | "streambuf": "cpp", 51 | "string": "cpp", 52 | "string_view": "cpp", 53 | "tuple": "cpp", 54 | "typeinfo": "cpp", 55 | "unordered_map": "cpp", 56 | "unordered_set": "cpp", 57 | "variant": "cpp", 58 | "vector": "cpp", 59 | "algorithm": "cpp" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CS106L Assignments 2 | 3 | This repository contains starter code for Stanford CS106L, a course on Standard C++ programming. 4 | 5 | To get started, [follow the setup instructions in A0](/assignment0/README.md)! -------------------------------------------------------------------------------- /admin.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | import argparse 4 | 5 | 6 | def find_last_commit_with_path(path): 7 | if os.path.exists(path): 8 | raise ValueError(f"Path {path} already exists!") 9 | try: 10 | # This gives us the hash that deleted the file 11 | commit_hash = subprocess.check_output( 12 | ["git", "log", "-n", "1", "--pretty=format:%H", "--", path], 13 | ).decode("utf-8").strip() 14 | 15 | # This gives us the hash before that one 16 | commit_hash = subprocess.check_output( 17 | ["git", "log", "-n", "1", "--pretty=format:%H", f"{commit_hash}^"], 18 | ).decode("utf8").strip() 19 | 20 | return commit_hash 21 | except subprocess.CalledProcessError: 22 | return None 23 | 24 | def copy_from_commit(commit_hash, path): 25 | os.makedirs(path, exist_ok=True) 26 | subprocess.run(["git", "checkout", commit_hash, "--", path]) 27 | 28 | 29 | def main(): 30 | parser = argparse.ArgumentParser(description="Copies an old assignment into the worktree.") 31 | parser.add_argument("assignment", help="An assignment, e.g. 'assignment0', 'assignment1', etc.") 32 | args = parser.parse_args() 33 | 34 | path = args.assignment 35 | commit_hash = find_last_commit_with_path(path) 36 | 37 | if commit_hash: 38 | print(f"Last commit for {path}: {commit_hash}") 39 | copy_from_commit(commit_hash, path) 40 | print(f"Copied {path} from commit {commit_hash}.") 41 | else: 42 | print(f"No commits found for the specified assignment: {path}.") 43 | 44 | main() 45 | -------------------------------------------------------------------------------- /assignment0/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Assignment 0: Setup! 4 | 5 | Due Friday, April 18th at 11:59PM 6 | 7 | ## Overview 8 | 9 | Welcome to CS106L! This assignment will get you setup for the rest of the quarter so that setup for the rest of the assignments is simple and smooth. By the end of this assignment, you should be able to compile and run C++ files from VSCode and run the autograder, which you'll be doing for each of the remaining assignments! 10 | 11 | If you run into any issues during setup, please reach out to us on [EdStem](https://edstem.org/us/courses/77935/discussion) or come to our office hours! 12 | 13 | ## Part 1: Installing Python 14 | 15 | ### Part 1.1: Checking for an existing Python installation 16 | 17 | The autograder for each assignment in CS106L uses Python. You must have a Python installation of version `3.8` or higher. To check your Python version you can run the following in your terminal: 18 | 19 | If you're on Linux or Mac: 20 | 21 | ```sh 22 | python3 --version 23 | ``` 24 | 25 | If you're on Windows: 26 | 27 | ```sh 28 | python --version 29 | ``` 30 | 31 | If you get a version that is `3.8` or higher, then you're good, **you can continue to Part 2**. Otherwise, please follow Part 1.2 to install Python on your machine. 32 | 33 | ### Part 1.2: Installing Python (if you don't already have it installed) 34 | 35 | #### Mac & Windows 36 | 37 | Please download the latest Python version [here](https://www.python.org/downloads/) and run the installer. **Note: on Windows, you must check `Add python.exe to PATH` in the installer**. After installing, verify that the installation worked by following the steps in **Part 1.1**. 38 | 39 | #### Linux 40 | 41 | These instructions are for Debian-based distributions, like Ubuntu. Tested on Ubuntu 20.04 LTS. 42 | 43 | 1. Update the Ubuntu package lists by running 44 | 45 | ```sh 46 | sudo apt-get update 47 | ``` 48 | 49 | 2. Install Python: 50 | 51 | ```sh 52 | sudo apt-get install python3 python3-venv 53 | ``` 54 | 55 | 3. Restart your terminal and verify that the installation worked by running: 56 | 57 | ```sh 58 | python3 --version 59 | ``` 60 | 61 | ## Part 2: Setup VSCode and C++ Compiler 62 | 63 | We will use VSCode to write C++ code for this class. Below are instructions to setup VSCode along with the GCC compiler for your machine. 64 | 65 | ### Mac 66 | 67 | #### Step One: Installing VSCode 68 | 69 | Go to [this link](https://code.visualstudio.com/docs/setup/mac) 70 | and download Visual Studio Code for Mac. Follow the instructions on this webpage under the 71 | section **Installation**. 72 | 73 | Inside VSCode, head to the extensions tab and search for **C/C++**. Click on the **C/C++** extension, and then click **Install**. 74 | 75 | Finally, open the command palette (Cmd+Shift+P), search for `Shell Command: Install 'code' command in PATH`, and select it. This will allow you to launch VSCode directly from the terminal by running the `code` command. 76 | 77 | **🥳 At this point you should successfully have VSCode on your Mac 👏** 78 | 79 | #### Step Two: Installing a C++ Compiler 80 | 81 | 1. Check if you have Homebrew by running 82 | 83 | ```sh 84 | brew --version 85 | ``` 86 | 87 | If you get something like 88 | 89 | ```sh 90 | brew --version 91 | Homebrew 4.2.21 92 | ``` 93 | 94 | then skip ahead to step 3. If you get anything else that looks suspicious, proceed to step 2! 95 | 96 | 2. Run this command: 97 | 98 | ```sh 99 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 100 | ``` 101 | 102 | which will download Homebrew🍺, a package manager for Mac. Woot woot. 103 | 104 | 3. Run the following command: 105 | 106 | ```sh 107 | brew install gcc 108 | ``` 109 | 110 | which will install the GCC compiler on your machine. 111 | 112 | 4. Make note of which GCC version Homebrew installs. In most cases, this will be `g++-14`. 113 | By default, the `g++` command on Mac is an alias to the built-in `clang` compiler. We can fix this by running 114 | 115 | ```sh 116 | echo 'export PATH="$(brew --prefix)/bin:$PATH"\nalias g++="g++-14"' >> ~/.zshrc 117 | ``` 118 | 119 | to make `g++` point to the version of GCC we just installed. Change `g++-14` in the above command to whichever version of GCC was installed. 120 | 121 | 5. Restart your terminal and verify that everything worked by running the following command: 122 | 123 | ```sh 124 | g++ --version 125 | ``` 126 | 127 | > [!NOTE] 128 | > If you are using VSCode to run your code, you may get an issue running this last command. **Make sure you are running a `zsh` terminal inside of VSCode,** as shown in the image below: 129 | > ![An image showing how to change the VSCode terminal to zsh](./docs/mac-zsh.png) 130 | > You will need to do this any time you want to run `g++` for this class. **Alternatively, change VSCode's default terminal to zsh** by pressing Cmd+Shift+P, going to **Terminal: Select Default Profile**, and selecting **`zsh`**. 131 | 132 | ### Windows 133 | 134 | #### Step One: Installing VSCode 135 | 136 | Go to [this link](https://code.visualstudio.com/docs/setup/windows) 137 | and download Visual Studio Code for Windows. Follow the instructions on this webpage under the 138 | section **Installation**. 139 | 140 | Inside VSCode, head to the extensions tab and search for **C/C++**. Click on the **C/C++** extension, and then click **Install**. 141 | 142 | **🥳 At this point you should successfully have VSCode on your PC 👏** 143 | 144 | #### Step Two: Installing a C++ Compiler 145 | 146 | 1. Follow the instructions at [this link](https://code.visualstudio.com/docs/cpp/config-mingw) under the section **Installing the MinGW-w64 toolchain.** 147 | 148 | 2. After fully following the instructions under **Installing the MinGW-w64 toolchain** you should now be able to verify everything worked by running the following command: 149 | 150 | ```sh 151 | g++ --version 152 | ``` 153 | 154 | ### Linux 155 | 156 | These instructions are for Debian-based distributions, like Ubuntu. Tested on Ubuntu 20.04 LTS. 157 | 158 | #### Step One: Installing VSCode 159 | 160 | Go to [this link](https://code.visualstudio.com/docs/setup/linux) 161 | and download Visual Studio Code for Linux. Follow the instructions on this webpage under the section **Installation**. 162 | 163 | Inside VSCode, head to the extensions tab and search for **C/C++**. Click on the **C/C++** extension, and then click **Install**. 164 | 165 | Finally, open the command palette (Ctrl+Shift+P), search for `Shell Command: Install 'code' command in PATH`, and select it. This will allow you to launch VSCode directly from the terminal by running the `code` command. 166 | 167 | **🥳 At this point you should successfully have VSCode on your Linux machine 👏** 168 | 169 | #### Step Two: Installing a C++ Compiler 170 | 171 | 1. In a terminal, update the Ubuntu package lists by running 172 | 173 | ```sh 174 | sudo apt-get update 175 | ``` 176 | 177 | 2. Next install the `g++` compiler: 178 | 179 | ```sh 180 | sudo apt-get install g++-10 181 | ``` 182 | 183 | 3. By default, the system version of `g++` will be used. To change it to the version you just installed, you can configure Linux to use G++ version 10 or a higher version installed like so: 184 | 185 | ```sh 186 | sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10 187 | ``` 188 | 189 | 4. Restart your terminal and verify that GCC was installed correctly. You must have a `g++` version of 10 or higher: 190 | 191 | ```sh 192 | g++ --version 193 | ``` 194 | 195 | ## Part 3: Cloning the class code via Git! 196 | 197 | Git is a popular VCS (version control system) that we will use to distribute starter codes for the assignments. Make sure that you have Git installed by running 198 | 199 | ```sh 200 | git --version 201 | ``` 202 | 203 | If you see anything that looks off, [download and install Git from this page](https://git-scm.com/downloads)! 204 | 205 | ### Download the starter code 206 | 207 | Open VSCode, and then open a terminal (hit Ctrl+\` or go to **Terminal > New Terminal** at the top of the window) and run the following command: 208 | 209 | ```sh 210 | git clone https://github.com/cs106l/cs106l-assignments.git 211 | ``` 212 | 213 | which will download the starter code into a folder `cs106l-assignments`. 214 | 215 | ### Opening a VSCode workspace 216 | 217 | When working on assignments in this class, we recommend you open up a VSCode workspace for the specific assignment folder you are working on. So if you now have a folder `cs106l-assignments`, you can first `cd` (change directory) into the correct folder: 218 | 219 | ```sh 220 | cd cs106l-assignments/assignment0 221 | ``` 222 | 223 | which changes your working directory to `assignment0`, and then you can open up a VSCode workspace dedicated to this folder: 224 | 225 | ```sh 226 | code . 227 | ``` 228 | 229 | and now you should be ready to go! 230 | 231 | ### Fetching assignments 232 | 233 | As we update existing assignments and release new ones, we will push updates to this repository. To fetch a new assignment, open up a terminal to your `cs106l-assignments` directory and run 234 | 235 | ```sh 236 | git pull origin main 237 | ``` 238 | 239 | You should now have the latest starter code! 240 | 241 | # Part 4: Testing your setup! 242 | 243 | Now we will have you compile your first C++ file and run the autograder. To run any C++ code, first you'll need to compile it. Open up a VSCode terminal (again, hit Ctrl+\` or go to **Terminal > New Terminal** at the top of the window). Then make sure that you are in the `assignment0/` directory and run: 244 | 245 | ```sh 246 | g++ -std=c++23 main.cpp -o main 247 | ``` 248 | 249 | This **compiles** the C++ file `main.cpp` into an executable file called `main` which contains raw machine code that your processor can execute. Assuming that your code compiles without any errors, you can now do: 250 | 251 | ```sh 252 | ./main 253 | ``` 254 | 255 | which will actually run the `main` function in `main.cpp`. This will execute your code and then run an autograder that will check that your installation is correct. 256 | 257 | > [!NOTE] 258 | > 259 | > ### Note for Windows 260 | > 261 | > On Windows, you may need to compile your code using 262 | > 263 | > ```sh 264 | > g++ -static-libstdc++ -std=c++20 main.cpp -o main 265 | > ``` 266 | > 267 | > in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with: 268 | > 269 | > ```sh 270 | > ./main.exe 271 | > ``` 272 | > 273 | 274 | > [!NOTE] 275 | > 276 | > ### Note for Mac 277 | > 278 | > You may get a compiler error when attempting to compile this code due to a missing `wchar.h` (or some similar file). If this happens, you may need to reinstall the Xcode command line tools on your machine by running the following commands: 279 | > 280 | > ```sh 281 | > sudo rm -rf /Library/Developer/CommandLineTools 282 | > sudo xcode-select --install 283 | > ``` 284 | > 285 | > Afterwards, you should be able to compile normally. 286 | 287 | # 🚀 Submission Instructions 288 | 289 | After compiling and running, if your autograder looks like this: 290 | 291 | ![An image showing a terminal window where the autograder has run with all tests passing](docs/autograder.png) 292 | 293 | then you have finished the assignment! Woot woot. To submit the assignment, please complete the feedback form [at this link](https://forms.gle/bC4xG9o1tsABsCug9)! 294 | -------------------------------------------------------------------------------- /assignment0/autograder/autograder.py: -------------------------------------------------------------------------------- 1 | from utils import Autograder 2 | 3 | import os 4 | import subprocess 5 | import sys 6 | 7 | PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 8 | AUTOGRADER_DIR = os.path.join(PATH, "autograder") 9 | 10 | def test_python_installed(): 11 | """Check that the correct Python version is installed.""" 12 | if sys.version_info < (3, 8): 13 | raise AssertionError(f"Python 3.8 not installed: current version is {sys.version_info.major}.{sys.version_info.minor}.") 14 | 15 | 16 | def test_cpp_compiler(): 17 | """Check that a C++ compiler is installed.""" 18 | try: 19 | result = subprocess.run(["g++", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) 20 | assert result.returncode == 0, "C++ compiler (g++) not found or not working." 21 | except FileNotFoundError: 22 | raise AssertionError("C++ compiler (g++) not installed or not in PATH.") 23 | 24 | 25 | def test_git_installed(): 26 | """Check that Git is installed.""" 27 | try: 28 | result = subprocess.run(["git", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) 29 | assert result.returncode == 0, "Git is not installed or not in PATH." 30 | except FileNotFoundError: 31 | raise AssertionError("Git is not installed or not in PATH.") 32 | 33 | 34 | 35 | if __name__ == "__main__": 36 | grader = Autograder() 37 | grader.add_part("Check C++ Compiler", test_cpp_compiler) 38 | grader.add_part("Check Git Installation", test_git_installed) 39 | grader.add_part("Check Python Installation", test_python_installed) 40 | grader.run() -------------------------------------------------------------------------------- /assignment0/autograder/requirements.txt: -------------------------------------------------------------------------------- 1 | colorama>=0.4.6 -------------------------------------------------------------------------------- /assignment0/autograder/utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | int 9 | run_autograder() 10 | { 11 | auto run_program = [](std::string program, 12 | std::initializer_list args, 13 | bool silent = false) { 14 | std::stringstream ss; 15 | 16 | ss << program; 17 | for (const auto& arg : args) { 18 | ss << ' ' << arg; 19 | } 20 | 21 | if (silent) { 22 | #ifdef _WIN32 23 | ss << " >nul 2>&1"; 24 | #else 25 | ss << " >/dev/null 2>&1"; 26 | #endif 27 | } 28 | 29 | std::cout.flush(); 30 | return system(ss.str().c_str()); 31 | }; 32 | 33 | std::string python; 34 | for (const auto& option : 35 | { "python", "python3", "/usr/bin/python3", "/usr/bin/python" }) { 36 | if (run_program(option, { "--version" }, true) == 0) { 37 | python = option; 38 | break; 39 | } 40 | } 41 | 42 | if (python.empty()) { 43 | std::cerr 44 | << "Python was not found on your system. Please install Python and " 45 | "try again." 46 | << "\n"; 47 | std::exit(1); 48 | } 49 | 50 | return run_program(python, { "autograder/autograder.py" }); 51 | } 52 | 53 | std::vector split(std::string s, char delim) { 54 | std::vector return_vec; 55 | std::stringstream ss(s); 56 | std::string token; 57 | while (getline(ss, token, delim)) { 58 | return_vec.push_back(token); 59 | } 60 | return return_vec; 61 | } 62 | 63 | std::vector read_lines(std::string filename) { 64 | std::vector lines; 65 | std::ifstream file(filename); 66 | if (!file.is_open()) { 67 | std::cerr << "Could not open file " << filename << "\n"; 68 | std::exit(1); 69 | } 70 | 71 | std::string line; 72 | while (std::getline(file, line)) lines.push_back(line); 73 | return lines; 74 | } -------------------------------------------------------------------------------- /assignment0/docs/autograder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment0/docs/autograder.png -------------------------------------------------------------------------------- /assignment0/docs/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment0/docs/header.png -------------------------------------------------------------------------------- /assignment0/docs/mac-zsh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment0/docs/mac-zsh.png -------------------------------------------------------------------------------- /assignment0/docs/vscode-extensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment0/docs/vscode-extensions.png -------------------------------------------------------------------------------- /assignment0/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CS106L Assignment 0: Environment Setup! 3 | * Created by Fabio Ibanez 4 | * 5 | * If you're reading this welcome to CS106L! For this assignment you don't 6 | * actually need to write any C++ code. Instead, you'll be setting up your 7 | * environment and getting familiar with the tools we'll be using in the course. 8 | * The code in this file will verify that your installation is correct and that 9 | * the autograder is working properly. 10 | */ 11 | 12 | #include 13 | #include 14 | #include "autograder/utils.cpp" 15 | 16 | int main() { 17 | return run_autograder(); 18 | } -------------------------------------------------------------------------------- /assignment1/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 1: SimpleEnroll 2 | 3 | Due Friday, April 25th, at 11:59PM 4 | 5 | ## Overview 6 | 7 | It’s that time of the quarter again; time to use SimpleEnroll 🤗 Wootwoot. 8 | One thing everyone realizes in their Stanford career at one point is that they 9 | have to eventually graduate — and so enrolling in classes becomes a strategic 10 | endeavor to maximize the XP towards graduation, while also being able to sleep 11 | more than 4 hours a night! 12 | 13 | In this hopefully short assignment, we’re going to use data from the 14 | ExploreCourses API to figure out which CS classes on ExploreCourses are 15 | offered this year, and which are not! We’ll be taking advantage of streams, while also exercising initialization and references in C++. Lets jump in ʕ•́ᴥ•̀ʔっ 16 | 17 | There are only two files you should need to care about: 18 | 19 | * `main.cpp`: All your code goes here 😀! 20 | * `utils.cpp`: Contains some utility functions. You'll use functions defined in this file, but you don't otherwise need to modify it. 21 | 22 | ## Running your code 23 | 24 | To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit Ctrl+\` or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assignment1/` directory and run: 25 | 26 | ```sh 27 | g++ -std=c++20 main.cpp -o main 28 | ``` 29 | 30 | Assuming that your code compiles without any compiler errors, you can now do: 31 | 32 | ```sh 33 | ./main 34 | ``` 35 | 36 | which will actually run the `main` function in `main.cpp`. This will execute your code and then run an autograder that will check that your code is correct. 37 | 38 | As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track! 39 | 40 | > [!NOTE] 41 | > ### Note for Windows 42 | > On Windows, you may need to compile your code using 43 | > ```sh 44 | > g++ -static-libstdc++ -std=c++20 main.cpp -o main 45 | > ``` 46 | > in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with: 47 | > ```sh 48 | > ./main.exe 49 | > ``` 50 | 51 | ## Part 0: Read the code and fill in the `Course` struct 52 | 53 | 1. In this assignment, we'll be using the `Course` struct to represent records pulled from ExploreCourses in C++. Take a look at the (incomplete) definition of the `Course` struct in `main.cpp` and fill in the field definitions. Ultimately, we'll be using streams to generate `Course`s --- remember what types streams deal with? 54 | 55 | 2. Take a look at the `main` function in `main.cpp`, and take special notice of how `courses` is passed into `parse_csv`, `write_courses_offered`, 56 | and `write_courses_not_offered`. Think about what these functions are doing. Do you need to change anything in the function definition? Spoiler, you do. 57 | 58 | ## Part 1: `parse_csv` 59 | 60 | Check out `courses.csv`, it is a CSV file, with three columns: Title, Number of 61 | Units, and Quarter. Implement `parse_csv` so that, for each line in the csv file, it creates a struct `Course` containing the Title, Number of Units, and Quarter for that line. 62 | 63 | A couple of things you need to think about: 64 | 1. How are you going to read in `courses.csv`? Muahahaha, perhaps a 65 | stream 😏? 66 | 2. How will you get each line in the file? 67 | 68 | ### Hints 69 | 70 | 1. Take a look at the `split` function we provide in `utils.cpp`. It may come in handy! 71 | * Feel free to check out the implementation of `split` and ask us any questions about it – you 72 | should be able to reason about it since it’s using a `stringstream`. 73 | 2. Each **line** is a record! *This is important, so we're saying it again :>)* 74 | 3. In CSV files (and specifically in `courses.csv`), the first line is usually a row that defines the column names (a column header row). This line doesn't actually correspond to a `Course`, so you'll need to skip it somehow! 75 | 76 | ## Part 2: `write_courses_offered` 77 | 78 | Ok. Now you have a populated `courses` vector which has all of the records 79 | of the `courses.csv` file neatly stored in a `Course` struct! You find yourself 80 | interested in only the courses that are offered, right? **A course is considered offered if its Quarter field is not the string `“null”`.** In this function, write out to `“student_output/courses_offered.csv”` all the courses that don’t have 81 | `“null”` in the quarter field. 82 | 83 | > [!IMPORTANT] 84 | > When writing out to the CSV file, please follow this format: 85 | > ``` 86 | > ,<Number of Units>,<Quarter> 87 | > ``` 88 | > Note that there are **no spaces** between the commas! The autograder will not be happy if this format is not followed! 89 | > 90 | > Also, **make sure to write out the column header row** as the first line in the output. This is the same line you had to skip in `courses.csv` for the previous step! 91 | 92 | Once `write_courses_offered` has been called, we expect that all of the offered courses (and consequently all the courses you wrote to the output file) will be removed from the `all_courses` vector. **This means that after this 93 | function runs, `all_courses` should ONLY contain courses that are 94 | not offered!** 95 | 96 | One way to do this is to keep track of the courses that are offered perhaps with another vector and delete them from `all_courses`. Just like in Python and many other languages, it is a bad idea to remove elements from a data structure while you are iterating over it, so you'll probably want to do this *after* you have written all offered courses to file. 97 | 98 | ## Part 3: `write_courses_not_offered` 99 | 100 | So you’re curious about courses that aren’t offered... In the 101 | `write_courses_not_offered` function, write out to 102 | `“student_output/courses_not_offered.csv”` the courses in 103 | `unlisted_courses`. Remember since you deleted the courses that are 104 | offered in the previous step, `unlisted_courses` trivially contains ONLY courses that are not offered – lucky you. So this step should look really similar to Part 2 except shorter and a *tiny* bit simpler. 105 | 106 | ## 🚀 Submission Instructions 107 | 108 | After compiling and running, if your autograder looks like this: 109 | 110 | ![An image showing a terminal window where the autograder has run with all tests passing](docs/autograder.png) 111 | 112 | then you have finished the assignment! Woot woot. To submit the assignment, please complete the feedback form [at this link](https://forms.gle/9CBz5HRYRADP3RW28). Once you submit the form, a link to the submission page will show up in the form submission confirmation. 113 | 114 | Your deliverable should be: 115 | 116 | - `main.cpp` 117 | -------------------------------------------------------------------------------- /assignment1/autograder/autograder.py: -------------------------------------------------------------------------------- 1 | from utils import Autograder 2 | 3 | import base64 4 | from colorama import Fore 5 | import difflib 6 | import pickle 7 | import re 8 | import os 9 | 10 | PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 11 | AUTOGRADER_DIR = os.path.join(PATH, "autograder") 12 | 13 | SOLN_NOT_OFFERED_BIN = os.path.join(AUTOGRADER_DIR, "courses_not_offered.bin") 14 | SOLN_OFFERED_BIN = os.path.join(AUTOGRADER_DIR, "courses_offered.bin") 15 | SOLN_OFFERED = os.path.join(AUTOGRADER_DIR, "courses_offered.csv") 16 | SOLN_NOT_OFFERED = os.path.join(AUTOGRADER_DIR, "courses_not_offered.csv") 17 | 18 | 19 | def binary_to_csv(binary_filename, csv_filename): 20 | with open(binary_filename, "rb") as pickle_file: 21 | encoded_data = pickle.load(pickle_file) 22 | decoded_data = base64.b64decode(encoded_data).decode("utf-8") 23 | with open(csv_filename, "w") as output_file: 24 | output_file.write(decoded_data) 25 | 26 | 27 | def csv_to_binary(csv_filename, binary_filename): 28 | with open(csv_filename, "r") as file: 29 | lines = file.read() 30 | encoded_data = base64.b64encode(lines.encode("utf-8")) 31 | with open(binary_filename, "wb") as pickle_file: 32 | pickle.dump(encoded_data, pickle_file) 33 | 34 | 35 | def check_files_equal(expected, actual): 36 | if not os.path.exists(expected): 37 | raise RuntimeError( 38 | f"Solution file '{os.path.relpath(expected, PATH)}' does not exist" 39 | ) 40 | if not os.path.exists(actual): 41 | raise RuntimeError( 42 | f"Output file '{os.path.relpath(actual, PATH)}' does not exist" 43 | ) 44 | 45 | with open(expected, "r") as f1, open(actual, "r") as f2: 46 | expected_lines = f1.readlines() 47 | actual_lines = f2.readlines() 48 | 49 | if expected_lines != actual_lines: 50 | diff = list( 51 | difflib.unified_diff( 52 | expected_lines, actual_lines, fromfile=expected, tofile=actual 53 | ) 54 | ) 55 | 56 | if len(diff) > 10: 57 | diff = diff[:10] + ["... (truncated, showing first 10 lines of diff) ...\n"] 58 | 59 | diff = [f"\t{l}" for l in diff] 60 | diff_output = "".join(diff) 61 | 62 | diff_output = re.sub(r"^\s*-+", lambda match: f"{Fore.RED}{match.group(0)}{Fore.RESET}", diff_output, flags=re.MULTILINE) 63 | diff_output = re.sub(r"^\s*\++", lambda match: f"{Fore.GREEN}{match.group(0)}{Fore.RESET}", diff_output, flags=re.MULTILINE) 64 | 65 | raise RuntimeError( 66 | f"Contents of '{os.path.relpath(actual, PATH)}' do not equal solution:\n{diff_output}" 67 | ) 68 | 69 | 70 | def setup(): 71 | binary_to_csv(SOLN_NOT_OFFERED_BIN, SOLN_NOT_OFFERED) 72 | binary_to_csv(SOLN_OFFERED_BIN, SOLN_OFFERED) 73 | 74 | 75 | def teardown(): 76 | os.remove(SOLN_OFFERED) 77 | os.remove(SOLN_NOT_OFFERED) 78 | 79 | 80 | def test_write_courses_offered(): 81 | student = os.path.join(PATH, "student_output/courses_offered.csv") 82 | check_files_equal(SOLN_OFFERED, student) 83 | 84 | 85 | def test_write_courses_not_offered(): 86 | student = os.path.join(PATH, "student_output/courses_not_offered.csv") 87 | check_files_equal(SOLN_NOT_OFFERED, student) 88 | 89 | 90 | if __name__ == "__main__": 91 | grader = Autograder() 92 | grader.add_part("write_course_offered", test_write_courses_offered) 93 | grader.add_part("write_course_not_offered", test_write_courses_not_offered) 94 | 95 | grader.setup = setup 96 | grader.teardown = teardown 97 | grader.run() 98 | -------------------------------------------------------------------------------- /assignment1/autograder/courses_not_offered.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment1/autograder/courses_not_offered.bin -------------------------------------------------------------------------------- /assignment1/autograder/courses_offered.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment1/autograder/courses_offered.bin -------------------------------------------------------------------------------- /assignment1/autograder/utils.py: -------------------------------------------------------------------------------- 1 | _AUTOGRADER_PACKAGES = [ 2 | # For logging and color output 3 | "colorama==0.4.6" 4 | ] 5 | 6 | # ============================================================================== 7 | # Virtual Environment Setup 8 | # ============================================================================== 9 | 10 | 11 | def _check_virtualenv(): 12 | import sys 13 | import os 14 | import subprocess 15 | 16 | venv_path = os.path.dirname(os.path.abspath(__file__)) 17 | 18 | # The first condition checks if we are running in the **correct** virtual environment. 19 | # The second condition checks if we ran this script to set environment variables correctly. 20 | if os.environ.get("VIRTUAL_ENV", None) != venv_path or "VIRTUAL_ENV_BIN" not in os.environ: 21 | config_path = os.path.join(venv_path, "pyvenv.cfg") 22 | if not os.path.isfile(config_path): 23 | print("🔍 Virtual environment not found. Creating one in 'autograder/'...") 24 | subprocess.check_call([sys.executable, "-m", "venv", venv_path]) 25 | print("✅ Virtual environment created.") 26 | 27 | bin_dir = next((bd for bd in ["Scripts", "bin"] if os.path.isdir(os.path.join(venv_path, bd))), None) 28 | if bin_dir is None: 29 | raise RuntimeError("Couldn't find 'Scripts' or 'bin' directory in virtual environment") 30 | bin_dir = os.path.join(venv_path, bin_dir) 31 | 32 | env = os.environ.copy() 33 | env["PATH"] = os.pathsep.join([bin_dir, *env.get("PATH", "").split(os.pathsep)]) 34 | env["VIRTUAL_ENV"] = venv_path # virtual env is right above bin directory 35 | env["VIRTUAL_ENV_BIN"] = bin_dir 36 | env["VIRTUAL_ENV_PROMPT"] = os.path.basename(venv_path) 37 | 38 | # Fixes encoding errors on Windows (specifically when installing packages with pip) 39 | env["PYTHONIOENCODING"] = "utf-8" 40 | 41 | interpreter_path = os.path.join(bin_dir, "python") 42 | result = subprocess.run([interpreter_path] + sys.argv, env=env) 43 | sys.exit(result.returncode) 44 | 45 | 46 | _check_virtualenv() 47 | 48 | 49 | # ============================================================================== 50 | # Pip Package Installation 51 | # ============================================================================== 52 | 53 | def _install_requirement(package: str): 54 | import subprocess 55 | import sys 56 | 57 | subprocess.check_call( 58 | [sys.executable, "-m", "pip", "install", package], 59 | stdout=subprocess.DEVNULL, 60 | ) 61 | 62 | def _install_requirements(): 63 | import sys 64 | import subprocess 65 | import os 66 | 67 | print("⏳ Installing autograder packages (this may take a few minutes)...") 68 | 69 | # pip might need to be updated for packages to install, so let's make sure 70 | def check_pip_update(): 71 | # Ensure pip is installed 72 | subprocess.run([sys.executable, "-m", "ensurepip", "--default-pip"], stdout=subprocess.DEVNULL) 73 | result = subprocess.run( 74 | [sys.executable, "-m", "pip", "list", "--outdated"], 75 | stdout=subprocess.PIPE, 76 | stderr=subprocess.DEVNULL, 77 | text=True, 78 | ) 79 | return "pip" in result.stdout 80 | 81 | if check_pip_update(): 82 | subprocess.check_call( 83 | [sys.executable, "-m", "pip", "install", "-U", "pip"], 84 | stdout=subprocess.DEVNULL, 85 | ) 86 | 87 | REQUIREMENTS = os.path.join(os.path.dirname(__file__), "requirements.txt") 88 | 89 | # Packages required by the core autograder 90 | for package in _AUTOGRADER_PACKAGES: 91 | _install_requirement(package) 92 | 93 | # Assignment specific packages 94 | if os.path.isfile(REQUIREMENTS): 95 | subprocess.check_call( 96 | [sys.executable, "-m", "pip", "install", "-r", REQUIREMENTS], 97 | stdout=subprocess.DEVNULL, 98 | ) 99 | 100 | print("✅ Autograder packages installed.") 101 | 102 | 103 | # Install autograder packages on import 104 | _install_requirements() 105 | 106 | # ============================================================================== 107 | # Imports 108 | # ============================================================================== 109 | 110 | import os 111 | from dataclasses import dataclass 112 | from typing import Callable, List, Optional, Union 113 | from colorama import Fore, init, Style, Back 114 | 115 | init() 116 | 117 | 118 | # ============================================================================== 119 | # Checking for Updates 120 | # ============================================================================== 121 | 122 | 123 | def check_for_updates(): 124 | import subprocess 125 | 126 | def needs_update() -> bool: 127 | try: 128 | subprocess.check_call( 129 | ["git", "rev-parse", "--is-inside-work-tree"], 130 | stdout=subprocess.DEVNULL, 131 | stderr=subprocess.DEVNULL, 132 | ) 133 | subprocess.check_call( 134 | ["git", "fetch", "origin"], 135 | stdout=subprocess.DEVNULL, 136 | stderr=subprocess.DEVNULL, 137 | ) 138 | 139 | origin_main_commit = subprocess.check_output( 140 | ["git", "rev-parse", "origin/main"] 141 | ).strip() 142 | result = subprocess.run( 143 | ["git", "merge-base", "--is-ancestor", origin_main_commit, "HEAD"] 144 | ) 145 | return result.returncode != 0 146 | 147 | except subprocess.CalledProcessError: 148 | return False 149 | 150 | if needs_update(): 151 | tab = f"{Back.YELLOW} {Back.RESET} " 152 | print( 153 | f"\n{tab}It looks like your assignment might be out of date. Try running:" 154 | f"\n{tab}\n{tab}\tgit pull origin main" 155 | f"\n{tab}\n{tab}to fetch any updates, and then re-run your code.\n" 156 | ) 157 | 158 | 159 | check_for_updates() 160 | 161 | 162 | # ============================================================================== 163 | # Autograder Core 164 | # ============================================================================== 165 | 166 | ASSIGNMENT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 167 | AUTOGRADER_DIR = os.path.join(ASSIGNMENT_DIR, "autograder") 168 | 169 | TestFunction = Callable[[], Union[bool, None]] 170 | """ 171 | A test function takes no parameters. 172 | It returns True or None if the test passes, and False if the test fails. 173 | It can also throw an exception to indicate failure, the details of which will be printed to the console. 174 | """ 175 | 176 | @dataclass(frozen=True) 177 | class TestPart: 178 | name: str 179 | func: TestFunction 180 | special: bool = False 181 | 182 | 183 | class Autograder: 184 | parts: List[TestPart] = [] 185 | setup: Optional[TestFunction] = None 186 | teardown: Optional[TestFunction] = None 187 | 188 | def add_part(self, name: str, func: Callable[[], bool]) -> None: 189 | self.parts.append(TestPart(name, func)) 190 | 191 | def run(self) -> None: 192 | parts = self.parts.copy() 193 | if self.setup: 194 | parts.insert(0, TestPart("Autograder Setup", self.setup, True)) 195 | if self.teardown: 196 | parts.append(TestPart("Autograder Teardown", self.teardown, True)) 197 | 198 | failures = False 199 | for part in parts: 200 | header = f"Running test: {part.name}... 🧪".ljust(80) 201 | print(f"\n{Back.CYAN}{Fore.LIGHTWHITE_EX}{header}{Style.RESET_ALL}") 202 | 203 | result = None 204 | error = None 205 | 206 | try: 207 | result = part.func() 208 | except Exception as e: 209 | error = e 210 | result = False 211 | 212 | if result is None or result: 213 | if not part.special: 214 | print(f"{Fore.GREEN}✅ {part.name} passed! 🚀 {Fore.RESET}") 215 | else: 216 | print(f"{Fore.RED}❌ {part.name} failed! 😞 {Fore.RESET}") 217 | if error: 218 | print(f"{Style.BRIGHT}Reason:{Style.DIM} {error}{Style.RESET_ALL}") 219 | failures = True 220 | 221 | if part.special: 222 | break 223 | 224 | if not failures: 225 | message = "🚀🚀🚀 Congratulations, your code passed all the autograder tests! 🚀🚀🚀" 226 | message = message.ljust(75) 227 | print( 228 | f"\n{Back.LIGHTGREEN_EX}{Fore.LIGHTWHITE_EX}{message}{Style.RESET_ALL}" 229 | ) 230 | 231 | 232 | # ============================================================================== 233 | # CastXML Installation 234 | # ============================================================================== 235 | 236 | _castxml_installed: bool = False 237 | 238 | def install_castxml(): 239 | global _castxml_installed 240 | _castxml_installed = True 241 | 242 | castxml_install_packages = [ 243 | # For CastXML installation 244 | "requests==2.32.3", 245 | # For CastXML installation 246 | "py-cpuinfo==9.0.0", 247 | # For inspecting CPP files in code 248 | "pygccxml==2.5.0" 249 | ] 250 | 251 | for package in castxml_install_packages: 252 | _install_requirement(package) 253 | 254 | bin_path = os.environ.get("VIRTUAL_ENV_BIN") 255 | castxml_dir = os.path.join(bin_path, "castxml") 256 | 257 | castxml_bin_dir = os.path.join(castxml_dir, "bin") 258 | os.environ["PATH"] = os.pathsep.join( 259 | [castxml_bin_dir, *os.environ.get("PATH", "").split(os.pathsep)] 260 | ) 261 | 262 | if os.path.isdir(castxml_dir): 263 | return 264 | 265 | print("⏳ Installing CastXML...") 266 | 267 | def get_platform_file(): 268 | import platform 269 | import cpuinfo 270 | import re 271 | 272 | os_name = platform.system().lower() 273 | arch = platform.machine().lower() 274 | 275 | if os_name == "linux" and arch == "aarch64": 276 | return "linux-aarch64.tar.gz" 277 | elif os_name == "linux": 278 | return "linux.tar.gz" 279 | elif os_name == "darwin": 280 | # Need to handle running Python under Rosetta on Apple Silicon 281 | brand = cpuinfo.get_cpu_info()["brand_raw"] 282 | if "arm" in arch or re.match(r"Apple M\d+", brand): 283 | return "macos-arm.tar.gz" 284 | return "macosx.tar.gz" 285 | elif os_name == "windows": 286 | return "windows.zip" 287 | 288 | raise RuntimeError( 289 | f"It looks like you are running on an unknown platform: {os_name}/{arch}. Please make a post on EdStem!" 290 | ) 291 | 292 | castxml_file = get_platform_file() 293 | castxml_download_url = f"https://github.com/CastXML/CastXMLSuperbuild/releases/download/v0.6.5/castxml-{castxml_file}" 294 | 295 | import requests 296 | import zipfile 297 | import tarfile 298 | 299 | castxml_archive_path = os.path.join(bin_path, castxml_file) 300 | 301 | with requests.get(castxml_download_url, stream=True) as r: 302 | r.raise_for_status() 303 | with open(castxml_archive_path, "wb") as f: 304 | for chunk in r.iter_content(chunk_size=8192): 305 | f.write(chunk) 306 | 307 | if castxml_file.endswith(".zip"): 308 | with zipfile.ZipFile(castxml_archive_path, "r") as zip_ref: 309 | zip_ref.extractall(bin_path) 310 | elif castxml_file.endswith(".tar.gz"): 311 | with tarfile.open(castxml_archive_path, "r:gz") as tar_ref: 312 | tar_ref.extractall(bin_path) 313 | 314 | print("✅ Installed CastXML!") 315 | 316 | 317 | def get_declarations(*files: os.PathLike): 318 | if not _castxml_installed: 319 | raise RuntimeError( 320 | "CastXML is not installed. Please run `install_castxml()` before calling `get_declarations()`." 321 | ) 322 | 323 | import subprocess 324 | from pygccxml import utils as gccutils 325 | from pygccxml import declarations 326 | from pygccxml import parser 327 | import logging 328 | 329 | # This should prevent pygccxml from outputting INFO messages 330 | gccutils.loggers.set_level(logging.WARNING) 331 | 332 | for file in files: 333 | abs_path = os.path.join(ASSIGNMENT_DIR, file) 334 | if not os.path.isfile(abs_path): 335 | raise FileNotFoundError(f"Couldn't find file: {abs_path}") 336 | 337 | # Grab the C++ parser 338 | generator_path, generator_name = gccutils.find_xml_generator() 339 | 340 | compiler_path = None 341 | if os.name == "nt": 342 | result = result = subprocess.run( 343 | ["where", "g++"], capture_output=True, text=True 344 | ) 345 | if result.returncode != 0: 346 | raise RuntimeError( 347 | "Couldn't find the path to g++. Did you follow the setup instructions?\n\nhttps://github.com/cs106l/cs106l-assignments" 348 | ) 349 | compiler_path = result.stdout.strip() 350 | compiler_path = f'"(" "{compiler_path}" -std=c++11 ")"' 351 | 352 | # Configure the C++ parser 353 | xml_generator_config = parser.xml_generator_configuration_t( 354 | xml_generator_path=generator_path, 355 | xml_generator=generator_name, 356 | compiler="g++", 357 | compiler_path=compiler_path, 358 | working_directory=ASSIGNMENT_DIR, 359 | ccflags="-std=c++11", 360 | ) 361 | 362 | try: 363 | decls: List[declarations.declaration_t] = parser.parse(files, xml_generator_config) 364 | except Exception as e: 365 | print() 366 | print(e) 367 | print() 368 | print( 369 | f"{Fore.RED}{Back.YELLOW}Failed to parse {', '.join(files)}. Did you remember to recompile your code?{Style.RESET_ALL}" 370 | ) 371 | print( 372 | f"{Fore.LIGHTWHITE_EX}If your code is compiling correctly, please reach out on Ed with the error message above.{Fore.RESET}\n" 373 | ) 374 | raise Exception("Failed to parse C++ file") 375 | 376 | return decls -------------------------------------------------------------------------------- /assignment1/docs/autograder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment1/docs/autograder.png -------------------------------------------------------------------------------- /assignment1/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CS106L Assignment 1: SimpleEnroll 3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca. 4 | * 5 | * Welcome to Assignment 1 of CS106L! Please complete each STUDENT TODO 6 | * in this file. You do not need to modify any other files. 7 | * 8 | * Students must implement: parse_csv, write_courses_offered, 9 | * write_courses_not_offered 10 | */ 11 | 12 | #include <algorithm> 13 | #include <fstream> 14 | #include <iostream> 15 | #include <sstream> 16 | #include <string> 17 | #include <vector> 18 | 19 | const std::string COURSES_OFFERED_PATH = "student_output/courses_offered.csv"; 20 | const std::string COURSES_NOT_OFFERED_PATH = "student_output/courses_not_offered.csv"; 21 | 22 | /** 23 | * Represents a course a student can take in ExploreCourses. 24 | * You must fill in the types of the fields in this struct. 25 | * Hint: Remember what types C++ streams work with?! 26 | */ 27 | struct Course { 28 | /* STUDENT TODO */ title; 29 | /* STUDENT TODO */ number_of_units; 30 | /* STUDENT TODO */ quarter; 31 | }; 32 | 33 | /** 34 | * (STUDENT TODO) Look at how the main function (at the bottom of this file) 35 | * calls `parse_csv`, `write_courses_offered`, and `write_courses_not_offered`. 36 | * Modify the signatures of these functions so that they work as intended, and then delete this 37 | * comment! 38 | */ 39 | 40 | /** 41 | * Note: 42 | * We need to #include utils.cpp _after_ we declare the Course struct above 43 | * so that the code inside utils.cpp knows what a Course is. 44 | * Recall that #include literally copies and pastes file contents. 45 | */ 46 | #include "utils.cpp" 47 | 48 | /** 49 | * This function should populate the `courses` vector with structs of type 50 | * `Course`. We want to create these structs with the records in the courses.csv 51 | * file, where each line is a record! 52 | * 53 | * Hints: 54 | * 1) Take a look at the split function we provide in utils.cpp 55 | * 2) Each LINE is a record! *this is important, so we're saying it again :>)* 56 | * 3) The first line in the CSV defines the column names, so you can ignore it! 57 | * 58 | * @param filename The name of the file to parse. 59 | * @param courses A vector of courses to populate. 60 | */ 61 | void parse_csv(std::string filename, std::vector<Course> courses) { 62 | /* (STUDENT TODO) Your code goes here... */ 63 | } 64 | 65 | /** 66 | * This function has TWO requirements. 67 | * 68 | * 1) Write the courses that are offered to the file 69 | * "student_output/courses_offered.csv" 70 | * 71 | * 2) Delete the courses that are offered from the `all_courses` vector. 72 | * IMPORTANT: do this after you write out to the file! 73 | * 74 | * HINTS: 75 | * 1) Keep track of the classes that you need to delete! 76 | * 2) Use the delete_elem_from_vector function we give you! 77 | * 3) Remember to write the CSV column headers at the beginning of the output! 78 | * See courses.csv for reference. 79 | * 80 | * @param all_courses A vector of all courses gotten by calling `parse_csv`. 81 | * This vector will be modified by removing all offered courses. 82 | */ 83 | void write_courses_offered(std::vector<Course> all_courses) { 84 | /* (STUDENT TODO) Your code goes here... */ 85 | } 86 | 87 | /** 88 | * This function writes the courses NOT offered to the file 89 | * "student_output/courses_not_offered.csv". 90 | * 91 | * This function is ALWAYS called after the `write_courses_offered` function. 92 | * `unlisted_courses` will trivially contain classes that are not offered 93 | * since you delete offered classes from `all_courses` in the 94 | * `write_courses_offered` function. 95 | * 96 | * HINT: This should be VERY similar to `write_courses_offered` 97 | * 98 | * @param unlisted_courses A vector of courses that are not offered. 99 | */ 100 | void write_courses_not_offered(std::vector<Course> unlisted_courses) { 101 | /* (STUDENT TODO) Your code goes here... */ 102 | } 103 | 104 | int main() { 105 | /* Makes sure you defined your Course struct correctly! */ 106 | static_assert(is_valid_course<Course>, "Course struct is not correctly defined!"); 107 | 108 | std::vector<Course> courses; 109 | parse_csv("courses.csv", courses); 110 | 111 | /* Uncomment for debugging... */ 112 | // print_courses(courses); 113 | 114 | write_courses_offered(courses); 115 | write_courses_not_offered(courses); 116 | 117 | return run_autograder(); 118 | } -------------------------------------------------------------------------------- /assignment1/student_output/courses_not_offered.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment1/student_output/courses_not_offered.csv -------------------------------------------------------------------------------- /assignment1/student_output/courses_offered.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment1/student_output/courses_offered.csv -------------------------------------------------------------------------------- /assignment1/utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CS106L Assignment 1: SimpleEnroll 3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca. 4 | * 5 | * This file contains utility functions that you may find useful while 6 | * implementing the assignment. You don't need to modify anything here, 7 | * but feel free to look around. 8 | */ 9 | 10 | /** 11 | * Forward declarations of some important methods. Don't worry about these! 12 | * (unless you really want to). 13 | */ 14 | bool operator==(const Course& lhs, const Course& rhs); 15 | std::ostream& operator<<(std::ostream& os, const Course& course); 16 | 17 | /** 18 | * Splits a string by a delimiter and returns a vector of the split strings. 19 | * @param s The string to split. 20 | * @param delim The delimiter to split by. 21 | * @returns A vector of the split strings. 22 | */ 23 | std::vector<std::string> split(const std::string& s, char delim) { 24 | std::vector<std::string> return_vec; 25 | std::stringstream ss(s); 26 | std::string token; 27 | while (std::getline(ss, token, delim)) { 28 | return_vec.push_back(token); 29 | } 30 | return return_vec; 31 | } 32 | 33 | /** 34 | * Deletes a Course from a vector of courses. 35 | * @param v The vector of courses. 36 | * @param elem The course to delete. 37 | * 38 | * We will learn more about what's going on in this function in Weeks 3 and 5! 39 | */ 40 | void delete_elem_from_vector(std::vector<Course>& v, const Course& elem) { 41 | std::vector<Course>::iterator it = std::find(v.begin(), v.end(), elem); 42 | v.erase(it); 43 | } 44 | 45 | /** 46 | * Prints a vector of courses (for debugging purposes)! 47 | * @param vector_of_courses The vector of courses to print. 48 | */ 49 | void print_courses(const std::vector<Course>& vector_of_courses) { 50 | for (const Course& course : vector_of_courses) { 51 | std::cout << course << std::endl; 52 | } 53 | } 54 | 55 | /* ========================================================================= * 56 | * Don't worry about anything beyond this point. * 57 | * (unless you are really curious and want to!) * 58 | * ========================================================================= */ 59 | 60 | bool operator==(const Course& lhs, const Course& rhs) { 61 | return lhs.title == rhs.title && lhs.number_of_units == rhs.number_of_units && 62 | lhs.quarter == rhs.quarter; 63 | } 64 | 65 | std::ostream& operator<<(std::ostream& os, const Course& course) { 66 | os << course.title << ", " << course.number_of_units << ", " << course.quarter; 67 | return os; 68 | } 69 | 70 | template <typename T> 71 | concept is_valid_course = requires(T t) { 72 | { T{"Standard C++ Programming", "1", "2023-2024 Winter"} }; 73 | std::is_same_v<T, Course>; 74 | }; 75 | 76 | int 77 | run_autograder() 78 | { 79 | auto run_program = [](std::string program, 80 | std::initializer_list<std::string> args, 81 | bool silent = false) { 82 | std::stringstream ss; 83 | 84 | ss << program; 85 | for (const auto& arg : args) { 86 | ss << ' ' << arg; 87 | } 88 | 89 | if (silent) { 90 | #ifdef _WIN32 91 | ss << " >nul 2>&1"; 92 | #else 93 | ss << " >/dev/null 2>&1"; 94 | #endif 95 | } 96 | 97 | std::cout.flush(); 98 | return system(ss.str().c_str()); 99 | }; 100 | 101 | std::string python; 102 | for (const auto& option : 103 | { "python", "python3", "/usr/bin/python3", "/usr/bin/python" }) { 104 | if (run_program(option, { "--version" }, true) == 0) { 105 | python = option; 106 | break; 107 | } 108 | } 109 | 110 | if (python.empty()) { 111 | std::cerr 112 | << "Python was not found on your system. Please install Python and " 113 | "try again." 114 | << "\n"; 115 | std::exit(1); 116 | } 117 | 118 | return run_program(python, { "autograder/autograder.py" }); 119 | } -------------------------------------------------------------------------------- /assignment2/README.md: -------------------------------------------------------------------------------- 1 | <p align="center"> 2 | <img src="docs/marriage_pact.png" alt="Marriage Pact Logo" /> 3 | </p> 4 | 5 | # Assignment 2: Marriage Pact 6 | 7 | Due Friday, May 2nd at 11:59PM 8 | 9 | ## Overview 10 | 11 | Happy assignment 2! This is meant to be a very short and sweet bit of practice to get you started working with the STL's containers and pointers. 12 | 13 | These are the files you need to care about: 14 | 15 | - `main.cpp`: All your code goes here 😀! 16 | - `short_answer.txt`: Short answer responses go here 📝! 17 | 18 | To download the starter code for this assignment, please see the instructions for [**Getting Started**](../README.md#getting-started) on the course assignments repository. 19 | 20 | ## Running your code 21 | 22 | To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit <kbd>Ctrl+\`</kbd> or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assignment2/` directory and run: 23 | 24 | ```sh 25 | g++ -std=c++20 main.cpp -o main 26 | ``` 27 | 28 | Assuming that your code compiles without any compiler errors, you can now do: 29 | 30 | ```sh 31 | ./main 32 | ``` 33 | 34 | which will actually run the `main` function in `main.cpp`. 35 | 36 | As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track! 37 | 38 | > [!NOTE] 39 | > 40 | > ### Note for Windows 41 | > 42 | > On Windows, you may need to compile your code using 43 | > 44 | > ```sh 45 | > g++ -static-libstdc++ -std=c++20 main.cpp -o main 46 | > ``` 47 | > 48 | > in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with: 49 | > 50 | > ```sh 51 | > ./main.exe 52 | > ``` 53 | 54 | ## Part 0: Setup 55 | 56 | Welcome to the Marriage Pact! Before you begin, we'll need to know your name. Please change the constant `kYourName` at the top of `main.cpp` from `"STUDENT TODO"` to your full name (with a space between first and last). 57 | 58 | ## Part 1: Get all applicants 59 | 60 | You’ve been waiting for days to get your Marriage Pact initials this year, and they’ve finally arrived in your inbox! This year, they’re implementing a new rule: your match MUST share your own initials to be eligible. However, even after talking about it for hours with your friends, you have no idea who your match could be! There are thousands of students on campus, and you can’t just go through the whole roster by hand to draft up a list of your potential soulmates. Fortunately enough for you, you’re in CS106L, and you remember that C++ has a pretty quick method of going through collected, similar information – containers! 61 | 62 | We’ve included a `.txt` file of all of the (fictional) students who signed up for The Marriage Pact this year (`students.txt`). Each line includes the first and last name of the student. You will first write the function `get_applicants`: 63 | 64 | > [!IMPORTANT] 65 | > 66 | > ### `get_applicants` 67 | > 68 | > From the `.txt` file, parse all of the names into a set. Each line contained in the file named `filename` is a single applicant's name. In your implementation, you are free to choose between an ordered (`std::set`) and unordered set (`std::unordered_set`) as you wish! If you do choose to use an unordered set, please change the relevant function definitions! 69 | 70 | Additionally, please answer the following short answer question in `short_answer.txt`: 71 | 72 | > [!IMPORTANT] 73 | > 74 | > ### `short_answer.txt` 75 | > 76 | > **Q1:** It is your choice to use either an ordered or unordered set. In a few sentences, what are some of the tradeoffs between the two? Additionally, please give an example (that has not been shown in lecture) of a valid hash function that could be used to hash student names for an unordered set. 77 | 78 | > [!NOTE] 79 | > All names appearing in this assignment are fictitious. Any resemblance to real persons, living or dead, is purely coincidental. 80 | 81 | ## Part 2: Find matches 82 | 83 | Great detective work! Now that you’ve narrowed down your list of potential soulmates, it’s time to put it to the test. After a long day of acapella and consulting club meetings, you return to your dorm to learn from your roommate that there is a mixer for Marriage Pact matches at Main Quad that night! Your best chance of finding true love is imminent — if only you can get out of your Ultimate Frisbee practice. Quickly, you decide to interview everyone who shares your initials at the mixer, and you get to work coding up a function that will compile the order for you automatically. 84 | 85 | For this section, you will write the functions `find_matches` and `get_match`: 86 | 87 | > [!IMPORTANT] 88 | > 89 | > ### `find_matches` 90 | > 91 | > From the set `students` (generated in the previous part), take all names that share initials with the parameter `name` and place pointers to them in a new `std::queue`. 92 | > 93 | > - If you’re having trouble figuring out how to iterate through a set, it could be helpful to look back over [Thursday’s lecture on iterators and pointers](https://office365stanford-my.sharepoint.com/:p:/g/personal/jtrb_stanford_edu/EbOKUV784rBHrO3JIhUSAUgBvuIGn5rSU8h3xbq-Q1JFfQ?e=BlZwa7). 94 | > - You will need to be familiar with the operations of a `std::queue` for this part. Take a look at cppreference's documentation [here](https://en.cppreference.com/w/cpp/container/queue). 95 | > - Hint: It might help to define a helper function that computes the initials of some student's name. Then you can use that helper function to compare the initials of `name` with the initials of each name in `students`. 96 | 97 | From here please implement the function `get_match` to find your “one true match.”: 98 | 99 | > [!IMPORTANT] 100 | > 101 | > ### `get_match` 102 | > 103 | > Gets your “one true match” from the queue of all possible matches. This can be determined as you see fit; choose some method of acquiring one student from the queue, ideally something with a bit more thought than a single `pop()`, but it doesn’t have to be particularly complicated! Consider random values or other methods of selection. 104 | > 105 | > If your initials have no matches in the dataset, print `“NO MATCHES FOUND.”` Better luck next year 😢 106 | 107 | Afterwards, answer the following question in `short_answer.txt`: 108 | 109 | > [!IMPORTANT] 110 | > 111 | > ### `short_answer.txt` 112 | > 113 | > **Q2:** Note that we are saving pointers to names in the queue, not names themselves. Why might this be desired in this problem? What happens if the original set where the names are stored goes out of scope and the pointers are referenced? 114 | 115 | ## 🚀 Submission Instructions 116 | 117 | Before you submit the assignment, please fill out this [short feedback form](https://forms.gle/jTQSpVjesp1F6MEp6). **Completion of the form is required to receive credit for the assignment.** After filling out the form, please upload the files to Paperless under the correct assignment heading. 118 | 119 | Your deliverable should be: 120 | 121 | - `main.cpp` 122 | - `short_answer.txt` 123 | 124 | You may resubmit as many times as you'd like before the deadline. 125 | -------------------------------------------------------------------------------- /assignment2/autograder/autograder.py: -------------------------------------------------------------------------------- 1 | from utils import Autograder 2 | import os 3 | 4 | PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 5 | AUTOGRADER_DIR = os.path.join(PATH, "autograder") 6 | TEST_FILE_PATH = os.path.join(PATH, "students.txt") 7 | 8 | 9 | def get_initials(name): 10 | parts = name.split() 11 | if len(parts) == 2: 12 | return parts[0][0] + parts[1][0] 13 | return None 14 | 15 | 16 | def read_students_file(filename): 17 | with open(filename, "r") as file: 18 | return {line.strip() for line in file.readlines()} 19 | 20 | 21 | def test_applicants_set(): 22 | expected_possible_matches = read_students_file(TEST_FILE_PATH) 23 | 24 | student_output_set_path = os.path.join(AUTOGRADER_DIR, "student_output/set.txt") 25 | 26 | with open(student_output_set_path, "r") as possible_matches_file: 27 | possible_matches = {line.strip() for line in possible_matches_file.readlines()} 28 | 29 | if possible_matches != expected_possible_matches: 30 | raise RuntimeError( 31 | f"❌ Output does not match expected set of possible matches. Diff: {possible_matches ^ expected_possible_matches}" 32 | ) 33 | 34 | 35 | def test_match(): 36 | all_students = read_students_file(TEST_FILE_PATH) 37 | 38 | student_output_match_path = os.path.join(AUTOGRADER_DIR, "student_output/match.txt") 39 | 40 | with open(student_output_match_path, "r") as student_file: 41 | lines = [line.strip() for line in student_file.readlines()] 42 | 43 | if len(lines) < 2: 44 | raise RuntimeError("❌ Student name and match not found in match.txt file") 45 | 46 | student_name = lines[0] 47 | student_match = lines[1] 48 | 49 | if student_name == "STUDENT TODO": 50 | raise RuntimeError("❌ You must replace kYourName in main.cpp with your name!") 51 | 52 | student_initials = get_initials(student_name) 53 | if not student_initials: 54 | raise RuntimeError(f"❌ Invalid student name format: {student_name}") 55 | 56 | expected_matches = { 57 | name for name in all_students if get_initials(name) == student_initials 58 | } 59 | 60 | match = student_match.split("Your match is: ")[1].strip() 61 | 62 | if match != "NO MATCHES FOUND.": 63 | if match not in expected_matches: 64 | raise RuntimeError( 65 | f"Matched student '{match}' is not in the expected set of matches: {expected_matches}" 66 | ) 67 | print(f"✅ Matched student '{match}'") 68 | 69 | 70 | if __name__ == "__main__": 71 | grader = Autograder() 72 | grader.add_part("test_applicants_set", test_applicants_set) 73 | grader.add_part("test_match", test_match) 74 | 75 | grader.setup = None 76 | grader.teardown = None 77 | grader.run() 78 | -------------------------------------------------------------------------------- /assignment2/autograder/requirements.txt: -------------------------------------------------------------------------------- 1 | colorama==0.4.6 -------------------------------------------------------------------------------- /assignment2/autograder/student_output/match.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment2/autograder/student_output/match.txt -------------------------------------------------------------------------------- /assignment2/autograder/student_output/set.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment2/autograder/student_output/set.txt -------------------------------------------------------------------------------- /assignment2/autograder/utils.hpp: -------------------------------------------------------------------------------- 1 | #include <fstream> 2 | #include <iostream> 3 | #include <sstream> 4 | #include <string> 5 | 6 | int 7 | run_autograder() 8 | { 9 | auto run_program = [](std::string program, 10 | std::initializer_list<std::string> args, 11 | bool silent = false) { 12 | std::stringstream ss; 13 | 14 | ss << program; 15 | for (const auto& arg : args) { 16 | ss << ' ' << arg; 17 | } 18 | 19 | if (silent) { 20 | #ifdef _WIN32 21 | ss << " >nul 2>&1"; 22 | #else 23 | ss << " >/dev/null 2>&1"; 24 | #endif 25 | } 26 | 27 | std::cout.flush(); 28 | return system(ss.str().c_str()); 29 | }; 30 | 31 | std::string python; 32 | for (const auto& option : 33 | { "python", "python3", "/usr/bin/python3", "/usr/bin/python" }) { 34 | if (run_program(option, { "--version" }, true) == 0) { 35 | python = option; 36 | break; 37 | } 38 | } 39 | 40 | if (python.empty()) { 41 | std::cerr 42 | << "Python was not found on your system. Please install Python and " 43 | "try again." 44 | << "\n"; 45 | std::exit(1); 46 | } 47 | 48 | /* #### Assignment Specific Operations #### */ 49 | auto allMatches = get_applicants("students.txt"); 50 | auto studentMatches = find_matches(kYourName, allMatches); 51 | auto match = get_match(studentMatches); 52 | 53 | std::fstream matchFile("autograder/student_output/match.txt"); 54 | std::fstream setFile("autograder/student_output/set.txt"); 55 | 56 | matchFile << kYourName << '\n' << "Your match is: " << match << '\n'; 57 | for (const auto& student : allMatches) { 58 | setFile << student << '\n'; 59 | } 60 | 61 | // Flush streams so that the Python autograder is guaranteed to see their 62 | // changes 63 | matchFile.flush(); 64 | setFile.flush(); 65 | /* #### End of Assignment Specific Operations #### */ 66 | 67 | return run_program(python, { "autograder/autograder.py" }); 68 | } 69 | 70 | int 71 | main() 72 | { 73 | return run_autograder(); 74 | } -------------------------------------------------------------------------------- /assignment2/docs/marriage_pact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment2/docs/marriage_pact.png -------------------------------------------------------------------------------- /assignment2/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CS106L Assignment 2: Marriage Pact 3 | * Created by Haven Whitney with modifications by Fabio Ibanez & Jacob Roberts-Baca. 4 | * 5 | * Welcome to Assignment 2 of CS106L! Please complete each STUDENT TODO 6 | * in this file. You do not need to modify any other files. 7 | * 8 | */ 9 | 10 | #include <fstream> 11 | #include <iostream> 12 | #include <queue> 13 | #include <set> 14 | #include <string> 15 | #include <unordered_set> 16 | 17 | std::string kYourName = "STUDENT TODO"; // Don't forget to change this! 18 | 19 | /** 20 | * Takes in a file name and returns a set containing all of the applicant names as a set. 21 | * 22 | * @param filename The name of the file to read. 23 | * Each line of the file will be a single applicant's name. 24 | * @returns A set of all applicant names read from the file. 25 | * 26 | * @remark Feel free to change the return type of this function (and the function 27 | * below it) to use a `std::unordered_set` instead. If you do so, make sure 28 | * to also change the corresponding functions in `utils.h`. 29 | */ 30 | std::set<std::string> get_applicants(std::string filename) { 31 | // STUDENT TODO: Implement this function. 32 | } 33 | 34 | /** 35 | * Takes in a set of student names by reference and returns a queue of names 36 | * that match the given student name. 37 | * 38 | * @param name The returned queue of names should have the same initials as this name. 39 | * @param students The set of student names. 40 | * @return A queue containing pointers to each matching name. 41 | */ 42 | std::queue<const std::string*> find_matches(std::string name, std::set<std::string>& students) { 43 | // STUDENT TODO: Implement this function. 44 | } 45 | 46 | /** 47 | * Takes in a queue of pointers to possible matches and determines the one true match! 48 | * 49 | * You can implement this function however you'd like, but try to do something a bit 50 | * more complicated than a simple `pop()`. 51 | * 52 | * @param matches The queue of possible matches. 53 | * @return Your magical one true love. 54 | * Will return "NO MATCHES FOUND." if `matches` is empty. 55 | */ 56 | std::string get_match(std::queue<const std::string*>& matches) { 57 | // STUDENT TODO: Implement this function. 58 | } 59 | 60 | /* #### Please don't remove this line! #### */ 61 | #include "autograder/utils.hpp" 62 | -------------------------------------------------------------------------------- /assignment2/short_answer.txt: -------------------------------------------------------------------------------- 1 | Before submitting this file, make sure that there are no more TODO 2 | placeholders remaining in the file (and remove this comment too). 3 | 4 | Marriage Pact 5 | ------------- 6 | 7 | Q1. It is your choice to use either an ordered or unordered set. In a few sentences, what are some of the tradeoffs between the two? Additionally, please give an example (that has not been shown in lecture) of a valid hash function that could be used to hash student names for an unordered set. 8 | A1. TODO 9 | 10 | Q2. Note that we are saving pointers to names in the queue, not names themselves. Why might this be desired in this problem? What happens if the original set where the names are stored goes out of scope and the pointers are referenced? 11 | A2. TODO -------------------------------------------------------------------------------- /assignment3/README.md: -------------------------------------------------------------------------------- 1 | <p align="center"> 2 | <img src="docs/bjarne.jpg" alt="Bjarne Stroustrup writing the declaration of a class on a whiteboard" /> 3 | </p> 4 | 5 | # Assignment 3: Make a Class 6 | 7 | Due Friday, May 9th at 11:59PM 8 | 9 | ## Overview 10 | 11 | <pre> 12 | (\_/) 13 | (•x•) <b>Howdy</b> 14 | (<☕) 15 | </pre> 16 | 17 | Now that we've learned about classes, it’s time for you to make your own! Have fun with this, let the creative juices flow. Your class can represent anything, and feel free to make more than one if you'd like. There are some requirements though. As long as you meet these requirements, you’ll get credit for this assignment! 🙂 18 | 19 | There are four files you'll work with for this assignment: 20 | 21 | * `class.h` - This is the header file for your class, where the class **declaration** will go. 22 | * `class.cpp` - This is the `.cpp` file for your class, where the class **definition** will go. 23 | * `sandbox.cpp` - You'll construct an instance of your class here. 24 | * `short_answer.txt` - You'll answer a few short answer questions here. 25 | 26 | To download the starter code for this assignment, please see the instructions for [**Getting Started**](../README.md#getting-started) on the course assignments repository. 27 | 28 | ## Running your code 29 | 30 | To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit <kbd>Ctrl+\`</kbd> or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assignment3/` directory and run: 31 | 32 | ```sh 33 | g++ -std=c++20 main.cpp class.cpp -o main 34 | ``` 35 | 36 | Assuming that your code compiles without any compiler errors, you can now do: 37 | 38 | ```sh 39 | ./main 40 | ``` 41 | 42 | which will actually run the `main` function in `main.cpp`. 43 | 44 | As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track! 45 | 46 | > [!NOTE] 47 | > 48 | > ### Note for Windows 49 | > 50 | > On Windows, you may need to compile your code using 51 | > 52 | > ```sh 53 | > g++ -static-libstdc++ -std=c++20 main.cpp class.cpp -o main 54 | > ``` 55 | > 56 | > in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with: 57 | > 58 | > ```sh 59 | > ./main.exe 60 | > ``` 61 | 62 | ## Part 1: Making your class 63 | 64 | Let your creative juices flow! Fill in `class.h` and `class.cpp` to create your own custom class. Please refer to the relevant slides from Tuesday's lecture on classes for more information. Your class can represent pretty much anything you want, as long as it meets the following requirements. 65 | 66 | > [!IMPORTANT] 67 | > ### Class Requirements 68 | > 69 | > Your class must: 70 | > 1. Have a custom constructor taking **one or more** parameters. 71 | > 2. Have a default (parameterless) constructor (i.e. constructor overloading). 72 | > 3. Have one or more private member fields (i.e. variables). 73 | > 4. Have one or more private member functions. 74 | > - Remember, private functions are like what happens underneath the hood of your car! They are a necessary part of the implementation of a class, but shouldn't be exposed in the public interface. Try to think of a private member function that logically makes sense in the context of your class. 75 | > 5. Have **at least one** public getter function for one of the private fields. 76 | > - E.g. if `int data` is the field, you must have a function called `get_data` or `getData` with the signature <pre lang="cpp">int getData();</pre> 77 | > - The getter function should also be marked `const`. Refer to Thursday's lecture on `const` correctness if you are unfamiliar! 78 | > 6. Have at least one public setter function for one of the private fields. 79 | > - E.g. if `int data` is the field, you must have a function called `set_data` or `setData` with the signature <pre lang="cpp">void setData(int value);</pre> 80 | 81 | Note that this is the bare minimum to get credit for the assignment. Please feel free to go above and beyond these requirements or create more than one class if you want extra practice! 82 | 83 | > [!NOTE] 84 | > For brownie points, you can choose to create a class template instead of a regular class using the `template <typename T>` notation discussed on Thursday's lecture. This is totally optional! 85 | > 86 | > Note that if you do decide to create a class template, you **must remove class.cpp 87 | > from the compilation command.** For example, on Mac/Linux, the compilation 88 | > command will be: 89 | > 90 | > ```sh 91 | > g++ -std=c++20 main.cpp -o main 92 | > ``` 93 | > 94 | > Remember to also swap the includes so that the `.h` file includes the `.cpp` 95 | > file at the end of the file, as discussed in Thursday's lecture. 96 | 97 | Now that you've created your class, let's actually use it. **Inside of the `sandbox` function in `sandbox.cpp`, construct an instance of your class!** You can do so however you like (call default constructor, use uniform initialization, etc.). 98 | 99 | To see if you did everything correctly, compile and run your code! The autograder will give you feedback on your class and check if it meets the specifications above. 100 | 101 | ## Part 2: Short answer questions 102 | 103 | Please answer the following questions inside `short_answer.txt`. We expect about 2-3 sentences per question. 104 | 105 | > [!IMPORTANT] 106 | > `short_answer.txt` 107 | > - **Q1:** What’s const-correctness and why is it important? 108 | > - **Q2:** Is your class const-correct? How do you know? 109 | 110 | ## 🚀 Submission Instructions 111 | 112 | Before you submit the assignment, please fill out this [short feedback form](https://forms.gle/BMHGeNPjotMS9njK7). **Completion of the form is required to receive credit for the assignment.** After filling out the form, please upload the files to Paperless under the correct assignment heading. 113 | 114 | Your deliverable should be: 115 | 116 | * `class.h` 117 | * `class.cpp` 118 | * `sandbox.cpp` 119 | * `short_answer.txt` 120 | 121 | You may resubmit as many times as you'd like before the deadline. 122 | -------------------------------------------------------------------------------- /assignment3/autograder/autograder.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, List, Set, Tuple, TypeVar 2 | from utils import Autograder, get_declarations, install_castxml 3 | install_castxml() 4 | 5 | import os 6 | import re 7 | 8 | from pygccxml import declarations 9 | from colorama import Fore, Style, Back 10 | 11 | PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 12 | AUTOGRADER_DIR = os.path.join(PATH, "autograder") 13 | 14 | class_decl: declarations.class_t = None 15 | definitions: Set[Tuple[str, str]] = None 16 | 17 | 18 | def norm_path(path: os.PathLike) -> os.PathLike: 19 | return os.path.normpath(os.path.relpath(path, os.getcwd())) 20 | 21 | 22 | def get_definitions(source_file: os.PathLike) -> Set[Tuple[str, str]]: 23 | if not os.path.exists(source_file): 24 | raise RuntimeError(f"Could not find source file: {source_file}") 25 | 26 | with open(source_file, "r") as file: 27 | content = file.read() 28 | 29 | comment_pattern = r"(\".*?\"|\'.*?\')|(/\*.*?\*/|//[^\r\n]*$)" 30 | regex = re.compile(comment_pattern, re.MULTILINE | re.DOTALL) 31 | content = regex.sub( 32 | lambda match: "" if match.group(2) is not None else match.group(1), content 33 | ) 34 | 35 | string_pattern = r"\".*?\"" 36 | regex = re.compile(string_pattern, re.MULTILINE | re.DOTALL) 37 | content = regex.sub("", content) 38 | 39 | def remove_nested_braces(content): 40 | result, stack = [], [] 41 | for char in content: 42 | if char == "{": 43 | stack.append("{") 44 | elif char == "}": 45 | stack.pop() if stack else None 46 | elif not stack: 47 | result.append(char) 48 | return "".join(result) 49 | 50 | content = remove_nested_braces(content) 51 | 52 | definition_pattern = r"(\w+)(?:<.*>)?::(\w+)" 53 | regex = re.compile(definition_pattern) 54 | matches = regex.findall(content) 55 | 56 | return matches 57 | 58 | 59 | def assert_defined(decl: declarations.declarated_t): 60 | class_name = class_decl.name 61 | class_name = re.sub(r"<.*>", "", class_name) 62 | 63 | decl_name: str = None 64 | if isinstance(decl, declarations.constructor_t): 65 | decl_name = decl.name 66 | elif isinstance(decl, declarations.member_function_t): 67 | decl_name = decl.name 68 | else: 69 | raise RuntimeError( 70 | f"Unhandled declaration type: {type(decl)}. Please reach out on Ed!" 71 | ) 72 | 73 | decl_tuple = (class_name, decl_name) 74 | if decl_tuple not in definitions: 75 | raise RuntimeError( 76 | f"Found declaration for {decl}, but could not find a matching definition in class.cpp" 77 | ) 78 | 79 | 80 | def skip_decl(decl: declarations.declaration_t, reason: str): 81 | print(f"⏩ Disregarding {decl}, {reason}") 82 | 83 | 84 | def find_decl(decl: declarations.declaration_t): 85 | print(f"🔍 Found {decl}!") 86 | 87 | 88 | T = TypeVar("T") 89 | 90 | 91 | def get_decls(getter: Callable[[], T], kind_plural: str, scope: str = "class") -> T: 92 | try: 93 | return getter() 94 | except RuntimeError as err: 95 | if "query returned 0 declarations" in str(err): 96 | raise RuntimeError(f"Could not find any {kind_plural} in {scope}") 97 | 98 | 99 | def setup(): 100 | sandbox_cpp_path = os.path.join(PATH, "sandbox.cpp") 101 | class_h_path = os.path.join(PATH, "class.h") 102 | class_cpp_path = os.path.join(PATH, "class.cpp") 103 | 104 | if not os.path.isfile(sandbox_cpp_path): 105 | raise RuntimeError( 106 | "Couldn't find '{main.cpp}'. Did you delete it from the starter code?" 107 | ) 108 | 109 | decls = get_declarations(norm_path(sandbox_cpp_path)) 110 | global_namespace = declarations.get_global_namespace(decls) 111 | 112 | # Try to find a class inside of class.h 113 | def find_class_decl() -> declarations.class_t: 114 | try: 115 | classes = global_namespace.classes() 116 | except: 117 | classes = [] 118 | cls_decl: declarations.class_t 119 | for cls_decl in classes: 120 | location: declarations.location_t = cls_decl.location 121 | if norm_path(class_h_path) == norm_path(location.file_name): 122 | return cls_decl 123 | raise Exception( 124 | "Couldn't find a class inside of class.h. Possible reasons:\n" 125 | " - Did you define one?\n" 126 | ' - Did you #include "class.h" inside main.cpp?\n' 127 | " - Did you construct an instance of the class inside main.cpp?" 128 | ) 129 | 130 | global class_decl 131 | class_decl = find_class_decl() 132 | print( 133 | f"{Fore.GREEN}Autograder found class {Back.LIGHTGREEN_EX}{class_decl.name}{Back.RESET} inside class.h!{Style.RESET_ALL}" 134 | ) 135 | 136 | global definitions 137 | definitions = get_definitions(class_cpp_path) 138 | 139 | 140 | def test_parameterized_constructor(): 141 | constructors = get_decls(class_decl.constructors, "constructors") 142 | cons: declarations.constructor_t 143 | for cons in constructors: 144 | if cons.is_artificial: 145 | continue 146 | if cons.access_type != declarations.ACCESS_TYPES.PUBLIC: 147 | skip_decl(cons, "not public") 148 | continue 149 | if len(cons.arguments) == 0: 150 | skip_decl(cons, "has no arguments") 151 | continue 152 | 153 | assert_defined(cons) 154 | find_decl(cons) 155 | return 156 | 157 | raise RuntimeError( 158 | "Could not find a public constructor taking one or more arguments" 159 | ) 160 | 161 | 162 | def test_parameterless_constructor(): 163 | constructors = get_decls(class_decl.constructors, "constructors") 164 | cons: declarations.constructor_t 165 | for cons in constructors: 166 | if cons.is_artificial: 167 | continue 168 | if cons.access_type != declarations.ACCESS_TYPES.PUBLIC: 169 | skip_decl(cons, "not public") 170 | continue 171 | if len(cons.arguments) > 0: 172 | skip_decl(cons, "has one or more parameters") 173 | continue 174 | 175 | assert_defined(cons) 176 | find_decl(cons) 177 | return 178 | 179 | raise RuntimeError("Could not find a public parameterless constructor") 180 | 181 | 182 | def test_private_member_fields(): 183 | fields = get_decls(class_decl.variables, "fields") 184 | field: declarations.variable_t 185 | for field in fields: 186 | if field.is_artificial: 187 | continue 188 | if field.type_qualifiers.has_static: 189 | skip_decl(field, "marked static") 190 | continue 191 | if field.type_qualifiers.has_extern: 192 | skip_decl(field, "marked extern") 193 | continue 194 | if field.access_type == declarations.ACCESS_TYPES.PUBLIC: 195 | skip_decl( 196 | field, 197 | "not private. Note: In general, it is bad practice to define public fields!", 198 | ) 199 | continue 200 | if field.access_type != declarations.ACCESS_TYPES.PRIVATE: 201 | skip_decl(field, "not private") 202 | continue 203 | 204 | find_decl(field) 205 | return 206 | 207 | raise RuntimeError("Could not find a private field") 208 | 209 | 210 | def test_private_member_functions(): 211 | functions = get_decls(class_decl.member_functions, "member functions") 212 | function: declarations.member_function_t 213 | for function in functions: 214 | if function.is_artificial: 215 | continue 216 | if function.has_static: 217 | skip_decl(function, "marked static") 218 | continue 219 | if function.has_extern: 220 | skip_decl(function, "marked extern") 221 | continue 222 | if function.access_type != declarations.ACCESS_TYPES.PRIVATE: 223 | skip_decl(function, "not private") 224 | continue 225 | 226 | find_decl(function) 227 | assert_defined(function) 228 | return 229 | 230 | raise RuntimeError("Could not find a private member function") 231 | 232 | 233 | def get_private_fields() -> List[declarations.variable_t]: 234 | def is_private(field: declarations.variable_t) -> bool: 235 | if field.is_artificial: 236 | return False 237 | if field.type_qualifiers.has_static: 238 | return False 239 | if field.type_qualifiers.has_extern: 240 | return False 241 | return field.access_type == declarations.ACCESS_TYPES.PRIVATE 242 | 243 | fields = get_decls(class_decl.variables, "fields") 244 | return [field for field in fields if is_private(field)] 245 | 246 | 247 | def get_prefix_functions( 248 | prefix: str, 249 | ) -> List[Tuple[declarations.member_function_t, str]]: 250 | name_regex = re.compile(rf"{prefix}_?([a-zA-Z]\w*)") 251 | 252 | funcs = [] 253 | func: declarations.member_function_t 254 | for func in get_decls(class_decl.member_functions, "member functions"): 255 | if func.is_artificial: 256 | continue 257 | if func.has_static: 258 | continue 259 | if func.has_extern: 260 | continue 261 | if func.access_type != declarations.ACCESS_TYPES.PUBLIC: 262 | continue 263 | match = name_regex.match(func.name) 264 | if match is None: 265 | continue 266 | funcs.append((func, match.group(1))) 267 | return funcs 268 | 269 | 270 | def find_matching_function(prefix: str, type: str): 271 | fields = get_private_fields() 272 | field_names = {f.name.lower().strip("_"): f for f in fields} 273 | funcs = get_prefix_functions(prefix) 274 | 275 | for func, field_name in funcs: 276 | field_name_lower = field_name.lower() 277 | if field_name_lower not in field_names: 278 | skip_decl(func, f"{field_name} did not match a private field") 279 | continue 280 | 281 | find_decl(func) 282 | return func, field_names[field_name_lower] 283 | 284 | raise RuntimeError( 285 | f"No {type} function found for a private field. " 286 | f"Options were:\n - {type.title()}s: [{', '.join(p[0].name for p in funcs)}]" 287 | f"\n - Private fields: [{', '.join(f.name for f in fields)}]" 288 | ) 289 | 290 | 291 | def test_getter_function(): 292 | function, field = find_matching_function("get", "getter") 293 | assert len(function.arguments) == 0, "A getter function must have no arguments" 294 | assert ( 295 | function.return_type == field.decl_type 296 | ), f"The return type of a getter function must match its field. Found {function.return_type} but expected {field.decl_type}" 297 | assert ( 298 | function.has_const 299 | ), "A getter function should be marked as const (refer to Thursday's lecture on const correctness)" 300 | assert_defined(function) 301 | 302 | 303 | def test_setter_function(): 304 | function, field = find_matching_function("set", "setter") 305 | assert ( 306 | len(function.arguments) == 1 307 | ), "A setter should have a single argument matching the type of its field" 308 | 309 | assert declarations.base_type(function.argument_types[0]) == declarations.base_type( 310 | field.decl_type 311 | ), ( 312 | f"The argument of a setter should be the type of its field. " 313 | f"Found {function.argument_types[0]} but expected {field.decl_type}" 314 | ) 315 | assert ( 316 | function.return_type == declarations.void_t() 317 | ), "A setter should have a void return type" 318 | assert_defined(function) 319 | 320 | 321 | if __name__ == "__main__": 322 | grader = Autograder() 323 | grader.setup = setup 324 | grader.add_part( 325 | "#1 / Public parameterized constructor", test_parameterized_constructor 326 | ) 327 | grader.add_part( 328 | "#2 / Public parameterless constructor", test_parameterless_constructor 329 | ) 330 | grader.add_part("#3 / Private field", test_private_member_fields) 331 | grader.add_part("#4 / Private member function", test_private_member_functions) 332 | grader.add_part("#5 / Public getter function", test_getter_function) 333 | grader.add_part("#6 / Public setter function", test_setter_function) 334 | grader.run() 335 | -------------------------------------------------------------------------------- /assignment3/autograder/requirements.txt: -------------------------------------------------------------------------------- 1 | colorama==0.4.6 2 | pygccxml==2.5.0 3 | requests==2.32.3 4 | py-cpuinfo==9.0.0 -------------------------------------------------------------------------------- /assignment3/class.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment3/class.cpp -------------------------------------------------------------------------------- /assignment3/class.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment3/class.h -------------------------------------------------------------------------------- /assignment3/docs/bjarne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment3/docs/bjarne.jpg -------------------------------------------------------------------------------- /assignment3/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CS106L Assignment 3: Make a Class 3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca. 4 | */ 5 | 6 | /* ========================================================================= * 7 | * Don't worry about anything beyond this point. * 8 | * (unless you are really curious and want to!) * 9 | * ========================================================================= */ 10 | 11 | #include <fstream> 12 | #include <iostream> 13 | #include <sstream> 14 | #include <string> 15 | 16 | #include "sandbox.cpp" 17 | 18 | int run_autograder() { 19 | auto run_program = [](std::string program, std::initializer_list<std::string> args, 20 | bool silent = false) { 21 | std::stringstream ss; 22 | 23 | ss << program; 24 | for (const auto& arg : args) { 25 | ss << ' ' << arg; 26 | } 27 | 28 | if (silent) { 29 | #ifdef _WIN32 30 | ss << " >nul 2>&1"; 31 | #else 32 | ss << " >/dev/null 2>&1"; 33 | #endif 34 | } 35 | 36 | std::cout.flush(); 37 | return system(ss.str().c_str()); 38 | }; 39 | 40 | std::string python; 41 | for (const auto& option : {"python", "python3", "/usr/bin/python3", "/usr/bin/python"}) { 42 | if (run_program(option, {"--version"}, true) == 0) { 43 | python = option; 44 | break; 45 | } 46 | } 47 | 48 | if (python.empty()) { 49 | std::cerr << "Python was not found on your system. Please install Python and " 50 | "try again." 51 | << "\n"; 52 | std::exit(1); 53 | } 54 | 55 | return run_program(python, {"autograder/autograder.py"}); 56 | } 57 | 58 | int main() { 59 | sandbox(); 60 | return run_autograder(); 61 | } -------------------------------------------------------------------------------- /assignment3/sandbox.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CS106L Assignment 3: Make a Class 3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca. 4 | */ 5 | 6 | void sandbox() { 7 | // STUDENT TODO: Construct an instance of your class! 8 | } -------------------------------------------------------------------------------- /assignment3/short_answer.txt: -------------------------------------------------------------------------------- 1 | Before submitting this file, make sure that there are no more TODO 2 | placeholders remaining in the file (and remove this comment too). 3 | 4 | Make a Class 5 | ------------ 6 | 7 | Q1. What’s const-correctness and why is it important? 8 | A1. TODO 9 | 10 | Q2. Is your class const-correct? How do you know? 11 | A2. TODO -------------------------------------------------------------------------------- /assignment3/utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CS106L Assignment 3: Make a Class 3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca. 4 | */ 5 | 6 | /* ========================================================================= * 7 | * Don't worry about anything beyond this point. * 8 | * (unless you are really curious and want to!) * 9 | * ========================================================================= */ 10 | 11 | #include <fstream> 12 | #include <iostream> 13 | #include <sstream> 14 | #include <string> 15 | 16 | int run_autograder() { 17 | auto run_program = [](std::string program, std::initializer_list<std::string> args, 18 | bool silent = false) { 19 | std::stringstream ss; 20 | 21 | ss << program; 22 | for (const auto& arg : args) { 23 | ss << ' ' << arg; 24 | } 25 | 26 | if (silent) { 27 | #ifdef _WIN32 28 | ss << " >nul 2>&1"; 29 | #else 30 | ss << " >/dev/null 2>&1"; 31 | #endif 32 | } 33 | 34 | std::cout.flush(); 35 | return system(ss.str().c_str()); 36 | }; 37 | 38 | std::string python; 39 | for (const auto& option : {"python", "python3", "/usr/bin/python3", "/usr/bin/python"}) { 40 | if (run_program(option, {"--version"}, true) == 0) { 41 | python = option; 42 | break; 43 | } 44 | } 45 | 46 | if (python.empty()) { 47 | std::cerr << "Python was not found on your system. Please install Python and " 48 | "try again." 49 | << "\n"; 50 | std::exit(1); 51 | } 52 | 53 | return run_program(python, {"autograder/autograder.py"}); 54 | } -------------------------------------------------------------------------------- /assignment4/autograder/autograder.py: -------------------------------------------------------------------------------- 1 | from utils import Autograder 2 | 3 | from typing import Dict, Iterable, Union 4 | 5 | from colorama import Fore, Style 6 | import difflib 7 | import os 8 | import re 9 | import subprocess 10 | import shutil 11 | import sys 12 | 13 | PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 14 | AUTOGRADER_DIR = os.path.join(PATH, "autograder") 15 | CODE_PATH = os.path.join(PATH, "spellcheck.cpp") 16 | EXAMPLES_PATH = os.path.join(PATH, "examples") 17 | EXAMPLES_GOLD_PATH = os.path.join(AUTOGRADER_DIR, "gold") 18 | 19 | # ============================================================================= 20 | # Verifying source code 21 | # ============================================================================= 22 | 23 | 24 | FUNCTION_MATCHERS: Dict[str, Iterable[Union[str, Iterable[str]]]] = { 25 | "tokenize": [ 26 | "find_all", 27 | "std::transform", 28 | "std::inserter", 29 | "std::erase_if", 30 | "#noloops", 31 | ], 32 | "spellcheck": [ 33 | ["std::ranges::views::filter", "rv::filter"], 34 | ["std::ranges::views::transform", "rv::transform"], 35 | "!std::copy_if", 36 | "!std::transform", 37 | "levenshtein", 38 | "#noloops", 39 | ], 40 | } 41 | 42 | 43 | def remove_comments_strings(content): 44 | comment_pattern = r"(\".*?\"|\'.*?\')|(/\*.*?\*/|//[^\r\n]*$)" 45 | comment_regex = re.compile(comment_pattern, re.MULTILINE | re.DOTALL) 46 | string_pattern = r"\".*?\"" 47 | string_regex = re.compile(string_pattern, re.MULTILINE | re.DOTALL) 48 | 49 | content = comment_regex.sub( 50 | lambda match: "" if match.group(2) is not None else match.group(1), content 51 | ) 52 | content = string_regex.sub("", content) 53 | 54 | return content 55 | 56 | 57 | def tokenize_source(input_code: str) -> Iterable[str]: 58 | tokens = [] 59 | 60 | pattern_fqn = re.compile(r"^(::)?[a-zA-Z_][a-zA-Z0-9_]*(::[a-zA-Z_][a-zA-Z0-9_]*)*") 61 | pattern_non_word = re.compile(r"^\W+") 62 | 63 | while input_code: 64 | fqn_match = pattern_fqn.match(input_code) 65 | if fqn_match: 66 | tokens.append(fqn_match.group().strip()) 67 | input_code = input_code[len(fqn_match.group()) :] 68 | else: 69 | non_word_match = pattern_non_word.match(input_code) 70 | if non_word_match: 71 | tokens.append(non_word_match.group().strip()) 72 | input_code = input_code[len(non_word_match.group()) :] 73 | else: 74 | tokens.append(input_code[0]) 75 | input_code = input_code[1:] 76 | 77 | return [t for t in tokens if t] 78 | 79 | 80 | def parse_methods(file_path): 81 | with open(file_path, "r") as file: 82 | content = file.read() 83 | content = remove_comments_strings(content) 84 | 85 | method_pattern = re.compile(r"\b(\w+)\s*\([^)]*\)\s*\{") 86 | methods = {} 87 | pos = 0 88 | 89 | while True: 90 | match = method_pattern.search(content, pos) 91 | if not match: 92 | break 93 | 94 | method_name = match.group(1) 95 | if method_name in methods: 96 | raise RuntimeError( 97 | f"Duplicate method definition of '{method_name}'. Have you tried recompiling your code?" 98 | ) 99 | 100 | start_idx = match.end() - 1 101 | brace_stack = 1 102 | end_idx = start_idx + 1 103 | 104 | while brace_stack > 0: 105 | if end_idx >= len(content): 106 | raise RuntimeError( 107 | f"Unmatched braces in method definition of '{method_name}'. Have you tried recompiling your code?" 108 | ) 109 | if content[end_idx] == "{": 110 | brace_stack += 1 111 | elif content[end_idx] == "}": 112 | brace_stack -= 1 113 | end_idx += 1 114 | 115 | method_body = content[start_idx + 1 : end_idx - 1].strip() 116 | methods[method_name] = tokenize_source(method_body) 117 | pos = end_idx 118 | 119 | return methods 120 | 121 | 122 | def add_matcher_tests(grader: Autograder, file: str): 123 | student_methods = parse_methods(file) 124 | for method, matchers in FUNCTION_MATCHERS.items(): 125 | 126 | def generate_test_method(method_copy, matchers_copy): 127 | def test(): 128 | if method_copy not in student_methods: 129 | raise RuntimeError( 130 | f"Could not find a definition for required method '{method_copy}' in {file}" 131 | ) 132 | 133 | method_body = student_methods[method_copy] 134 | 135 | for matcher in matchers_copy: 136 | if matcher == "#noloops": 137 | for loop_type in ["for", "while", "goto"]: 138 | if loop_type in method_body: 139 | raise RuntimeError( 140 | f"Method {method_copy} may not contain any explicit for/while loops! You must use the STL instead! Found loop: {loop_type}" 141 | ) 142 | print(f"🔎 {method_copy} has no for/while loops!") 143 | continue 144 | 145 | if isinstance(matcher, str): 146 | matcher = [matcher] 147 | 148 | for m in matcher: 149 | if m.startswith("!"): 150 | m = m[1:] 151 | if m in method_body: 152 | raise RuntimeError(f"Method '{method_copy}' is not allowed to call method: {m}") 153 | elif m in method_body: 154 | print(f"🔎 {method_copy} called method {m}") 155 | break 156 | else: 157 | if not any(m.startswith("!") for m in matcher): 158 | raise RuntimeError( 159 | f"Method '{method_copy}' must call one of the following methods: {matcher}." 160 | ) 161 | 162 | return test 163 | 164 | grader.add_part(method, generate_test_method(method, matchers)) 165 | 166 | # Ensure no helper function were used 167 | def test_no_helper_functions(): 168 | present = set(student_methods.keys()) 169 | expected = set(FUNCTION_MATCHERS.keys()) 170 | extra = present - expected 171 | if extra: 172 | raise RuntimeError( 173 | f"You may not use any helper functions for this assignment. You must implement all your code in the following functions: {', '.join(expected)}. \n\nFound extra functions: {', '.join(extra)}" 174 | ) 175 | 176 | grader.add_part( 177 | "Check submission has no helper functions", test_no_helper_functions 178 | ) 179 | 180 | 181 | def no_obvious_namespace_std(): 182 | with open(CODE_PATH, "r") as file: 183 | content = file.read() 184 | content = remove_comments_strings(content) 185 | content = content.replace("\n", " ") 186 | using_namespace = re.compile(r"using\s+namespace\s+std\s*;") 187 | if using_namespace.search(content): 188 | raise RuntimeError( 189 | "You should not use 'using namespace std;' for this assignment. In general, this is considered bad practice as it can lead to naming conflicts, and will affect the autograder for this assignment." 190 | ) 191 | 192 | 193 | # ============================================================================= 194 | # Verifying program correctness 195 | # ============================================================================= 196 | 197 | 198 | import os 199 | import subprocess 200 | 201 | 202 | def find_executable(containing_dir): 203 | # Search for the executable in the given directory 204 | for filename in ("main", "main.exe"): 205 | exe_path = os.path.join(containing_dir, filename) 206 | if os.path.isfile(exe_path) and os.access(exe_path, os.X_OK): 207 | return exe_path 208 | raise FileNotFoundError( 209 | f"No executable named 'main' or 'main.exe' found in '{containing_dir}'." 210 | ) 211 | 212 | 213 | def spellcheck(file_path): 214 | exe_path = find_executable(PATH) 215 | command = [exe_path, "--stdin", "--unstyled"] 216 | 217 | with open(file_path, "r", encoding="utf-8") as file: 218 | result = subprocess.run( 219 | command, 220 | stdin=file, 221 | stdout=subprocess.PIPE, 222 | stderr=subprocess.STDOUT, 223 | text=True, 224 | encoding="utf-8" 225 | ) 226 | 227 | return result.stdout 228 | 229 | 230 | def generate_gold_dir(): 231 | if os.path.exists(EXAMPLES_GOLD_PATH): 232 | shutil.rmtree(EXAMPLES_GOLD_PATH) 233 | os.makedirs(EXAMPLES_GOLD_PATH) 234 | 235 | for example_file in os.listdir(EXAMPLES_PATH): 236 | example_file_path = os.path.join(EXAMPLES_PATH, example_file) 237 | if not os.path.isfile(example_file_path): 238 | continue 239 | try: 240 | output = spellcheck(example_file_path) 241 | gold_file_path = os.path.join(EXAMPLES_GOLD_PATH, example_file) 242 | with open(gold_file_path, "w", encoding="utf-8") as gold_file: 243 | gold_file.write(output) 244 | 245 | print(f"Processed {example_file} -> {gold_file_path}") 246 | except Exception as e: 247 | print(f"Failed to process {example_file}: {e}") 248 | 249 | 250 | def assert_contents_equal(expected, actual, filename): 251 | if expected != actual: 252 | expected_lines = expected.split("\n") 253 | actual_lines = actual.split("\n") 254 | 255 | diff = list( 256 | difflib.unified_diff( 257 | expected_lines, 258 | actual_lines, 259 | fromfile="Expected in solution, missing in your output", 260 | tofile="Present in your output, missing in solution", 261 | ) 262 | ) 263 | 264 | diff = [f"\t{l}" for l in diff] 265 | diff[0] = diff[0].rstrip() 266 | diff_output = "\n".join(diff) 267 | 268 | def matcher(fore): 269 | return ( 270 | lambda match: f"{fore}{Style.BRIGHT}{match.group(0)}{Style.RESET_ALL}" 271 | ) 272 | 273 | diff_output = re.sub( 274 | r"^\s*-+", matcher(Fore.RED), diff_output, flags=re.MULTILINE 275 | ) 276 | diff_output = re.sub( 277 | r"^\s*\++", matcher(Fore.GREEN), diff_output, flags=re.MULTILINE 278 | ) 279 | 280 | error_lines = [ 281 | f"Contents do not match solution:", 282 | diff_output, 283 | "", 284 | f"\t{Fore.CYAN}To see the output of your submission on this file, run:", 285 | "", 286 | f'\t\t ./main --stdin < "examples/{filename}"', 287 | "", 288 | f'\tTo see the expected solution output, open "autograder/gold/{filename}"{Fore.RESET}', 289 | ] 290 | 291 | raise RuntimeError("\n".join(error_lines)) 292 | 293 | 294 | def test_spellcheck(): 295 | for example_file in os.listdir(EXAMPLES_GOLD_PATH): 296 | gold_path = os.path.join(EXAMPLES_GOLD_PATH, example_file) 297 | input_path = os.path.join(EXAMPLES_PATH, example_file) 298 | 299 | if not os.path.isfile(input_path): 300 | raise RuntimeError( 301 | f"Could not find gold file for example '{example_file}'. Did you modify the examples/ directory?" 302 | ) 303 | 304 | with open(gold_path, "r", encoding="utf-8") as f: 305 | gold_output = f.read() 306 | 307 | spellcheck_result = spellcheck(input_path) 308 | 309 | assert_contents_equal(gold_output, spellcheck_result, example_file) 310 | print(f"🔎 {example_file} spellcheck matched solution!") 311 | 312 | 313 | # ============================================================================= 314 | # Autograder setup 315 | # ============================================================================= 316 | 317 | if __name__ == "__main__": 318 | if "--gold" in sys.argv: 319 | generate_gold_dir() 320 | sys.exit(0) 321 | 322 | grader = Autograder() 323 | grader.setup = no_obvious_namespace_std 324 | add_matcher_tests(grader, CODE_PATH) 325 | grader.add_part("Spellcheck", test_spellcheck) 326 | grader.run() 327 | -------------------------------------------------------------------------------- /assignment4/autograder/gold/(kafka).txt: -------------------------------------------------------------------------------- 1 | Loading dictionary... loaded 464811 unique words. 2 | Tokenizing input... got 121 tokens. 3 | 4 | Someone must have slandered Josef K., for one morning, without having 5 | done anything wrong, he was arrested. 6 | 7 | The cook, who always <<broght>> him his breakfast at eight o’clock, did 8 | not come this time. That had never happened before. For a moment he 9 | lay still, looking at the pillow and the old woman who lived opposite 10 | and who was watching him with an inquisitiveness quite unusual for 11 | her. Then, with <<astonshment>>, he noticed that a man he had never seen 12 | before was in his room. He was wearing a tight black suit which, 13 | with its various <<trimings>>, looked like a travelling outfit that had 14 | gone out of fashion years ago. 15 | 16 | “What do you want?” asked K., raising himself half up in bed. 17 | 18 | broght: {bright, brocht, brogh, brought} 19 | astonshment: {astonishment} 20 | trimings: {primings, timings, trimmings} 21 | -------------------------------------------------------------------------------- /assignment4/autograder/gold/(marquez).txt: -------------------------------------------------------------------------------- 1 | Loading dictionary... loaded 464811 unique words. 2 | Tokenizing input... got 84 tokens. 3 | 4 | Many years later, as he faced the firing squad, Colonel <<Aureliano>> Buendía 5 | was to remember that distant afternoon when his father took him to discover ice. 6 | 7 | At that time Macondo was a <<vilage>> of twenty adobe houses, built on the bank 8 | of a river of clear water that ran along a bed of <<pollished>> stones, which were 9 | white and enormous, like prehistoric eggs. The world was so recent that many 10 | things lacked names, and in order to indicate them it was <<necesary>> to point. 11 | 12 | Aureliano: {aurelian} 13 | vilage: {milage, pilage, silage, viage, village, vinage, visage, volage} 14 | pollished: {polished} 15 | necesary: {necessary} 16 | -------------------------------------------------------------------------------- /assignment4/autograder/gold/(morrison).txt: -------------------------------------------------------------------------------- 1 | Loading dictionary... loaded 464811 unique words. 2 | Tokenizing input... got 128 tokens. 3 | 4 | 124 WAS SPITEFUL. Full of a <<baby's>> venom. The women in the house knew 5 | it and so did the children. For years each put up with the spite in his 6 | own way, but by 1873 <<Sethe>> and her daughter Denver were its only victims. 7 | The grandmother, Baby <<Suggs>>, was dead, and the sons, Howard and <<Buglar>>, 8 | had run away by the time they were thirteen years old--as soon as merely 9 | looking in a mirror shattered it (that was the signal for <<Buglar>>); as soon 10 | as two tiny hand prints appeared in the cake (that was it for Howard). Neither 11 | boy waited to see more; another kettleful of chickpeas smoking in a heap on 12 | the floor; soda crackers crumbled and strewn in a line next to the door sill. 13 | 14 | baby's: {babe's} 15 | Sethe: {bethe, ethe, lethe, rethe, seathe, seethe, setae, seth, sethi, sithe, smethe} 16 | Suggs: {muggs, sugg, sughs, sugis, vuggs} 17 | Buglar: {bugler, burglar, juglar} 18 | Buglar: {bugler, burglar, juglar} 19 | -------------------------------------------------------------------------------- /assignment4/autograder/gold/gibberish.txt: -------------------------------------------------------------------------------- 1 | Loading dictionary... loaded 464811 unique words. 2 | Tokenizing input... got 22 tokens. 3 | 4 | This is a bunch of gibberish: 5 | 6 | ansdka nakdlsnakln lnfgklanrf nksladnksal nkglrnkadf nklsadn 7 | 8 | These are definitely misspelled, but probably don't have any suggestions. 9 | 10 | -------------------------------------------------------------------------------- /assignment4/autograder/gold/melville.txt: -------------------------------------------------------------------------------- 1 | Loading dictionary... loaded 464811 unique words. 2 | Tokenizing input... got 56 tokens. 3 | 4 | Call me Ishmael. Some years ago—never mind how long precisely—having little 5 | or no money in my purse, and nothing particular to interest me on shore, 6 | I thought I would sail about a little and see the watery part of the world. 7 | It is a way I have of driving off the spleen and regulating the circulation. 8 | 9 | -------------------------------------------------------------------------------- /assignment4/autograder/gold/orwell.txt: -------------------------------------------------------------------------------- 1 | Loading dictionary... loaded 464811 unique words. 2 | Tokenizing input... got 55 tokens. 3 | 4 | It was a bright cold day in April, and the clocks were striking thirteen. 5 | Winston Smith, his chin nuzzled into his breast in an effort to escape the vile wind, 6 | slipped quickly through the glass doors of Victory Mansions, though not quickly 7 | enough to prevent a swirl of gritty dust from entering along with him. 8 | 9 | -------------------------------------------------------------------------------- /assignment4/autograder/gold/tolstoy.txt: -------------------------------------------------------------------------------- 1 | Loading dictionary... loaded 464811 unique words. 2 | Tokenizing input... got 66 tokens. 3 | 4 | All happy families are alike; each unhappy family is unhappy in its own way. 5 | 6 | Everything was in confusion in the Oblonskys' house. The wife had discovered 7 | that the husband was carrying on an intrigue with a French girl who had been 8 | a governess in their family, and she had announced to her husband that she 9 | could not go on living in the same house with him. 10 | 11 | -------------------------------------------------------------------------------- /assignment4/autograder/utils.hpp: -------------------------------------------------------------------------------- 1 | /* ========================================================================= * 2 | * Don't worry about anything beyond this point. * 3 | * (unless you are really curious and want to!) * 4 | * ========================================================================= */ 5 | 6 | #include <chrono> 7 | #include <fstream> 8 | #include <iomanip> 9 | #include <iostream> 10 | #include <sstream> 11 | #include <string> 12 | 13 | int run_autograder() { 14 | auto run_program = [](std::string program, std::initializer_list<std::string> args, 15 | bool silent = false) { 16 | std::stringstream ss; 17 | 18 | ss << program; 19 | for (const auto& arg : args) { 20 | ss << ' ' << arg; 21 | } 22 | 23 | if (silent) { 24 | #ifdef _WIN32 25 | ss << " >nul 2>&1"; 26 | #else 27 | ss << " >/dev/null 2>&1"; 28 | #endif 29 | } 30 | 31 | std::cout.flush(); 32 | return system(ss.str().c_str()); 33 | }; 34 | 35 | std::string python; 36 | for (const auto& option : {"python", "python3", "/usr/bin/python3", "/usr/bin/python"}) { 37 | if (run_program(option, {"--version"}, true) == 0) { 38 | python = option; 39 | break; 40 | } 41 | } 42 | 43 | if (python.empty()) { 44 | std::cerr << "Python was not found on your system. Please install Python and " 45 | "try again." 46 | << "\n"; 47 | std::exit(1); 48 | } 49 | 50 | return run_program(python, {"autograder/autograder.py"}); 51 | } 52 | 53 | namespace ansi { 54 | 55 | const int style_idx = std::ios_base::xalloc(); 56 | 57 | template <class CharT, class Traits> 58 | constexpr std::basic_ostream<CharT, Traits>& styled(std::basic_ostream<CharT, Traits>& os) { 59 | os.iword(style_idx) = 1; 60 | return os; 61 | } 62 | 63 | template <class CharT, class Traits> 64 | constexpr std::basic_ostream<CharT, Traits>& unstyled(std::basic_ostream<CharT, Traits>& os) { 65 | os.iword(style_idx) = 0; 66 | return os; 67 | } 68 | 69 | #define make_style(NAME, VALUE) \ 70 | template <class CharT, class Traits> \ 71 | constexpr std::basic_ostream<CharT, Traits>& NAME(std::basic_ostream<CharT, Traits>& os) { \ 72 | if (os.iword(style_idx) > 0) \ 73 | os << VALUE; \ 74 | return os; \ 75 | } 76 | 77 | make_style(reset, "\033[0m\e[0m"); 78 | make_style(fg_red, "\033[31m"); 79 | make_style(fg_lightred, "\033[91m"); 80 | make_style(fg_green, "\033[92m"); 81 | make_style(fg_gray, "\033[90m"); 82 | make_style(bg_yellow, "\e[43m"); 83 | 84 | } // namespace ansi 85 | 86 | std::string read_stream(std::istream& is) { 87 | std::istreambuf_iterator<char> begin(is), end; 88 | return std::string(begin, end); 89 | } 90 | 91 | struct TimerResult { 92 | std::string name; 93 | size_t trials; 94 | std::chrono::nanoseconds ns; 95 | }; 96 | 97 | class TimerSummary { 98 | public: 99 | TimerSummary() : trial_noun{"trial"}, enabled{false} {} 100 | void add(const TimerResult& result) { results.push_back(result); } 101 | void set_trial_noun(const std::string& trial_noun) { this->trial_noun = trial_noun; } 102 | 103 | TimerSummary& operator=(const TimerSummary&) = delete; 104 | ~TimerSummary() { 105 | if (!enabled) 106 | return; 107 | 108 | std::cout << "\n"; 109 | std::cout << ansi::bg_yellow << std::left << std::setw(120) << "Timing Results:" << ansi::reset << '\n'; 110 | std::cout << ansi::fg_gray; 111 | for (const auto& [name, trials, ns] : results) { 112 | std::cout << " · " << name << " took "; 113 | format_time(ns); 114 | if (trials > 1) { 115 | std::cout << ", averaging "; 116 | format_time(ns / trials); 117 | std::cout << " per " << trial_noun; 118 | } 119 | std::cout << " (" << trials << " " << trial_noun; 120 | if (trials != 1) std::cout << "s"; 121 | std::cout << ")\n"; 122 | } 123 | std::cout << ansi::reset; 124 | } 125 | 126 | void enable() { enabled = true; } 127 | void disable() { enabled = false; } 128 | 129 | private: 130 | bool enabled; 131 | std::string trial_noun; 132 | std::vector<TimerResult> results; 133 | 134 | void format_time(const std::chrono::nanoseconds& ns) { 135 | using namespace std::chrono; 136 | 137 | if (ns < 10us) { 138 | std::cout << ns.count() << "ns"; 139 | } else if (ns < 1s) { 140 | std::cout << std::fixed << std::setprecision(2) 141 | << duration_cast<microseconds>(ns).count() / 1000.0 << "ms" << std::defaultfloat; 142 | } else { 143 | std::cout << std::fixed << std::setprecision(3) 144 | << duration_cast<milliseconds>(ns).count() / 1000.0 << "s" << std::defaultfloat; 145 | } 146 | } 147 | }; 148 | 149 | class Timer { 150 | public: 151 | Timer(TimerSummary& summary, const std::string& name, size_t trials = 1) 152 | : summary{summary}, name{name}, trials{trials}, 153 | start{std::chrono::high_resolution_clock::now()}, stopped{false} {} 154 | 155 | ~Timer() { 156 | stop(); 157 | auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start); 158 | summary.add({name, trials, ns}); 159 | } 160 | 161 | Timer& operator=(const Timer&) = delete; 162 | 163 | void set_trials(size_t trials) { this->trials = trials; } 164 | 165 | void stop() { 166 | if (stopped) 167 | return; 168 | stopped = true; 169 | end = std::chrono::high_resolution_clock::now(); 170 | } 171 | 172 | private: 173 | std::string name; 174 | size_t trials; 175 | std::chrono::high_resolution_clock::time_point start; 176 | std::chrono::high_resolution_clock::time_point end; 177 | bool stopped; 178 | 179 | TimerSummary& summary; 180 | }; 181 | -------------------------------------------------------------------------------- /assignment4/docs/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment4/docs/header.png -------------------------------------------------------------------------------- /assignment4/docs/mispelled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment4/docs/mispelled.png -------------------------------------------------------------------------------- /assignment4/docs/spellcheck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment4/docs/spellcheck.png -------------------------------------------------------------------------------- /assignment4/examples/(kafka).txt: -------------------------------------------------------------------------------- 1 | Someone must have slandered Josef K., for one morning, without having 2 | done anything wrong, he was arrested. 3 | 4 | The cook, who always broght him his breakfast at eight o’clock, did 5 | not come this time. That had never happened before. For a moment he 6 | lay still, looking at the pillow and the old woman who lived opposite 7 | and who was watching him with an inquisitiveness quite unusual for 8 | her. Then, with astonshment, he noticed that a man he had never seen 9 | before was in his room. He was wearing a tight black suit which, 10 | with its various trimings, looked like a travelling outfit that had 11 | gone out of fashion years ago. 12 | 13 | “What do you want?” asked K., raising himself half up in bed. -------------------------------------------------------------------------------- /assignment4/examples/(marquez).txt: -------------------------------------------------------------------------------- 1 | Many years later, as he faced the firing squad, Colonel Aureliano Buendía 2 | was to remember that distant afternoon when his father took him to discover ice. 3 | 4 | At that time Macondo was a vilage of twenty adobe houses, built on the bank 5 | of a river of clear water that ran along a bed of pollished stones, which were 6 | white and enormous, like prehistoric eggs. The world was so recent that many 7 | things lacked names, and in order to indicate them it was necesary to point. -------------------------------------------------------------------------------- /assignment4/examples/(morrison).txt: -------------------------------------------------------------------------------- 1 | 124 WAS SPITEFUL. Full of a baby's venom. The women in the house knew 2 | it and so did the children. For years each put up with the spite in his 3 | own way, but by 1873 Sethe and her daughter Denver were its only victims. 4 | The grandmother, Baby Suggs, was dead, and the sons, Howard and Buglar, 5 | had run away by the time they were thirteen years old--as soon as merely 6 | looking in a mirror shattered it (that was the signal for Buglar); as soon 7 | as two tiny hand prints appeared in the cake (that was it for Howard). Neither 8 | boy waited to see more; another kettleful of chickpeas smoking in a heap on 9 | the floor; soda crackers crumbled and strewn in a line next to the door sill. -------------------------------------------------------------------------------- /assignment4/examples/gibberish.txt: -------------------------------------------------------------------------------- 1 | This is a bunch of gibberish: 2 | 3 | ansdka nakdlsnakln lnfgklanrf nksladnksal nkglrnkadf nklsadn 4 | 5 | These are definitely misspelled, but probably don't have any suggestions. -------------------------------------------------------------------------------- /assignment4/examples/melville.txt: -------------------------------------------------------------------------------- 1 | Call me Ishmael. Some years ago—never mind how long precisely—having little 2 | or no money in my purse, and nothing particular to interest me on shore, 3 | I thought I would sail about a little and see the watery part of the world. 4 | It is a way I have of driving off the spleen and regulating the circulation. -------------------------------------------------------------------------------- /assignment4/examples/orwell.txt: -------------------------------------------------------------------------------- 1 | It was a bright cold day in April, and the clocks were striking thirteen. 2 | Winston Smith, his chin nuzzled into his breast in an effort to escape the vile wind, 3 | slipped quickly through the glass doors of Victory Mansions, though not quickly 4 | enough to prevent a swirl of gritty dust from entering along with him. -------------------------------------------------------------------------------- /assignment4/examples/tolstoy.txt: -------------------------------------------------------------------------------- 1 | All happy families are alike; each unhappy family is unhappy in its own way. 2 | 3 | Everything was in confusion in the Oblonskys' house. The wife had discovered 4 | that the husband was carrying on an intrigue with a French girl who had been 5 | a governess in their family, and she had announced to her husband that she 6 | could not go on living in the same house with him. -------------------------------------------------------------------------------- /assignment4/main.cpp: -------------------------------------------------------------------------------- 1 | #include <algorithm> 2 | #include <fstream> 3 | #include <iostream> 4 | #include <iterator> 5 | #include <optional> 6 | #include <ostream> 7 | #include <random> 8 | #include <string> 9 | 10 | #include "autograder/utils.hpp" 11 | #include "spellcheck.h" 12 | 13 | void print_output(const std::string& source, const std::set<Misspelling>& Misspellings) { 14 | std::string_view sv(source); 15 | size_t last_ofs = 0; 16 | for (const auto& Misspelling : Misspellings) { 17 | // Print text before the Misspelling 18 | std::cout << sv.substr(last_ofs, Misspelling.token.src_offset - last_ofs); 19 | 20 | std::cout << ansi::fg_red << "<<"; 21 | std::cout << sv.substr(Misspelling.token.src_offset, Misspelling.token.content.size()); 22 | std::cout << ">>" << ansi::reset; 23 | last_ofs = Misspelling.token.src_offset + Misspelling.token.content.size(); 24 | } 25 | 26 | std::cout << sv.substr(last_ofs) << "\n\n"; 27 | 28 | for (const auto& Misspelling : Misspellings) { 29 | std::cout << ansi::fg_red; 30 | std::cout << sv.substr(Misspelling.token.src_offset, Misspelling.token.content.size()); 31 | std::cout << ansi::reset; 32 | 33 | std::cout << ": {"; 34 | 35 | bool first = true; 36 | for (const auto& suggestion : Misspelling.suggestions) { 37 | if (!first) 38 | std::cout << ", "; 39 | std::cout << suggestion; 40 | first = false; 41 | } 42 | 43 | std::cout << "}\n"; 44 | } 45 | } 46 | 47 | void print_success() { 48 | std::vector<std::string> messages = { 49 | "You're a spelling wizard! If words had a kingdom, you'd rule it.", 50 | "Are you secretly a dictionary in disguise? Impressive!", 51 | "If spelling were an Olympic sport, you'd have gold by now.", 52 | "Your spelling skills are so good, even autocorrect is taking notes.", 53 | "The spelling bee champions fear you. Rightfully so.", 54 | "Your grasp of spelling is tighter than a jar lid no one can open.", 55 | "I checked the dictionary, and your name is under 'spelling genius.'", 56 | "Shakespeare just sent a 'Well done!' from the beyond.", 57 | "You spell so well, even Scrabble pieces arrange themselves for you.", 58 | "Your spelling game is so strong, spellcheck just quit its job."}; 59 | 60 | std::random_device rd; 61 | std::mt19937 gen(rd()); 62 | std::uniform_int_distribution<size_t> dist(0, messages.size() - 1); 63 | 64 | std::cout << ansi::fg_green << messages[dist(gen)] << ansi::reset << std::endl; 65 | } 66 | 67 | int main(int argc, char** argv) { 68 | if (argc == 1) { 69 | return run_autograder(); 70 | } 71 | 72 | bool read_stdin = false; 73 | std::string input; 74 | std::string dictionary_file = "words.txt"; 75 | bool styled = true; 76 | 77 | TimerSummary summary; 78 | summary.set_trial_noun("token"); 79 | 80 | for (int i = 1; i < argc; ++i) { 81 | std::string arg = argv[i]; 82 | if (arg == "--dict" && i + 1 < argc) { 83 | dictionary_file = argv[++i]; 84 | } else if (arg == "--unstyled") { 85 | styled = false; 86 | } else if (arg == "--stdin") { 87 | read_stdin = true; 88 | } else if (arg == "--profile") { 89 | summary.enable(); 90 | } else { 91 | input += argv[i]; 92 | } 93 | } 94 | 95 | if (styled) 96 | std::cout << ansi::styled; 97 | else 98 | std::cout << ansi::unstyled; 99 | 100 | if (read_stdin) 101 | input += read_stream(std::cin); 102 | 103 | std::ifstream dict_stream(dictionary_file); 104 | if (!dict_stream.is_open()) { 105 | std::cerr << "Failed to open dict file '" << dictionary_file << "'" << std::endl; 106 | return EXIT_FAILURE; 107 | } 108 | 109 | std::cout << ansi::fg_gray << "Loading dictionary... "; 110 | 111 | std::string dict_contents = read_stream(dict_stream); 112 | 113 | Timer tokenize_dict_timer { summary, "Tokenizing dictionary" }; 114 | Corpus dictionary_tokens = tokenize(dict_contents); 115 | tokenize_dict_timer.stop(); 116 | tokenize_dict_timer.set_trials(dictionary_tokens.size()); 117 | 118 | Dictionary dictionary; 119 | std::for_each(dictionary_tokens.begin(), dictionary_tokens.end(), 120 | [&](const Token& t) { dictionary.insert(t.content); }); 121 | 122 | std::cout << "loaded " << dictionary.size() << " unique words." << std::endl; 123 | std::cout << "Tokenizing input... "; 124 | 125 | Timer tokenize_source_timer { summary, "Tokenizing input"}; 126 | Corpus source = tokenize(input); 127 | tokenize_source_timer.stop(); 128 | tokenize_source_timer.set_trials(source.size()); 129 | 130 | std::cout << "got " << source.size() << " tokens." << ansi::reset << "\n\n"; 131 | 132 | Timer spellcheck_timer { summary, "Spellcheck", source.size() }; 133 | std::set<Misspelling> Misspellings = spellcheck(source, dictionary); 134 | spellcheck_timer.stop(); 135 | 136 | print_output(input, Misspellings); 137 | 138 | if (styled && !dictionary.empty() && Misspellings.empty()) 139 | print_success(); 140 | 141 | return Misspellings.empty() ? 0 : EXIT_FAILURE; 142 | } -------------------------------------------------------------------------------- /assignment4/spellcheck.cpp: -------------------------------------------------------------------------------- 1 | #include "spellcheck.h" 2 | 3 | #include <algorithm> 4 | #include <iostream> 5 | #include <numeric> 6 | #include <ranges> 7 | #include <set> 8 | #include <vector> 9 | 10 | template <typename Iterator, typename UnaryPred> 11 | std::vector<Iterator> find_all(Iterator begin, Iterator end, UnaryPred pred); 12 | 13 | Corpus tokenize(std::string& source) { 14 | /* TODO: Implement this method */ 15 | return Corpus(); 16 | } 17 | 18 | std::set<Misspelling> spellcheck(const Corpus& source, const Dictionary& dictionary) { 19 | /* TODO: Implement this method */ 20 | return std::set<Misspelling>(); 21 | }; 22 | 23 | /* Helper methods */ 24 | 25 | #include "utils.cpp" -------------------------------------------------------------------------------- /assignment4/spellcheck.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include <set> 4 | #include <string> 5 | #include <unordered_set> 6 | 7 | struct Token { 8 | 9 | std::string content; 10 | size_t src_offset; 11 | 12 | template <typename It> 13 | Token(std::string& source, It begin, It end) 14 | : src_offset{static_cast<std::size_t>(std::distance(source.begin(), begin))}, 15 | content{std::string(begin, end)} { 16 | clean(source); 17 | } 18 | 19 | private: 20 | void clean(const std::string& source); 21 | }; 22 | 23 | struct Misspelling { 24 | Token token; 25 | std::set<std::string> suggestions; 26 | }; 27 | 28 | using Corpus = std::set<Token>; 29 | using Dictionary = std::unordered_set<std::string>; 30 | 31 | Corpus tokenize(std::string& input); 32 | std::set<Misspelling> spellcheck(const Corpus& source, const Dictionary& dictionary); 33 | 34 | /* Helper methods */ 35 | 36 | size_t levenshtein(const std::string&, const std::string&); 37 | bool operator<(const Token&, const Token&); 38 | bool operator<(const Misspelling&, const Misspelling&); -------------------------------------------------------------------------------- /assignment4/utils.cpp: -------------------------------------------------------------------------------- 1 | #include <algorithm> 2 | #include <string> 3 | #include <tuple> 4 | 5 | bool operator<(const Token& a, const Token& b) { 6 | return std::tie(a.src_offset, a.content) < std::tie(b.src_offset, b.content); 7 | } 8 | 9 | bool operator<(const Misspelling& a, const Misspelling& b) { return a.token < b.token; } 10 | 11 | template <typename Iterator, typename UnaryPred> 12 | std::vector<Iterator> find_all(Iterator begin, Iterator end, UnaryPred pred) { 13 | std::vector<Iterator> its{begin}; 14 | for (auto it = begin; it != end; ++it) { 15 | if (pred(*it)) 16 | its.push_back(it); 17 | } 18 | its.push_back(end); 19 | return its; 20 | } 21 | 22 | void Token::clean(const std::string& source) { 23 | auto begin = source.begin() + src_offset; 24 | auto end = source.begin() + src_offset + content.size(); 25 | 26 | auto left = std::find_if(begin, end, ::isalnum); 27 | auto right = 28 | std::find_if(std::make_reverse_iterator(end), std::make_reverse_iterator(begin), ::isalnum) 29 | .base(); 30 | src_offset += std::distance(begin, left); 31 | 32 | if (left < right) { 33 | content = std::string(left, right); 34 | } else { 35 | content = ""; 36 | } 37 | 38 | std::transform(content.begin(), content.end(), content.begin(), ::tolower); 39 | } 40 | 41 | /* ========================================================================= * 42 | * Damerau-Levenshtein Distance Algorithm * 43 | * * 44 | * Taken from: * 45 | * https://github.com/sp1ff/damerau-levenshtein * 46 | * ========================================================================= */ 47 | 48 | size_t levenshtein(const std::string& s1, const std::string& s2) { 49 | /* If size differences are extreme, then we know D-L exceeds 1 */ 50 | size_t diff = std::abs((ptrdiff_t)s1.size() - (ptrdiff_t)s2.size()); 51 | if (diff > 1) 52 | return diff; 53 | 54 | /* If strings have same size, we'll count mismatches and potentially early exit */ 55 | if (diff == 0) { 56 | size_t mismatches = std::inner_product(s1.begin(), s1.end(), s2.begin(), 0UL, std::plus<>(), 57 | std::not_equal_to<>()); 58 | if (mismatches <= 1) 59 | return mismatches; 60 | } 61 | 62 | /* Otherwise we run an optimized DP D-L algorithm. 63 | * This will early exit if the min possible D-L distance exceeds 1 */ 64 | const std::size_t l1 = s1.size(), l2 = s2.size(); 65 | std::vector<size_t> col(l2 + 1), prevCol(l2 + 1); 66 | for (size_t i = 0; i < prevCol.size(); i++) 67 | prevCol[i] = i; 68 | for (size_t i = 0; i < l1; i++) { 69 | col[0] = i + 1; 70 | size_t row_min = col[0]; 71 | for (size_t j = 0; j < l2; j++) { 72 | col[j + 1] = 73 | std::min({prevCol[1 + j] + 1, col[j] + 1, prevCol[j] + (s1[i] == s2[j] ? 0 : 1)}); 74 | row_min = std::min(row_min, col[j + 1]); 75 | } 76 | if (row_min > 1) 77 | return row_min; 78 | col.swap(prevCol); 79 | } 80 | 81 | return prevCol[l2]; 82 | } -------------------------------------------------------------------------------- /assignment5/README.md: -------------------------------------------------------------------------------- 1 | <p align="center"> 2 | <img src="docs/logo.jpeg" alt="A logo of Treebook, a fictional Stanford-based social media startup" style="width: 300px; height: auto;" /> 3 | </p> 4 | 5 | # Assignment 5: Treebook 6 | 7 | Due Friday, May 23rd at 11:59PM 8 | 9 | ## Overview 10 | 11 | The newest Stanford social media startup is Treebook, and you’re a founding member of the team! To get the product off the ground and compete with an unnamed, completely legally unaffiliated app from Harvard, you’ve been assigned the task of implementing user profiles. 12 | 13 | For this assignment, you will be implementing parts of a class to allow for operator overloads, as well as to modify some aspects of the special member functions. 14 | 15 | There are two files you'll work with for this assignment: 16 | 17 | * `user.h` - Contains the declaration for the `User` class that you will extend with special member functions and operators. 18 | * `user.cpp` - Contains the definition of the `User` class. 19 | 20 | To download the starter code for this assignment, please see the instructions for [**Getting Started**](../README.md#getting-started) on the course assignments repository. 21 | 22 | ## Running your code 23 | 24 | To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit <kbd>Ctrl+\`</kbd> or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assign5/` directory and run: 25 | 26 | ```sh 27 | g++ -std=c++20 main.cpp user.cpp -o main 28 | ``` 29 | 30 | Assuming that your code compiles without any compiler errors, you can now do: 31 | 32 | ```sh 33 | ./main 34 | ``` 35 | 36 | which will actually run the `main` function in `main.cpp`. 37 | 38 | As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track! 39 | 40 | > [!NOTE] 41 | > 42 | > ### Note for Windows 43 | > 44 | > On Windows, you may need to compile your code using 45 | > 46 | > ```sh 47 | > g++ -static-libstdc++ -std=c++20 main.cpp user.cpp -o main 48 | > ``` 49 | > 50 | > in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with: 51 | > 52 | > ```sh 53 | > ./main.exe 54 | > ``` 55 | 56 | ## Part 1: Viewing Profiles 57 | 58 | Take a look at the `user.h` header file. Your coworkers have begun to write a `User` class that will store the name and friends list of each user who joins your social media platform! In order to keep this class super efficient, they have chosen to represent the list of friends as a raw pointer array of `std::string` (kind of like how a `std::vector` stores its elements behind the scenes). Thankfully, they have already written logic for creating a new `User` and for adding friends to an existing `User`'s friend list (`add_friend`), but they've begun to experience some strange issues when working with `User` objects. 59 | 60 | To begin with, there's no easy way to print information about each `User` object to the console, which has made debugging at Treebook difficult. To help your coworkers out, write an `operator<<` method that prints a `User` to a `std::ostream`. **This operator should be declared as a friend function in `user.h` and implemented in `user.cpp`.** For example, a user named `"Alice"` with friends `"Bob"` and `"Charlie"` should give the following output when printed to the console: 61 | 62 | ``` 63 | User(name=Alice, friends=[Bob, Charlie]) 64 | ``` 65 | 66 | Note: `operator<<` should not print any newline characters. 67 | 68 | > [!IMPORTANT] 69 | > In your implementation of `operator<<`, you will need to access and loop through the `_friends` private field of the `User` class in order to print out a user's friends. Normally, you cannot access private fields inside of a class in a non-member function—in this case, we can get around this restriction by marking `operator<<` as a **friend function inside of the `User` class.** See the slides for Tuesday's lecture for more information! 70 | 71 | ## Part 2: Unfriendly Behaviour 72 | 73 | With the help of your `operator<<`, your coworkers have been able to make good progress on the social media app. However, they can't quite wrap their head around some seemingly bizzare issues that occur when they try to make copies of `User` objects in memory. Having recently taken CS106L, you suspect that it might have something to do with the special member functions (or the lack thereof) on the `User` class. To fix this issue, we'll implement our own versions of the special member functions (SMFs) for the `User` class, and remove some of the others for which the compiler generated versions are insufficient. 74 | 75 | To be specific, you will need to: 76 | 77 | 1. Implement a destructor for the `User` class. To do so, implement the `~User()` SMF. 78 | 2. Make the `User` class copy constructible. To do so, implement the `User(const User& user)` SMF. 79 | 3. Make the `User` class copy assignable. To do so, implement the `User& operator=(const User& user)` SMF. 80 | 4. Prevent the `User` class from being move constructed. To do so, delete the `User(User&& user)` SMF. 81 | 5. Prevent the `User` class from being move assigned. To do so, delete the `User& operator=(User&& user)` SMF. 82 | 83 | In performing these tasks, you are expected to make changes to **both** the `user.h` and `user.cpp` files. 84 | 85 | > [!IMPORTANT] 86 | > In your implementations of points 2 and 3 above, you will need to copy the contents of the `_friends` array. Recall from Thursday's lecture on special member functions that you can copy a pointer array by first allocating memory for a new one (possibly within a member initializer list), and then copying over the elements with a for loop. 87 | > Make sure that you also set the `_size`, `_capacity`, and `_name` of the instance you are changing as well! 88 | 89 | ## Part 3: Always Be Friending 90 | 91 | After making changes to the special member functions, you've been able to scale out Treebook across Stanford and word has started to spread at other universities! However, your coworkers and you have found that some common use cases for the `User` class are either inconvenient or impossible given how the class is currently written, and you think you might be able to fix this by implementing some custom operators. 92 | 93 | You will overload two operators for the `User` class. **Please implement both of these operators as member functions** (i.e. declare them inside of the `User` class in `user.h` and provide implementations in `user.cpp`). 94 | 95 | ### `operator+=` 96 | 97 | The `+=` operator will representing adding a user to another user's friend list. This should be symmetric, meaning that adding, for example, Charlie to Alice's friend list should cause Alice to also be in Charlie's list. For example, consider this code: 98 | 99 | ```cpp 100 | User alice("Alice"); 101 | User charlie("Charlie"); 102 | 103 | alice += charlie; 104 | std::cout << alice << std::endl; 105 | std::cout << charlie << std::endl; 106 | 107 | // Expected output: 108 | // User(name=Alice, friends=[Charlie]) 109 | // User(name=Charlie, friends=[Alice]) 110 | ``` 111 | 112 | The function signature for this operator should be `User& operator+=(User& rhs)`. Note that like the copy-assignment operator, it returns a reference to itself. 113 | 114 | ### `operator<` 115 | 116 | Recall that the `<` operator is required to store users in a `std::set`, as `std::set` is implemented in terms of the comparison operator. Implement `operator<` to compare users alphabetically by name. For example: 117 | 118 | ```cpp 119 | User alice("Alice"); 120 | User charlie("Charlie"); 121 | 122 | if (alice < charlie) 123 | std::cout << "Alice is less than Charlie"; 124 | else 125 | std::cout << "Charlie is less than Alice"; 126 | 127 | // Expected output: 128 | // Alice is less than Charlie 129 | ``` 130 | 131 | The function signature for this operator should be `bool operator<(const User& rhs) const`. 132 | 133 | ## 🚀 Submission Instructions 134 | 135 | Before you submit the assignment, please fill out this [short feedback form](https://forms.gle/YFA8Z6GwBp976irm7). **Completion of the form is required to receive credit for the assignment.** After filling out the form, please upload the files to Paperless under the correct assignment heading. 136 | 137 | Your deliverable should be: 138 | 139 | - `user.h` 140 | - `user.cpp` 141 | 142 | You may resubmit as many times as you'd like before the deadline. 143 | -------------------------------------------------------------------------------- /assignment5/autograder/autograder.py: -------------------------------------------------------------------------------- 1 | import functools 2 | from typing import List 3 | from utils import Autograder, ASSIGNMENT_DIR 4 | 5 | import os 6 | import subprocess 7 | 8 | 9 | def get_main_exe_path() -> os.PathLike: 10 | main_path = os.path.join(ASSIGNMENT_DIR, "main") 11 | if not os.path.isfile(main_path): 12 | main_path = os.path.join(ASSIGNMENT_DIR, "main.exe") 13 | if not os.path.isfile(main_path): 14 | raise RuntimeError( 15 | "Could not find a main executable. Did you compile your code with the command in the README?" 16 | ) 17 | return main_path 18 | 19 | 20 | @functools.lru_cache 21 | def get_memory_leak_exit_code() -> int: 22 | main_path = get_main_exe_path() 23 | result = subprocess.check_output([main_path, "memory_leak_exit_code"], text=True) 24 | return int(result.strip()) 25 | 26 | 27 | def verify_output(test_case: str, expected: List[str]): 28 | main_path = get_main_exe_path() 29 | result = subprocess.run( 30 | [main_path, test_case], 31 | stdout=subprocess.PIPE, 32 | stderr=subprocess.PIPE, 33 | text=True, 34 | ) 35 | 36 | if result.returncode == get_memory_leak_exit_code(): 37 | raise RuntimeError(f"💧Memory leak detected: {result.stderr}") 38 | 39 | if result.returncode != 0: 40 | raise RuntimeError(result.stderr) 41 | 42 | actual = result.stdout.strip() 43 | if not actual: 44 | actual = [] 45 | else: 46 | actual = actual.split("\n") 47 | 48 | if actual != expected: 49 | nl = "\n\t" 50 | raise RuntimeError( 51 | f"Test output did not match expected. Expected: \n{nl}{nl.join(expected)}\n\nGot: \n{nl}{nl.join(actual)}" 52 | ) 53 | 54 | 55 | if __name__ == "__main__": 56 | grader = Autograder() 57 | grader.add_part( 58 | "Part 1: operator<<", 59 | lambda: verify_output( 60 | "insertion_operator", 61 | [ 62 | "User(name=Alice, friends=[])", 63 | "User(name=Bob, friends=[Alice])", 64 | "User(name=Charlie, friends=[Alice, Bob])", 65 | ], 66 | ), 67 | ) 68 | 69 | grader.add_part("Part 2: Destructor", lambda: verify_output("destructor", [])) 70 | 71 | grader.add_part( 72 | "Part 2: Copy Constructor", 73 | lambda: verify_output( 74 | "copy_constructor", 75 | [ 76 | "User(name=Alice, friends=[F, F, F, F, F, F, F, F, F])", 77 | "User(name=Alice, friends=[Bob, Alice, Charlie, F, F, F, F, F, F])", 78 | ], 79 | ), 80 | ) 81 | 82 | grader.add_part( 83 | "Part 2: Copy Assignment Operator", 84 | lambda: verify_output( 85 | "copy_assignment", 86 | [ 87 | "User(name=Bob, friends=[BF, BF, BF, BF, BF, BF, BF, BF, BF])", 88 | "User(name=Bob, friends=[BF, BF, BF, BF, BF, BF, BF, BF, BF])", 89 | ], 90 | ), 91 | ) 92 | 93 | grader.add_part( 94 | "Part 2: Move Constructor", lambda: verify_output("move_constructor", []) 95 | ) 96 | grader.add_part( 97 | "Part 2: Move Assignment Operator", lambda: verify_output("move_assignment", []) 98 | ) 99 | 100 | grader.add_part( 101 | "Part 3: operator+=", 102 | lambda: verify_output( 103 | "compound_assignment", 104 | ["User(name=Alice, friends=[Bob])", "User(name=Bob, friends=[Alice])"], 105 | ), 106 | ) 107 | 108 | grader.add_part( 109 | "Part 3: operator<", 110 | lambda: verify_output( 111 | "comparable", 112 | [ 113 | "A < B is true", 114 | "B < A is false", 115 | "B < C is true", 116 | "C < B is false", 117 | "C < A is false", 118 | "A < C is true", 119 | ], 120 | ), 121 | ) 122 | 123 | grader.run(), 124 | -------------------------------------------------------------------------------- /assignment5/autograder/diagnostics.hpp: -------------------------------------------------------------------------------- 1 | /** Contains memory diagnostics to track memory allocations and report memory leaks. */ 2 | 3 | #pragma once 4 | 5 | #include <cstdio> 6 | #include <cstdlib> 7 | #include <iostream> 8 | #include <limits> 9 | #include <new> 10 | #include <unordered_map> 11 | 12 | namespace MemoryDiagnostics { 13 | namespace detail { 14 | 15 | /** 16 | * In order to initialize an unordered_map inside of the MemoryTracker class below, 17 | * we must make dynamic allocations. However, the default allocator for unordered_map 18 | * calls operator new and delete, which would cause infinite recursion. To avoid this, 19 | * we define a custom allocator that uses std::malloc and std::free instead and define 20 | * the unordered_map in MemoryTracker to use this allocator. 21 | * 22 | * See https://en.cppreference.com/w/cpp/named_req/Allocator for more information. 23 | */ 24 | template <class T> struct Mallocator { 25 | using value_type = T; 26 | 27 | Mallocator() = default; 28 | 29 | template <class U> constexpr Mallocator(const Mallocator<U>&) noexcept {} 30 | 31 | [[nodiscard]] T* allocate(std::size_t n) { 32 | if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) 33 | throw std::bad_array_new_length(); 34 | 35 | if (auto p = static_cast<T*>(std::malloc(n * sizeof(T)))) { 36 | return p; 37 | } 38 | 39 | throw std::bad_alloc(); 40 | } 41 | 42 | void deallocate(T* p, std::size_t n) noexcept { std::free(p); } 43 | }; 44 | 45 | template <class T, class U> bool operator==(const Mallocator<T>&, const Mallocator<U>&) { 46 | return true; 47 | } 48 | 49 | template <class T, class U> bool operator!=(const Mallocator<T>&, const Mallocator<U>&) { 50 | return false; 51 | } 52 | 53 | class MemoryTracker { 54 | using AllocationsMap = std::unordered_map<void*, size_t, std::hash<void*>, std::equal_to<void*>, 55 | Mallocator<std::pair<void* const, size_t>>>; 56 | size_t bytes_outstanding = 0; 57 | AllocationsMap allocations; 58 | 59 | public: 60 | void* allocate(size_t size) { 61 | if (size == 0) 62 | ++size; // avoid std::malloc(0) which may return nullptr on success 63 | 64 | void* ptr = std::malloc(size); 65 | if (ptr == nullptr) 66 | throw std::bad_alloc{}; // required by [new.delete.single]/3 67 | 68 | bytes_outstanding += size; 69 | allocations[ptr] = size; 70 | return ptr; 71 | } 72 | 73 | void deallocate(void* ptr) { 74 | std::free(ptr); 75 | auto it = allocations.find(ptr); 76 | if (it != allocations.end()) { 77 | bytes_outstanding -= it->second; 78 | allocations.erase(it); 79 | } 80 | } 81 | 82 | size_t get_bytes_outstanding() const { return bytes_outstanding; } 83 | }; 84 | 85 | MemoryTracker& get_tracker() { 86 | static MemoryTracker tracker; 87 | return tracker; 88 | } 89 | 90 | } // namespace detail 91 | 92 | /** 93 | * MemoryGuard makes sure that the dynamic allocation count at construction 94 | * matches the count at destruction. If the counts do not match, MemoryGuard 95 | * will print an error message to stderr and exit the program with a predefined 96 | * exit code. 97 | */ 98 | class MemoryGuard { 99 | public: 100 | MemoryGuard(const char* message) 101 | : message(message), initial_bytes_outstanding(detail::get_tracker().get_bytes_outstanding()) { 102 | } 103 | 104 | ~MemoryGuard() { 105 | if (initial_bytes_outstanding != detail::get_tracker().get_bytes_outstanding()) { 106 | if (message) 107 | std::cerr << message << std::endl; 108 | std::exit(exit_code); 109 | } 110 | } 111 | 112 | static int get_exit_code() { return exit_code; } 113 | 114 | private: 115 | const char* message = nullptr; 116 | size_t initial_bytes_outstanding; 117 | static constexpr const int exit_code = 106; 118 | }; 119 | 120 | } // namespace MemoryDiagnostics 121 | 122 | void* operator new(std::size_t sz) { return MemoryDiagnostics::detail::get_tracker().allocate(sz); } 123 | 124 | void* operator new[](std::size_t sz) { 125 | return MemoryDiagnostics::detail::get_tracker().allocate(sz); 126 | } 127 | 128 | void operator delete(void* ptr) noexcept { 129 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr); 130 | } 131 | 132 | void operator delete(void* ptr, std::size_t size) noexcept { 133 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr); 134 | } 135 | 136 | void operator delete[](void* ptr) noexcept { 137 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr); 138 | } 139 | 140 | void operator delete[](void* ptr, std::size_t size) noexcept { 141 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr); 142 | } -------------------------------------------------------------------------------- /assignment5/autograder/utils.hpp: -------------------------------------------------------------------------------- 1 | int 2 | run_autograder() 3 | { 4 | auto run_program = [](std::string program, 5 | std::initializer_list<std::string> args, 6 | bool silent = false) { 7 | std::stringstream ss; 8 | 9 | ss << program; 10 | for (const auto& arg : args) { 11 | ss << ' ' << arg; 12 | } 13 | 14 | if (silent) { 15 | #ifdef _WIN32 16 | ss << " >nul 2>&1"; 17 | #else 18 | ss << " >/dev/null 2>&1"; 19 | #endif 20 | } 21 | 22 | std::cout.flush(); 23 | return system(ss.str().c_str()); 24 | }; 25 | 26 | std::string python; 27 | for (const auto& option : 28 | { "python", "python3", "/usr/bin/python3", "/usr/bin/python" }) { 29 | if (run_program(option, { "--version" }, true) == 0) { 30 | python = option; 31 | break; 32 | } 33 | } 34 | 35 | if (python.empty()) { 36 | std::cerr 37 | << "Python was not found on your system. Please install Python and " 38 | "try again." 39 | << "\n"; 40 | std::exit(1); 41 | } 42 | 43 | return run_program(python, { "autograder/autograder.py" }); 44 | } -------------------------------------------------------------------------------- /assignment5/docs/logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment5/docs/logo.jpeg -------------------------------------------------------------------------------- /assignment5/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CS106L Assignment 5: TreeBook 3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca. 4 | */ 5 | 6 | /* ========================================================================= * 7 | * Don't worry about anything beyond this point. * 8 | * (unless you are really curious and want to!) * 9 | * ========================================================================= */ 10 | 11 | #include <concepts> 12 | #include <fstream> 13 | #include <functional> 14 | #include <ios> 15 | #include <iostream> 16 | #include <sstream> 17 | #include <string> 18 | #include <unordered_map> 19 | #include <vector> 20 | 21 | #include "autograder/diagnostics.hpp" 22 | #include "autograder/utils.hpp" 23 | #include "user.h" 24 | 25 | void test_memory_leak_exit_code() { 26 | std::cout << MemoryDiagnostics::MemoryGuard::get_exit_code() << "\n"; 27 | } 28 | 29 | template <typename T> 30 | concept printable = requires(T t) { 31 | { std::cout << t } -> std::same_as<std::ostream&>; 32 | }; 33 | 34 | template <typename TUser> void test_insertion_operator() { 35 | if constexpr (printable<TUser>) { 36 | TUser alice("Alice"); 37 | std::cout << alice << "\n"; 38 | 39 | TUser bob("Bob"); 40 | bob.add_friend("Alice"); 41 | std::cout << bob << "\n"; 42 | 43 | TUser charlie("Charlie"); 44 | charlie.add_friend("Alice"); 45 | charlie.add_friend("Bob"); 46 | std::cout << charlie << "\n"; 47 | } else { 48 | std::cerr << "User does not have a valid operator<<." << "\n"; 49 | std::exit(1); 50 | } 51 | } 52 | 53 | template <typename TUser> void test_destructor() { 54 | MemoryDiagnostics::MemoryGuard guard( 55 | "Did you remember to delete[] _friends inside the destructor?"); 56 | TUser alice("Alice"); 57 | for (size_t i = 0; i < 100; ++i) { 58 | alice.add_friend("Friend"); 59 | } 60 | } 61 | 62 | template <typename TUser> void test_copy_constructor() { 63 | if constexpr (printable<TUser>) { 64 | if constexpr (std::is_copy_constructible_v<TUser>) { 65 | TUser a("Alice"); 66 | for (size_t i = 0; i < 9; i++) { 67 | a.add_friend("F"); 68 | } 69 | 70 | TUser b(a); 71 | b.set_friend(0, "Bob"); 72 | b.set_friend(1, "Alice"); 73 | b.set_friend(2, "Charlie"); 74 | 75 | std::cout << a << "\n"; 76 | std::cout << b << "\n"; 77 | } else { 78 | std::cerr << "User does not have a valid copy constructor." << "\n"; 79 | std::exit(1); 80 | } 81 | } else { 82 | std::cerr << "User does not have a valid operator<<." << "\n"; 83 | std::exit(1); 84 | } 85 | } 86 | 87 | template <typename TUser> void test_copy_assignment() { 88 | if constexpr (printable<TUser>) { 89 | if constexpr (std::is_copy_assignable_v<TUser>) { 90 | MemoryDiagnostics::MemoryGuard guard( 91 | "Did you remember to delete[] the old value of _friends inside the copy " 92 | "assignment operator?"); 93 | 94 | TUser a("Alice"); 95 | for (size_t i = 0; i < 9; i++) { 96 | a.add_friend("AF"); 97 | } 98 | 99 | TUser b("Bob"); 100 | for (size_t i = 0; i < 9; i++) { 101 | b.add_friend("BF"); 102 | } 103 | 104 | a = b; 105 | 106 | std::cout << a << "\n"; 107 | std::cout << b << "\n"; 108 | } else { 109 | std::cerr << "User does not have a valid copy constructor." << "\n"; 110 | std::exit(1); 111 | } 112 | } else { 113 | std::cerr << "User does not have a valid operator<<." << "\n"; 114 | std::exit(1); 115 | } 116 | } 117 | 118 | template <typename TUser> void test_move_constructor() { 119 | if constexpr (std::move_constructible<TUser>) { 120 | std::cerr << "User should not be move constructible. Did you remember to " 121 | "delete the move constructor?\n"; 122 | std::exit(1); 123 | } 124 | } 125 | 126 | template <typename TUser> void test_move_assignment() { 127 | if constexpr (std::is_move_assignable_v<TUser>) { 128 | std::cerr << "User should not be move assignable. Did you remember to " 129 | "delete the move assignment operator?\n"; 130 | std::exit(1); 131 | } 132 | } 133 | 134 | template <typename T> 135 | concept compound_assignable = requires(T a, T b) { 136 | { a += b } -> std::same_as<T&>; 137 | }; 138 | 139 | template <typename TUser> void test_compound_assignment() { 140 | if constexpr (printable<TUser>) { 141 | if constexpr (compound_assignable<TUser>) { 142 | TUser a("Alice"); 143 | TUser b("Bob"); 144 | a += b; 145 | std::cout << a << "\n"; 146 | std::cout << b << "\n"; 147 | } else { 148 | std::cerr << "User does not have a valid operator+=. Function signature " 149 | "should be:\n\n\tUser& operator+=(User& " 150 | "other);\n\ninside the User class." 151 | << "\n"; 152 | std::exit(1); 153 | } 154 | } else { 155 | std::cerr << "User does not have a valid operator<<." << "\n"; 156 | std::exit(1); 157 | } 158 | } 159 | 160 | template <typename TUser> 161 | concept comparable = requires(TUser a, TUser b) { 162 | { a < b } -> std::same_as<bool>; 163 | }; 164 | 165 | template <typename TUser> void test_comparable() { 166 | if constexpr (comparable<TUser>) { 167 | TUser a("A"); 168 | TUser b("B"); 169 | TUser c("C"); 170 | 171 | std::vector<std::pair<TUser, TUser>> pairs = {{a, b}, {b, a}, {b, c}, {c, b}, {c, a}, {a, c}}; 172 | std::cout << std::boolalpha; 173 | for (const auto& [left, right] : pairs) { 174 | std::cout << left.get_name() << " < " << right.get_name() << " is " << (left < right) << "\n"; 175 | } 176 | } else { 177 | std::cerr << "User does not have a valid operator<. Function signature " 178 | "should be:\n\n\tbool operator<(const " 179 | "User& other);\n\ninside the User class." 180 | << "\n"; 181 | std::exit(1); 182 | } 183 | } 184 | 185 | const std::unordered_map<std::string, std::function<void()>> test_functions = { 186 | {"memory_leak_exit_code", test_memory_leak_exit_code}, 187 | {"insertion_operator", test_insertion_operator<User>}, 188 | {"destructor", test_destructor<User>}, 189 | {"copy_constructor", test_copy_constructor<User>}, 190 | {"copy_assignment", test_copy_assignment<User>}, 191 | {"move_constructor", test_move_constructor<User>}, 192 | {"move_assignment", test_move_assignment<User>}, 193 | {"compound_assignment", test_compound_assignment<User>}, 194 | {"comparable", test_comparable<User>}}; 195 | 196 | int main(int argc, char* argv[]) { 197 | if (argc == 2) { 198 | if (test_functions.find(argv[1]) != test_functions.end()) { 199 | test_functions.at(argv[1])(); 200 | return 0; 201 | } 202 | 203 | std::cerr << "Test '" << argv[1] << "' not found." << "\n"; 204 | return 1; 205 | } 206 | 207 | return run_autograder(); 208 | } -------------------------------------------------------------------------------- /assignment5/user.cpp: -------------------------------------------------------------------------------- 1 | #include "user.h" 2 | 3 | /** 4 | * Creates a new User with the given name and no friends. 5 | */ 6 | User::User(const std::string& name) 7 | : _name(name) 8 | , _friends(nullptr) 9 | , _size(0) 10 | , _capacity(0) 11 | { 12 | } 13 | 14 | /** 15 | * Adds a friend to this User's list of friends. 16 | * @param name The name of the friend to add. 17 | */ 18 | void 19 | User::add_friend(const std::string& name) 20 | { 21 | if (_size == _capacity) { 22 | _capacity = 2 * _capacity + 1; 23 | std::string* newFriends = new std::string[_capacity]; 24 | for (size_t i = 0; i < _size; ++i) { 25 | newFriends[i] = _friends[i]; 26 | } 27 | delete[] _friends; 28 | _friends = newFriends; 29 | } 30 | 31 | _friends[_size++] = name; 32 | } 33 | 34 | /** 35 | * Returns the name of this User. 36 | */ 37 | std::string 38 | User::get_name() const 39 | { 40 | return _name; 41 | } 42 | 43 | /** 44 | * Returns the number of friends this User has. 45 | */ 46 | size_t 47 | User::size() const 48 | { 49 | return _size; 50 | } 51 | 52 | /** 53 | * Sets the friend at the given index to the given name. 54 | * @param index The index of the friend to set. 55 | * @param name The name to set the friend to. 56 | */ 57 | void User::set_friend(size_t index, const std::string& name) 58 | { 59 | _friends[index] = name; 60 | } 61 | 62 | /** 63 | * STUDENT TODO: 64 | * The definitions for your custom operators and special member functions will go here! 65 | */ 66 | -------------------------------------------------------------------------------- /assignment5/user.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CS106L Assignment 5: TreeBook 3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca. 4 | */ 5 | 6 | #include <iostream> 7 | #include <string> 8 | 9 | class User 10 | { 11 | public: 12 | User(const std::string& name); 13 | void add_friend(const std::string& name); 14 | std::string get_name() const; 15 | size_t size() const; 16 | void set_friend(size_t index, const std::string& name); 17 | 18 | /** 19 | * STUDENT TODO: 20 | * Your custom operators and special member functions will go here! 21 | */ 22 | 23 | private: 24 | std::string _name; 25 | std::string* _friends; 26 | size_t _size; 27 | size_t _capacity; 28 | }; -------------------------------------------------------------------------------- /assignment6/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 6: Explore Courses 2 | 3 | Due Friday, May 30th at 11:59PM 4 | 5 | ## Overview 6 | 7 | In this assignment you will be exercising your understanding of `std::optional`. We'll be making use of the same `courses.csv` from assignment 1. You are tasked to write one function for this assignment, which attempts to find the a `Course` in the `CourseDatabase` object, and return it. 8 | You'll also explore the monadic operations that come with the `std::optional` class. Take a look at the code and review the `CourseDatabase` class to understand the interface. 9 | 10 | ## Running your code 11 | 12 | To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit <kbd>Ctrl+\`</kbd> or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assignment6/` directory and run: 13 | 14 | ```sh 15 | g++ -std=c++23 main.cpp -o main 16 | ``` 17 | 18 | Assuming that your code compiles without any compiler errors, you can now do: 19 | 20 | ```sh 21 | ./main 22 | ``` 23 | 24 | which will actually run the `main` function in `main.cpp`. 25 | 26 | As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track! 27 | 28 | > [!NOTE] 29 | > 30 | > ### Note for Windows 31 | > 32 | > On Windows, you may need to compile your code using 33 | > 34 | > ```sh 35 | > g++ -static-libstdc++ -std=c++23 main.cpp -o main 36 | > ``` 37 | > 38 | > in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with: 39 | > 40 | > ```sh 41 | > ./main.exe 42 | > ``` 43 | 44 | ## Part 0: Include `<optional>` 45 | 46 | At the top of the `main.cpp` include `<optional>`, we're going to make use of `std::optional` in this assignment! 47 | 48 | ## Part 1: Write the `find_course` function 49 | 50 | This function takes in a string `course_title`, and the function should try to find the `course` inside of the private `courses` member of the `CourseDatabase` object. What should the return type be? (hint: there may or may not be a `Course` for the `course_title` passed in) 51 | 52 | > [!NOTE] 53 | > You need to change the type returned by `find_course` which is currenty `FillMeIn`. 54 | 55 | ## Part 2: Modifying the `main` function 56 | 57 | Notice that we call the `find_course` here in the `main` function: 58 | 59 | ```cpp 60 | auto course = db.find_course(argv[1]); 61 | ``` 62 | 63 | Now, you need to make use of the [monadic operations](https://en.cppreference.com/w/cpp/utility/optional) to populate the `output` string properly. Let's walk through how to do this. 64 | 65 | Here's the behavior that you want to recreate, **without using any conditionals** like `if` statements: 66 | ```cpp 67 | if (course.has_value()) { 68 | std::cout << "Found course: " << course->title << "," 69 | << course->number_of_units << "," << course->quarter << "\n"; 70 | } else { 71 | std::cout << "Course not found.\n"; 72 | } 73 | ``` 74 | 75 | Very simply, if there is a course then the line at the bottom of `main` 76 | 77 | ```cpp 78 | std::cout << output << std::end; 79 | ``` 80 | 81 | Should produce: 82 | ```bash 83 | Found course: <title>,<number_of_units>,<quarter> 84 | ``` 85 | 86 | if there is no course then 87 | 88 | ```cpp 89 | std::cout << output << std::end; 90 | ``` 91 | 92 | Should produce: 93 | ```bash 94 | Course not found. 95 | ``` 96 | 97 | ### Monadic Operations 98 | 99 | There are three monadic operations: [`and_then`](https://en.cppreference.com/w/cpp/utility/optional/and_then), [`transform`](https://en.cppreference.com/w/cpp/utility/optional/transform), and [`or_else`](https://en.cppreference.com/w/cpp/utility/optional/or_else). Read the description of each of them in the lecture slides, and take a look at [the standard library documentation](https://en.cppreference.com/w/cpp/utility/optional). You will only need to use 2 of the mondadic operations. 100 | 101 | Your code should end up looking something like this: 102 | 103 | ```cpp 104 | std::string output = course 105 | ./* monadic function one */ (/* ... */) 106 | ./* monadic function two */ (/* ... */) 107 | .value(); // OR `.value_or(...)`, see below 108 | ``` 109 | 110 | It can help to **think about what the type of `output` is and work backwards from there**. Pay attention to what each of the monadic functions does, as described in the hint below. 111 | 112 | > [!NOTE] 113 | > Recall what the role is of each of the monadic functions. The official C++ library doesn't do a good job explaining this, so we have included a short reference here. Suppose `T` and `U` are arbitrary types. 114 | > 115 | > ```cpp 116 | > /** 117 | > * tl;dr; 118 | > * Calls a function to produce a new optional if there is a value; otherwise, returns nothing. 119 | > * 120 | > * The function passed to `and_then` takes a non-optional instance of type `T` and returns a `std::optional<U>`. 121 | > * If the optional has a value, `and_then` applies the function to its value and returns the result. 122 | > * If the optional doesn't have a value (i.e. it is `std::nullopt`), it returns `std::nullopt`. 123 | > */ 124 | > template <typename U> 125 | > std::optional<U> std::optional<T>::and_then(std::function<std::optional<U>(T)> func); 126 | > 127 | > /** 128 | > * tl;dr; 129 | > * Applies a function to the stored value if present, wrapping the result in an optional, or returns nothing otherwise. 130 | > * 131 | > * The function passed to `transform` takes a non-optional instance of type `T` and returns a non-optional instance of type `U`. 132 | > * If the optional has a value, `transform` applies the function to its value and returns the result wrapped in an `std::optional<U>`. 133 | > * If the optional doesn't have a value (i.e. it is `std::nullopt`), it returns `std::nullopt`. 134 | > */ 135 | > template <typename U> 136 | > std::optional<U> std::optional<T>::transform(std::function<U(T)> func); 137 | > 138 | > /** 139 | > * tl;dr; 140 | > * Returns the optional itself if it has a value; otherwise, it calls a function to produce a new optional. 141 | > * 142 | > * The opposite of `and_then`. 143 | > * The function passed to `or_else` takes in no arguments and returns a `std::optional<U>`. 144 | > * If the optional has a value, `or_else` returns it. 145 | > * If the optional doesn't have a value (i.e. it is `std::nullopt`), `or_else invokes the function and returns the result. 146 | > */ 147 | > template <typename U> 148 | > std::optional<U> std::optional<T>::or_else(std::function<std::optional<U>(T)> func); 149 | > ``` 150 | > 151 | > For example, given a `std::optional<T> opt` object, the monadic operations could be invoked as follows: 152 | > 153 | > ```cpp 154 | > opt 155 | > .and_then([](T value) -> std::optional<U> { return /* ... */; }) 156 | > .transform([](T value) -> U { return /* ... */; }); 157 | > .or_else([]() -> std::optional<U> { return /* ... */; }) 158 | > ``` 159 | > 160 | > <sup>Note that the `->` notation in the lambda function is a way of explicitly writing out the return type of the function!</sup> 161 | > 162 | > Notice that since each method returns an `std::optional`, you can chain them together. If you are certain that the optional will have a value at the end of the chain, you could call [`.value()`](https://en.cppreference.com/w/cpp/utility/optional/value) to get the value. Otherwise, you could call [`.value_or(fallback)`](https://en.cppreference.com/w/cpp/utility/optional/value_or) to get the result or some other `fallback` value if the optional doesn't have a value. 163 | 164 | 165 | 166 | ## 🚀 Submission Instructions 167 | 168 | Before you submit the assignment, please fill out this [short feedback form](https://forms.gle/cZGTbEQ8bp8awoA3A). **Completion of the form is required to receive credit for the assignment.** After filling out the form, please upload the files to Paperless under the correct assignment heading. 169 | 170 | Your deliverable should be: 171 | 172 | - `main.cpp` 173 | 174 | You may resubmit as many times as you'd like before the deadline. 175 | -------------------------------------------------------------------------------- /assignment6/autograder/autograder.py: -------------------------------------------------------------------------------- 1 | from utils import Autograder, ASSIGNMENT_DIR 2 | from typing import List 3 | 4 | import os 5 | import subprocess 6 | 7 | 8 | def get_main_exe_path() -> os.PathLike: 9 | main_path = os.path.join(ASSIGNMENT_DIR, "main") 10 | if not os.path.isfile(main_path): 11 | main_path = os.path.join(ASSIGNMENT_DIR, "main.exe") 12 | if not os.path.isfile(main_path): 13 | raise RuntimeError( 14 | "Could not find a main executable. Did you compile your code with the command in the README?" 15 | ) 16 | return main_path 17 | 18 | 19 | def verify_output(courses: List[str], found: bool): 20 | main_path = get_main_exe_path() 21 | 22 | course_names = [course.split(",")[0] for course in courses] 23 | for course_name, course in zip(course_names, courses): 24 | result = subprocess.run( 25 | [main_path, course_name], 26 | stdout=subprocess.PIPE, 27 | stderr=subprocess.PIPE, 28 | text=True, 29 | ) 30 | 31 | if result.returncode != 0: 32 | raise RuntimeError(result.stderr) 33 | 34 | actual = result.stdout.strip() 35 | 36 | expected = f"Found course: {course}" if found else "Course not found." 37 | 38 | if actual != expected: 39 | raise RuntimeError( 40 | f"For course {course_name}, expected output:\n\t{expected}\nbut got:\n\t{actual}" 41 | ) 42 | 43 | 44 | if __name__ == "__main__": 45 | grader = Autograder() 46 | grader.add_part( 47 | "Present Courses", 48 | lambda: verify_output( 49 | [ 50 | "Introduction to Mobile Augmented Reality Design and Development,1,2023-2024 Spring", 51 | "Design for Play (SYMSYS 195G),4,2023-2024 Spring", 52 | "Practical Machine Learning,4,null", 53 | "Human-Centered AI,3,null", 54 | "Seminar in Artificial Intelligence in Healthcare,1,2023-2024 Autumn", 55 | "Introduction to Python Programming,1,2023-2024 Autumn", 56 | "Machine Learning for Discrete Optimization (MS&E 236),3,2023-2024 Spring", 57 | "AI for Social Good,2,2023-2024 Spring", 58 | "Software Project Experience with Corporate Partners,4,2023-2024 Winter", 59 | "The Future of Mechanical Engineering (ME 228),1,null", 60 | ], 61 | True, 62 | ), 63 | ) 64 | 65 | grader.add_part( 66 | "Missing Courses", 67 | lambda: verify_output( 68 | [ 69 | "Introduction to Quantum Mechanics for Engineers", 70 | "Fundamentals of Blockchain and Cryptography", 71 | "Applied Data Science for Social Impact", 72 | "Advanced Robotics and Autonomous Systems", 73 | "Neural Networks and Deep Learning Fundamentals", 74 | "Exploring the Future of Artificial General Intelligence", 75 | "Data Ethics and Privacy in the Digital Age", 76 | "Introduction to Cloud Computing with AWS", 77 | "Creative Coding for Interactive Media", 78 | "Virtual Reality and Augmented Reality for Healthcare", 79 | ], 80 | False, 81 | ), 82 | ) 83 | 84 | grader.run(), 85 | -------------------------------------------------------------------------------- /assignment6/autograder/utils.hpp: -------------------------------------------------------------------------------- 1 | #include <iostream> 2 | #include <sstream> 3 | #include <string> 4 | #include <fstream> 5 | 6 | struct Course; 7 | using FillMeIn = Course*; 8 | 9 | int 10 | run_autograder() 11 | { 12 | auto run_program = [](std::string program, 13 | std::initializer_list<std::string> args, 14 | bool silent = false) { 15 | std::stringstream ss; 16 | 17 | ss << program; 18 | for (const auto& arg : args) { 19 | ss << ' ' << arg; 20 | } 21 | 22 | if (silent) { 23 | #ifdef _WIN32 24 | ss << " >nul 2>&1"; 25 | #else 26 | ss << " >/dev/null 2>&1"; 27 | #endif 28 | } 29 | 30 | std::cout.flush(); 31 | return system(ss.str().c_str()); 32 | }; 33 | 34 | std::string python; 35 | for (const auto& option : 36 | { "python", "python3", "/usr/bin/python3", "/usr/bin/python" }) { 37 | if (run_program(option, { "--version" }, true) == 0) { 38 | python = option; 39 | break; 40 | } 41 | } 42 | 43 | if (python.empty()) { 44 | std::cerr 45 | << "Python was not found on your system. Please install Python and " 46 | "try again." 47 | << "\n"; 48 | std::exit(1); 49 | } 50 | 51 | return run_program(python, { "autograder/autograder.py" }); 52 | } 53 | 54 | std::vector<std::string> split(std::string s, char delim) { 55 | std::vector<std::string> return_vec; 56 | std::stringstream ss(s); 57 | std::string token; 58 | while (getline(ss, token, delim)) { 59 | return_vec.push_back(token); 60 | } 61 | return return_vec; 62 | } 63 | 64 | std::vector<std::string> read_lines(std::string filename) { 65 | std::vector<std::string> lines; 66 | std::ifstream file(filename); 67 | if (!file.is_open()) { 68 | std::cerr << "Could not open file " << filename << "\n"; 69 | std::exit(1); 70 | } 71 | 72 | std::string line; 73 | while (std::getline(file, line)) lines.push_back(line); 74 | return lines; 75 | } -------------------------------------------------------------------------------- /assignment6/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CS106L Assignment 6: Explore Courses 3 | * Created by Haven Whitney with modifications by Jacob Roberts-Baca and Fabio 4 | * Ibanez. 5 | */ 6 | 7 | #include <algorithm> 8 | #include <type_traits> 9 | #include <vector> 10 | 11 | /** STUDENT_TODO: You will need to include a relevant header file here! */ 12 | 13 | #include "autograder/utils.hpp" 14 | 15 | /** 16 | * A course. This should be familiar from Assignment 1! 17 | */ 18 | struct Course 19 | { 20 | std::string title; 21 | std::string number_of_units; 22 | std::string quarter; 23 | 24 | /** 25 | * You don't have to ignore this anymore! We're defining the `==` operator for 26 | * the Course struct. 27 | */ 28 | bool operator==(const Course& other) const 29 | { 30 | return title == other.title && number_of_units == other.number_of_units && 31 | quarter == other.quarter; 32 | } 33 | }; 34 | 35 | class CourseDatabase 36 | { 37 | public: 38 | CourseDatabase(std::string filename) 39 | { 40 | auto lines = read_lines(filename); 41 | std::transform(lines.begin(), 42 | lines.end(), 43 | std::back_inserter(courses), 44 | [](std::string line) { 45 | auto parts = split(line, ','); 46 | return Course{ parts[0], parts[1], parts[2] }; 47 | }); 48 | } 49 | 50 | /** 51 | * Finds a course in the database with the given title, if it exists. 52 | * @param course_title The title of the course to find. 53 | * @return You will need to figure this out! 54 | */ 55 | FillMeIn find_course(std::string course_title) 56 | { 57 | /* STUDENT_TODO: Implement this method! You will need to change the return 58 | * type. */ 59 | } 60 | 61 | private: 62 | std::vector<Course> courses; 63 | }; 64 | 65 | int 66 | main(int argc, char* argv[]) 67 | { 68 | static_assert( 69 | !std::is_same_v<std::invoke_result_t<decltype (&CourseDatabase::find_course), 70 | CourseDatabase, std::string>, 71 | FillMeIn>, 72 | "You must change the return type of CourseDatabase::find_course to " 73 | "something other than FillMeIn."); 74 | 75 | if (argc == 2) { 76 | CourseDatabase db("autograder/courses.csv"); 77 | auto course = db.find_course(argv[1]); 78 | 79 | /******************************************************** 80 | STUDENT_TODO: Populate the output string with the right information to print 81 | Please pay special attention to the README here 82 | ********************************************************/ 83 | 84 | std::string output = /* STUDENT_TODO */ 85 | 86 | /******************************************************** 87 | DO NOT MODIFY ANYTHING BELOW THIS LINE PLEASE 88 | ********************************************************/ 89 | 90 | std::cout << output << std::endl; 91 | return 0; 92 | } 93 | 94 | return run_autograder(); 95 | } -------------------------------------------------------------------------------- /assignment7/README.md: -------------------------------------------------------------------------------- 1 | <p align="center"> 2 | <img src="docs/art.png" /> 3 | </p> 4 | 5 | # Assignment 7: Unique Pointer 6 | 7 | Due Friday, June 6th at 11:59PM 8 | 9 | ## Overview 10 | 11 | In this assignment, you will implement a custom version of `unique_ptr` in order to gain some exposure to concepts like RAII and smart pointers that were introduced in lecture this week. In addition, you will exercise some of the skills we've seen throughout the course: templates, operator overloading, and move semantics. 12 | 13 | There are three files you'll work with for this assignment: 14 | 15 | - `unique_ptr.h` - Contains all the code for your `unique_ptr` implementation. 16 | - `main.cpp` - Contains some code that uses your `unique_ptr`. You'll write one function here! 17 | - `short_answer.txt` - Contains a few short answer questions you'll answer as you work on the assignment. 18 | 19 | ## Running your code 20 | 21 | To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit <kbd>Ctrl+\`</kbd> or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assign7/` directory and run: 22 | 23 | ```sh 24 | g++ -std=c++20 main.cpp -o main 25 | ``` 26 | 27 | Assuming that your code compiles without any compiler errors, you can now do: 28 | 29 | ```sh 30 | ./main 31 | ``` 32 | 33 | which will actually run the `main` function in `main.cpp`. 34 | 35 | As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track! 36 | 37 | > [!NOTE] 38 | > 39 | > ### Note for Windows 40 | > 41 | > On Windows, you may need to compile your code using 42 | > 43 | > ```sh 44 | > g++ -static-libstdc++ -std=c++20 main.cpp -o main 45 | > ``` 46 | > 47 | > in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with: 48 | > 49 | > ```sh 50 | > ./main.exe 51 | > ``` 52 | 53 | ## Part 1: Implementing `unique_ptr` 54 | 55 | In the first part of the assignment, you will implement one of the smart pointers we discussed in Thursday's lecture: `unique_ptr`. The `unique_ptr` you will implement is a simpler version of the standard library's [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr). Recall that a `unique_ptr` represents a pointer to dynamically allocated memory that is owned by a single (*unique*) variable. When that variable goes out of scope, it automatically cleans up the allocated memory that it owns by calling `delete`. This behaviour is known as RAII (resource acquisition is initialization). **For our purposes, you may assume that `unique_ptr` points to a single element of type T. You will not have to call `delete[]` at any point or handle pointers to dynamically allocated arrays.** 56 | 57 | > [!IMPORTANT] 58 | > ##### `short_answer.txt` 59 | > **Q1:** List one or two benefits of using RAII to manage memory instead manually calling `new` and `delete`. 60 | 61 | > [!NOTE] 62 | > While our `unique_ptr` will not support pointers to arrays, we could add this behaviour if we wanted to. For example, the C++ standard library `std::unique_ptr` uses a *template specialization* to implement different behaviour for array pointers. Such a template specialization might look like this: 63 | > 64 | > ```cpp 65 | > template <typename T> 66 | > class unique_ptr<T[]>; 67 | > ``` 68 | > 69 | > In effect, we would have two versions of `unique_ptr`: one for single elements and one for arrays of elements. Each version supports different operations; for example, the array version provides a subscript operator (`operator[]`) to deference elements in the array, while the single element version does not. 70 | 71 | ### Implementing `unique_ptr` functionality 72 | 73 | Take a moment to scan over the provided code for `unique_ptr` in `unique_ptr.h`. We have provided the basic interface for a `unique_ptr`: you will implement this interface. Remember, a `unique_ptr` should look and behave like a regular pointer, supporting operations like dereferencing (`operator*`) and member access (`operator->`). Several of these methods have both `const` and non-`const` versions in order for our class to be fully const-correct. 74 | 75 | You will implement the basic pointer interface for a `unique_ptr` by implementing the following points. Each of these tasks should be relatively straightforward and can be completed by adding/changing 1-2 lines in `unique_ptr.h`: 76 | 77 | * The `private` section of `unique_ptr` 78 | * `unique_ptr(T* ptr)` (constructor) 79 | * `unique_ptr(std::nullptr_t)` (constructor for `nullptr`) 80 | * `T& operator*()` 81 | * `const T& operator*() const` 82 | * `T* operator->()` 83 | * `const T* operator->() const` 84 | * `operator bool() const` 85 | 86 | ### Implementing RAII 87 | 88 | At this point, our `unique_ptr` will behave as if it were a raw pointer, but it will not actually do any automatic memory management such as deallocating memory when a `unique_ptr` variable goes out of scope. Add to that, our pointer is not *unique*: multiple copies of it (all pointing to the same memory) can be made indiscriminantly. For example, let's assume that our `unique_ptr` properly cleans up its data when it goes out of scope. Consider the following code block: 89 | 90 | ```cpp 91 | int main() 92 | { 93 | unique_ptr<int> ptr1 = make_unique<int>(5); 94 | 95 | // ptr1 points to 5 (dynamically allocated on the heap) 96 | 97 | { 98 | 99 | unique_ptr<int> ptr2 = ptr1; // shallow copy 100 | 101 | } // <-- data for ptr2 deallocated here 102 | 103 | std::cout << *ptr1 << std::endl; 104 | return 0; 105 | } 106 | ``` 107 | 108 | Since `ptr1` and `ptr2` point to the same memory, when `ptr2` goes out of scope, it takes `ptr1`'s data with it! As a result, `*ptr1` is undefined behaviour. 109 | 110 | On the other hand, we should still be able to **move** a `unique_ptr`. Recall that move semantics allows us to take ownership of an object's resources without making an expensive copy. Moving a unique pointer is valid because it preserves the uniqueness of the pointer — at any point in time, we still have only one pointer to the underlying memory. We simply change who (what variable) owns that memory. 111 | 112 | In order to achieve these goals — automatic deallocation of memory, no copying, and move semantics — we must implement some special member functions on the `unique_ptr` class. **Specifically, implement the following SMFs:** 113 | 114 | * `~unique_ptr()`: Deallocates the pointer's memory 115 | * `unique_ptr(const unique_ptr& other)`: Copies a unique pointer. Should be deleted. 116 | * `unique_ptr& operator=(const unique_ptr& other)`: Copy assigns a unique pointer. Should be deleted. 117 | * `unique_ptr(unique_ptr&& other)`: Moves a unique pointer. 118 | * `unique_ptr& operator=(unique_ptr&& other)`: Move assigns a unique pointer. 119 | 120 | After implementing the above functions, you should be passing all of the autograder tests for **Part 1**. 121 | 122 | > [!IMPORTANT] 123 | > ##### `short_answer.txt` 124 | > **Q2:** When implementing move semantics for a `unique_ptr`, for example in the move constructor `unique_ptr(unique_ptr&& other)`, it is essential that we set the underlying pointer of the `other` parameter to `nullptr` before exiting the function. Explain in your own words what problem would arise if we did not. 125 | 126 | ## Part 2: Using `unique_ptr` 127 | 128 | Now that we have a `unique_ptr` implementation, let's use it! Take a look at `main.cpp`. We have given you a complete implementation of a singly-linked list (`ListNode`) that utilizes `unique_ptr` to ensure that all nodes in the list get deallocated properly. For example, this code produces the following output: 129 | 130 | ```cpp 131 | int main() 132 | { 133 | 134 | auto head = cs106l::make_unique<ListNode<int>>(1); 135 | head->next = cs106l::make_unique<ListNode<int>>(2); 136 | head->next->next = cs106l::make_unique<ListNode<int>>(3); 137 | 138 | // memory of head: 139 | // 140 | // head -> (1) -> (2) -> (3) -> nullptr 141 | // 142 | // 143 | 144 | } // <- `head` destructed here! 145 | 146 | // Output: 147 | // Constructing node with value '1' 148 | // Constructing node with value '2' 149 | // Constructing node with value '3' 150 | // Destructing node with value '1' 151 | // Destructing node with value '2' 152 | // Destructing node with value '3' 153 | ``` 154 | 155 | Notice that we didn't have to make any calls to `delete`! The RAII behaviour of `unique_ptr` guarantees that all memory in the list is deallocated recursively. When `head` goes out of scope, it calls the destructor of node `(1)`, which calls the destructor of `(2)`, which calls the destructor of `(3)`. 156 | 157 | > [!IMPORTANT] 158 | > ##### `short_answer.txt` 159 | > **Q3:** This method of recursive deallocation through RAII works great for small lists, but may pose a problem for longer lists. Why? Hint: what is the limit for how "deep" a recursive function's call stack can grow? 160 | 161 | **Your task is to implement the function `create_list` which converts a `std::vector<T>` into a `unique_ptr<ListNode<T>>`.** The order of elements in the vector should be preserved in the list, and `nullptr` should be returned for an empty vector. There are many ways you could go about this; one is to construct the list in reverse (starting at the tail and working towards the head). **Note that you must use the `cs106l::unique_ptr` under the `cs106l` namespace, and not the `std::unique_ptr`!** Here is an algorithm you should follow in your implementation: 162 | 163 | 1. Initialize a `cs106l::unique_ptr<ListNode<T>> head = nullptr`. 164 | 2. Iterate through the `std::vector` **backwards.** For each element in the vector: 165 | - 2a. Create a new `cs106l::unique_ptr<ListNode<T>> node` whose value is the element in the vector. 166 | - 2b. Set `node->next` to `head`. 167 | - 2c. Set `head` to `node` 168 | 3. Finally, return `head` 169 | 170 | > [!IMPORTANT] 171 | > ##### `short_answer.txt` 172 | > **Q4.** In your implementation of points 2b and 2c, you may have a hard time getting the compiler to allow you to assign, for example, `node->next` to `head` as it will complain that there is no copy assignment operator. That is exactly right, as `unique_ptr` cannot be copied as we discussed previously! 173 | > 174 | > In order to get the behaviour we want, we must force the compiler to **move assign** `head` into `node->next` rather than copy assign. Recall from the move semantics lecture that we can do this by writing `node->next = std::move(head)`. 175 | > 176 | > What does `std::move` do in this context? Why is it safe to use `std::move` and move semantics here? 177 | 178 | > [!NOTE] 179 | > Be careful of trying to use `size_t` as an index while looping backwards through a vector. `size_t` can only be a non-negative integer, and attempting to go below zero while checking the for loop bounds can lead to unexpected behaviour. 180 | > To fix this issue, try using an `int` instead. 181 | 182 | Once you've implemented `create_list`, we can now create a list and print it out. For brownie points, take a look at the `map_list()` and `linked_list_example()` functions which together call your `create_list` function and print out its elements each on their own line. At this point, you should pass all of the tests in **Part 2**. 183 | 184 | ## 🚀 Submission Instructions 185 | 186 | Before you submit the assignment, please fill out this [short feedback form](https://forms.gle/TXzLEgdKYnEPes22A). **Completion of the form is required to receive credit for the assignment.** After filling out the form, please upload the files to Paperless under the correct assignment heading. 187 | 188 | Your deliverable should be: 189 | 190 | - `unique_ptr.h` 191 | - `main.cpp` 192 | - `short_answer.txt` 193 | 194 | You may resubmit as many times as you'd like before the deadline. 195 | -------------------------------------------------------------------------------- /assignment7/autograder/autograder.py: -------------------------------------------------------------------------------- 1 | import functools 2 | from typing import List 3 | from utils import Autograder, ASSIGNMENT_DIR 4 | 5 | import os 6 | import subprocess 7 | 8 | 9 | def get_main_exe_path() -> os.PathLike: 10 | main_path = os.path.join(ASSIGNMENT_DIR, "main") 11 | if not os.path.isfile(main_path): 12 | main_path = os.path.join(ASSIGNMENT_DIR, "main.exe") 13 | if not os.path.isfile(main_path): 14 | raise RuntimeError( 15 | "Could not find a main executable. Did you compile your code with the command in the README?" 16 | ) 17 | return main_path 18 | 19 | 20 | @functools.lru_cache 21 | def get_memory_leak_exit_code() -> int: 22 | main_path = get_main_exe_path() 23 | result = subprocess.check_output([main_path, "memory_leak_exit_code"], text=True) 24 | return int(result.strip()) 25 | 26 | 27 | def verify_output( 28 | test_case: str, *, expected: List[str] = [], hint: str = "", empty_hint: str = "", show_diff: bool = True 29 | ): 30 | hint = hint.strip() 31 | has_hint = bool(hint) 32 | if has_hint: 33 | hint += " " 34 | 35 | main_path = get_main_exe_path() 36 | result = subprocess.run( 37 | [main_path, test_case], 38 | stdout=subprocess.PIPE, 39 | stderr=subprocess.PIPE, 40 | text=True, 41 | ) 42 | 43 | if result.returncode == get_memory_leak_exit_code(): 44 | raise RuntimeError(f"💧Memory leak detected: {result.stderr}") 45 | 46 | if result.returncode != 0: 47 | error = result.stderr.strip() 48 | if not error and empty_hint: 49 | raise RuntimeError(empty_hint) 50 | if has_hint: 51 | if error: 52 | raise RuntimeError(f"{hint}Got the following error message:\n\n{error}") 53 | raise RuntimeError(f"{hint}") 54 | raise RuntimeError(error) 55 | 56 | actual = result.stdout.strip() 57 | if not actual: 58 | actual = [] 59 | else: 60 | actual = actual.split("\n") 61 | 62 | if actual != expected: 63 | nl = "\n\t" 64 | if show_diff: 65 | raise RuntimeError( 66 | f"Test output did not match expected. Expected: \n{nl}{nl.join(expected)}\n\nGot: \n{nl}{nl.join(actual)}\n\n{hint}" 67 | ) 68 | raise RuntimeError(hint) 69 | 70 | 71 | if __name__ == "__main__": 72 | grader = Autograder() 73 | grader.add_part( 74 | "Part 1: Destructor", 75 | lambda: verify_output("destructor", hint="Did you make sure to set the underlying pointer to `nullptr` in the `nullptr` version of the constructor? If you didn't, you might be trying to delete an arbitrary region of memory!"), 76 | ) 77 | 78 | grader.add_part( 79 | "Part 1: Deleted copy constructor and assignment", lambda: verify_output("copy") 80 | ) 81 | 82 | grader.add_part( 83 | "Part 1: Move constructor", 84 | lambda: verify_output( 85 | "move_constructor", 86 | expected=["400308000"], 87 | hint="Did you make sure to set the other unique_ptr to nullptr in the move constructor? It may help to review the lecture on move semantics!", 88 | show_diff=False, 89 | ), 90 | ) 91 | 92 | grader.add_part( 93 | "Part 1: Move assignment", 94 | lambda: verify_output( 95 | "move_assignment", 96 | hint="Did you make sure to deallocate the existing data before assigning the private fields? It may help to review the lecture on move semantics!", 97 | show_diff=False, 98 | ), 99 | ) 100 | 101 | grader.add_part( 102 | "Part 1: Move assignment (self-assignment)", 103 | lambda: verify_output( 104 | "move_self_assignment", 105 | hint="Did you make sure to check for self-assignment in the move assignment operator? You should have code that checks `if (this == &other) ...`. It may help to review the lecture on move semantics!", 106 | expected=["400308000"], 107 | show_diff=False, 108 | ), 109 | ) 110 | 111 | grader.add_part( 112 | "Part 2: Linked List", 113 | lambda: verify_output( 114 | "linked_list", 115 | expected=[ 116 | "Constructing node with value 'Sean'", 117 | "Constructing node with value 'Chris'", 118 | "Constructing node with value 'Keith'", 119 | "Constructing node with value 'Fabio'", 120 | "Constructing node with value 'Jacob'", 121 | "Jacob", 122 | "Fabio", 123 | "Keith", 124 | "Chris", 125 | "Sean", 126 | "Destructing node with value 'Jacob'", 127 | "Destructing node with value 'Fabio'", 128 | "Destructing node with value 'Keith'", 129 | "Destructing node with value 'Chris'", 130 | "Destructing node with value 'Sean'", 131 | ], 132 | empty_hint="Hmm... that didn't work. Make sure that you are passing all of the previous tests! You might have an issue with your `unique_ptr` implementation." 133 | ), 134 | ) 135 | 136 | grader.run(), 137 | -------------------------------------------------------------------------------- /assignment7/autograder/diagnostics.hpp: -------------------------------------------------------------------------------- 1 | /** Contains memory diagnostics to track memory allocations and report memory leaks. */ 2 | 3 | #pragma once 4 | 5 | #include <cstdio> 6 | #include <cstdlib> 7 | #include <iostream> 8 | #include <limits> 9 | #include <new> 10 | #include <unordered_map> 11 | 12 | namespace MemoryDiagnostics { 13 | namespace detail { 14 | 15 | /** 16 | * In order to initialize an unordered_map inside of the MemoryTracker class below, 17 | * we must make dynamic allocations. However, the default allocator for unordered_map 18 | * calls operator new and delete, which would cause infinite recursion. To avoid this, 19 | * we define a custom allocator that uses std::malloc and std::free instead and define 20 | * the unordered_map in MemoryTracker to use this allocator. 21 | * 22 | * See https://en.cppreference.com/w/cpp/named_req/Allocator for more information. 23 | */ 24 | template <class T> struct Mallocator { 25 | using value_type = T; 26 | 27 | Mallocator() = default; 28 | 29 | template <class U> constexpr Mallocator(const Mallocator<U>&) noexcept {} 30 | 31 | [[nodiscard]] T* allocate(std::size_t n) { 32 | if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) 33 | throw std::bad_array_new_length(); 34 | 35 | if (auto p = static_cast<T*>(std::malloc(n * sizeof(T)))) { 36 | return p; 37 | } 38 | 39 | throw std::bad_alloc(); 40 | } 41 | 42 | void deallocate(T* p, std::size_t n) noexcept { std::free(p); } 43 | }; 44 | 45 | template <class T, class U> bool operator==(const Mallocator<T>&, const Mallocator<U>&) { 46 | return true; 47 | } 48 | 49 | template <class T, class U> bool operator!=(const Mallocator<T>&, const Mallocator<U>&) { 50 | return false; 51 | } 52 | 53 | class MemoryTracker { 54 | using AllocationsMap = std::unordered_map<void*, size_t, std::hash<void*>, std::equal_to<void*>, 55 | Mallocator<std::pair<void* const, size_t>>>; 56 | size_t bytes_outstanding = 0; 57 | AllocationsMap allocations; 58 | 59 | public: 60 | void* allocate(size_t size) { 61 | if (size == 0) 62 | ++size; // avoid std::malloc(0) which may return nullptr on success 63 | 64 | void* ptr = std::malloc(size); 65 | if (ptr == nullptr) 66 | throw std::bad_alloc{}; // required by [new.delete.single]/3 67 | 68 | bytes_outstanding += size; 69 | allocations[ptr] = size; 70 | return ptr; 71 | } 72 | 73 | void deallocate(void* ptr) { 74 | std::free(ptr); 75 | auto it = allocations.find(ptr); 76 | if (it != allocations.end()) { 77 | bytes_outstanding -= it->second; 78 | allocations.erase(it); 79 | } 80 | } 81 | 82 | size_t get_bytes_outstanding() const { return bytes_outstanding; } 83 | }; 84 | 85 | MemoryTracker& get_tracker() { 86 | static MemoryTracker tracker; 87 | return tracker; 88 | } 89 | 90 | } // namespace detail 91 | 92 | /** 93 | * MemoryGuard makes sure that the dynamic allocation count at construction 94 | * matches the count at destruction. If the counts do not match, MemoryGuard 95 | * will print an error message to stderr and exit the program with a predefined 96 | * exit code. 97 | */ 98 | class MemoryGuard { 99 | public: 100 | MemoryGuard(const char* message) 101 | : message(message), initial_bytes_outstanding(detail::get_tracker().get_bytes_outstanding()) { 102 | } 103 | 104 | ~MemoryGuard() { 105 | if (initial_bytes_outstanding != detail::get_tracker().get_bytes_outstanding()) { 106 | if (message) 107 | std::cerr << message << std::endl; 108 | std::exit(exit_code); 109 | } 110 | } 111 | 112 | static int get_exit_code() { return exit_code; } 113 | 114 | private: 115 | const char* message = nullptr; 116 | size_t initial_bytes_outstanding; 117 | static constexpr const int exit_code = 106; 118 | }; 119 | 120 | } // namespace MemoryDiagnostics 121 | 122 | void* operator new(std::size_t sz) { return MemoryDiagnostics::detail::get_tracker().allocate(sz); } 123 | 124 | void* operator new[](std::size_t sz) { 125 | return MemoryDiagnostics::detail::get_tracker().allocate(sz); 126 | } 127 | 128 | void operator delete(void* ptr) noexcept { 129 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr); 130 | } 131 | 132 | void operator delete(void* ptr, std::size_t size) noexcept { 133 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr); 134 | } 135 | 136 | void operator delete[](void* ptr) noexcept { 137 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr); 138 | } 139 | 140 | void operator delete[](void* ptr, std::size_t size) noexcept { 141 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr); 142 | } -------------------------------------------------------------------------------- /assignment7/autograder/utils.hpp: -------------------------------------------------------------------------------- 1 | /* ========================================================================= * 2 | * Don't worry about anything beyond this point. * 3 | * (unless you are really curious and want to!) * 4 | * ========================================================================= */ 5 | 6 | #include "diagnostics.hpp" 7 | 8 | static const std::string kReallyLongString = 9 | "this is a really long string! It should be long enough that attempting to iterate " 10 | "through it will eventually cause a segfault if the pointer is dangling, because some " 11 | "region of the memory is OOB. This string is brought to you by CS106L, the course that " 12 | "believes in the importance of understanding memory safety, because one missed pointer " 13 | "could mean the difference between A and F! Go Cardinal! Also, consider this a gentle " 14 | "reminder: this isn’t Python, so manual memory management may be included at no extra " 15 | "charge. You’re welcome. Speaking of pointers, has anyone seen the \"mystery byte\"? " 16 | "It’s just hanging around, waiting for a dereference. Maybe it’s busy \"pointing\" out " 17 | "the occasional nullptr. Ah, the smell of freshly allocated memory. By the way, in case " 18 | "you forgot, we're a couple hundred characters into this string now, and if you're " 19 | "reading this, you're probably wondering, 'Is there any actual purpose to this string, " 20 | "or is it just here to mess with us?' Answer: Yes. And if you think you’re almost at " 21 | "the end... not quite. Consider this a page-sized ode to the indomitable resilience " 22 | "of pointers, the unsung heroes of the stack and the heap alike. Here’s to them, " 23 | "navigating virtual landscapes one byte at a time. Now, let’s throw in some more filler " 24 | "text to make sure this string really hits the memory mark, like a long C++ template " 25 | "compile time, slow but ultimately fulfilling! Here's a toast to dangling pointers " 26 | "that never quite got to dangle, references that referenced but nobody cared, and " 27 | "nulls that nulled all our hopes and dreams. Cheers, and remember, this is CS106L, " 28 | "where every day is a new lesson in why C++ might actually be the most rewarding " 29 | "source of frustration you’ll ever encounter! Now, brace yourself, because we’re only " 30 | "halfway there. In the meantime, let’s review a few key takeaways from this course: " 31 | "Pointers may seem daunting, but once you master them, they’re basically pets that " 32 | "require a lot of attention (and feeding). Smart pointers? They’re like that pet that " 33 | "takes care of itself (most of the time), but when they don’t, good luck explaining " 34 | "the problem to the debugger. Let's dive deeper into memory safety: it's like living " 35 | "next to a fireworks factory — thrilling, a little dangerous, and you'll need to wear " 36 | "safety goggles (or at least code defensively). Don’t forget, C++ loves you, it just " 37 | "has an unusual way of showing it, like a friend who insists that 'new' and 'delete' " 38 | "are still a thing. In fact, using raw pointers in modern C++ is like riding a unicycle " 39 | "through San Francisco traffic: technically possible but highly inadvisable. And for " 40 | "those brave souls exploring STL containers, just remember, std::vector is like the " 41 | "cafeteria food line at Stanford: it has a fixed capacity, but there's always room for " 42 | "more if you ask politely (or push_back). Speaking of vectors, did you know std::string " 43 | "is basically a vector of characters? Which means this string here is essentially a " 44 | "really long vector. In fact, it's so long it might just loop back around and greet " 45 | "you on the other side of memory. But don’t let that distract you. Let’s continue, shall " 46 | "we? There's still plenty of memory to allocate, and we wouldn’t want to waste it. After " 47 | "all, we’re in Silicon Valley, where memory is abundant, though perhaps a bit overpriced. " 48 | "CS106L: exploring the unexplored realms of pointers, templates, and algorithms that " 49 | "feel like they should be simpler, but somehow they’re not. And now for an interlude: " 50 | "If you’ve made it this far, you’re either incredibly dedicated or you just scrolled down " 51 | "to see if I was serious about making this string long enough to fill a page. Spoiler " 52 | "alert: I was. Here’s more filler to add weight to this epic string: from unique_ptr to " 53 | "shared_ptr, the whole world of C++ is a carefully orchestrated chaos. Null pointers " 54 | "dream of electric dereferences, references hold their ground, and const qualifiers just " 55 | "sit there, unmoving, unyielding, a quiet reminder of immutability in an ever-changing " 56 | "world. And if you’re still reading, well, hats off to you. You’ve truly embodied the " 57 | "spirit of CS106L, the class where we redefine what it means to ‘go the extra mile’ in " 58 | "memory allocation. One last bit of advice: in C++, life is short, so make sure your " 59 | "references are always up to date. Also, remember that every dangling pointer is just " 60 | "a tragedy waiting to happen, and each memory leak a tear in the fabric of the stack. " 61 | "So go forth, code boldly, and may all your pointers land safely!"; 62 | 63 | void test_linked_list_example() { 64 | MemoryDiagnostics::MemoryGuard guard("Failed to deallocate all nodes in linked list!"); 65 | std::vector<std::string> names{"Jacob", "Fabio", "Keith", "Chris", "Sean"}; 66 | auto head = create_list(names); 67 | map_list(head, [](const std::string& name) { std::cout << name << "\n"; }); 68 | } 69 | 70 | void test_destructor() { 71 | MemoryDiagnostics::MemoryGuard guard("Failed to deallocate memory in unique_ptr destructor!"); 72 | for (size_t i = 0; i < 1000; ++i) { 73 | auto ptr = cs106l::make_unique<std::string>("hello"); 74 | } 75 | 76 | // Ensure that deallocation of empty unique_ptr is successful 77 | for (size_t i = 0; i < 1000; ++i) { 78 | cs106l::unique_ptr<std::string> ptr; 79 | } 80 | } 81 | 82 | void test_move_constructor() { 83 | MemoryDiagnostics::MemoryGuard guard( 84 | "Failed to deallocate memory in unique_ptr move constructor! It may help to review the " 85 | "lecture on move semantics!"); 86 | size_t sum = 0; 87 | for (size_t i = 0; i < 1000; ++i) { 88 | auto ptr = cs106l::make_unique<std::string>(kReallyLongString); 89 | auto size = ptr->size(); 90 | auto other = std::move(ptr); 91 | ptr.~unique_ptr(); // Forcibly destroy old pointer 92 | 93 | // Iterating through other should be valid. 94 | // Even though old was destroyed, we took ownership of the memory. 95 | 96 | for (size_t i = 0; i < size; ++i) { 97 | sum += (*other)[i]; 98 | } 99 | } 100 | 101 | std::cout << sum << "\n"; 102 | } 103 | 104 | void test_move_assignment() { 105 | MemoryDiagnostics::MemoryGuard guard( 106 | "Failed to deallocate memory in unique_ptr move assignment! Did you make sure to deallocate " 107 | "the existing data before assigning the private fields? It may help to review the lecture on " 108 | "move semantics!"); 109 | for (size_t i = 0; i < 1000; ++i) { 110 | auto ptr1 = cs106l::make_unique<std::string>("hello"); 111 | auto ptr2 = cs106l::make_unique<std::string>("world"); 112 | ptr1 = std::move(ptr2); 113 | if (ptr2) { 114 | std::cerr << "Move assignment failed to set other pointer to nullptr! It may help to review " 115 | "the lecture on move semantics!" 116 | << "\n"; 117 | std::exit(1); 118 | } 119 | } 120 | } 121 | 122 | void test_move_self_assignment() { 123 | MemoryDiagnostics::MemoryGuard guard( 124 | "Failed to deallocate memory in unique_ptr move assignment with self-assignment! It may help " 125 | "to review the lecture on move semantics!"); 126 | size_t sum = 0; 127 | for (size_t i = 0; i < 1000; ++i) { 128 | auto ptr = cs106l::make_unique<std::string>(kReallyLongString); 129 | ptr = std::move(ptr); 130 | 131 | // If we don't check for self-assignment in the move constructor, 132 | // then ptr will either be dangling or end up being nullptr. 133 | // This should check against that 134 | if (!ptr) 135 | break; 136 | 137 | for (size_t i = 0; i < ptr->size(); ++i) { 138 | sum += (*ptr)[i]; 139 | } 140 | } 141 | 142 | std::cout << sum << "\n"; 143 | } 144 | 145 | template <typename UniquePtr> void test_copy() { 146 | if constexpr (std::is_copy_constructible_v<UniquePtr>) { 147 | std::cerr << "unique_ptr should not be copy constructible. Did you remember to delete the copy " 148 | "constructor?\n"; 149 | std::exit(1); 150 | } 151 | 152 | if constexpr (std::is_copy_assignable_v<UniquePtr>) { 153 | std::cerr << "unique_ptr should not be copy assignable. Did you remember to delete the copy " 154 | "assignment operator?\n"; 155 | std::exit(1); 156 | } 157 | } 158 | 159 | const std::unordered_map<std::string, std::function<void()>> test_functions = { 160 | {"memory_leak_exit_code", 161 | []() { std::cout << MemoryDiagnostics::MemoryGuard::get_exit_code() << "\n"; }}, 162 | {"destructor", test_destructor}, 163 | {"copy", test_copy<cs106l::unique_ptr<std::string>>}, 164 | {"move_constructor", test_move_constructor}, 165 | {"move_assignment", test_move_assignment}, 166 | {"move_self_assignment", test_move_self_assignment}, 167 | {"linked_list", test_linked_list_example}, 168 | }; 169 | 170 | int run_autograder() { 171 | auto run_program = [](std::string program, std::initializer_list<std::string> args, 172 | bool silent = false) { 173 | std::stringstream ss; 174 | 175 | ss << program; 176 | for (const auto& arg : args) { 177 | ss << ' ' << arg; 178 | } 179 | 180 | if (silent) { 181 | #ifdef _WIN32 182 | ss << " >nul 2>&1"; 183 | #else 184 | ss << " >/dev/null 2>&1"; 185 | #endif 186 | } 187 | 188 | std::cout.flush(); 189 | return system(ss.str().c_str()); 190 | }; 191 | 192 | std::string python; 193 | for (const auto& option : {"python", "python3", "/usr/bin/python3", "/usr/bin/python"}) { 194 | if (run_program(option, {"--version"}, true) == 0) { 195 | python = option; 196 | break; 197 | } 198 | } 199 | 200 | if (python.empty()) { 201 | std::cerr << "Python was not found on your system. Please install Python and " 202 | "try again." 203 | << "\n"; 204 | std::exit(1); 205 | } 206 | 207 | return run_program(python, {"autograder/autograder.py"}); 208 | } 209 | 210 | int main(int argc, char* argv[]) { 211 | if (argc == 2) { 212 | if (test_functions.find(argv[1]) != test_functions.end()) { 213 | test_functions.at(argv[1])(); 214 | return 0; 215 | } 216 | 217 | std::cerr << "Test '" << argv[1] << "' not found." << "\n"; 218 | return 1; 219 | } 220 | 221 | return run_autograder(); 222 | } -------------------------------------------------------------------------------- /assignment7/docs/art.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment7/docs/art.png -------------------------------------------------------------------------------- /assignment7/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CS106L Assignment 7: Unique Pointer 3 | * Created by Jacob Roberts-Baca. 4 | */ 5 | 6 | #include <functional> 7 | #include <iostream> 8 | #include <sstream> 9 | #include <unordered_map> 10 | #include <vector> 11 | 12 | #include "unique_ptr.h" 13 | 14 | /** 15 | * @brief A singly-linked list node that deallocates itself automatically 16 | * by using a `unique_ptr` to manage the pointer to the next node. 17 | * @tparam T The type of the value stored in the node. 18 | * 19 | * @note No modifications are necessary to this struct in order to complete the assignment! 20 | */ 21 | template <typename T> struct ListNode { 22 | 23 | /** @brief The value stored inside this node. */ 24 | T value; 25 | 26 | /** @brief The smart pointer to the next node. May be null. */ 27 | cs106l::unique_ptr<ListNode<T>> next; 28 | 29 | /** 30 | * @brief Constructs a single element linked list, setting `next` to `nullptr`. 31 | * @param value The value to store in the node. 32 | */ 33 | ListNode(T value) : value(value), next(nullptr) { 34 | /* This line is just here for logging purposes so we can see when the 35 | * constructor runs! 36 | */ 37 | std::cout << "Constructing node with value '" << value << "'\n"; 38 | } 39 | 40 | ~ListNode() { 41 | /* This line is just here for logging purposes so we can see when the 42 | * destructor runs! 43 | */ 44 | std::cout << "Destructing node with value '" << value << "'\n"; 45 | } 46 | }; 47 | 48 | /** 49 | * @brief Creates a singly-linked list from a vector of values. 50 | * @param values The values to store in the list. 51 | * @return A `unique_ptr` to the head of the list. 52 | */ 53 | template <typename T> cs106l::unique_ptr<ListNode<T>> create_list(const std::vector<T>& values) { 54 | /* STUDENT TODO: Implement this method */ 55 | throw std::runtime_error("Not implemented: createList"); 56 | } 57 | 58 | /** 59 | * @brief Applies a function to each element in the linked list. 60 | * @tparam T The type of the value stored in the list. 61 | * @tparam Func The type of the function to apply. 62 | * @param head The head of the linked list. 63 | * @paragraph func The function to apply to each element. 64 | */ 65 | template <typename T, typename Func> 66 | void map_list(const cs106l::unique_ptr<ListNode<T>>& head, const Func& func) { 67 | if (!head) 68 | return; 69 | func(head->value); 70 | map_list(head->next, func); 71 | } 72 | 73 | /** 74 | * @brief An example of using a singly-linked list with `unique_ptr`. 75 | */ 76 | void linked_list_example() { 77 | std::vector<std::string> names{"Jacob", "Fabio", "Keith", "Chris", "Sean"}; 78 | auto head = create_list(names); 79 | map_list(head, [](const std::string& name) { std::cout << name << "\n"; }); 80 | } 81 | 82 | #include "autograder/utils.hpp" -------------------------------------------------------------------------------- /assignment7/short_answer.txt: -------------------------------------------------------------------------------- 1 | Before submitting this file, make sure that there are no more TODO 2 | placeholders remaining in the file (and remove this comment too). 3 | 4 | Unique Pointer 5 | -------------- 6 | 7 | Q1. List one or two benefits of using RAII to manage memory instead manually calling `new` and `delete`. 8 | A1. TODO 9 | 10 | Q2. When implementing move semantics for a `unique_ptr`, for example in the move constructor `unique_ptr(unique_ptr&& other)`, it is essential that we set the underlying pointer of the `other` parameter to `nullptr` before exiting the function. Explain in your own words what problem would arise if we did not. 11 | A2. TODO 12 | 13 | Q3. This method of recursive deallocation through RAII works great for small lists, but may pose a problem for longer lists. Why? Hint: what is the limit for how "deep" a recursive function's call stack can grow? 14 | A3. TODO 15 | 16 | Q4. What does `std::move` do in this context? Why is it safe to use `std::move` and move semantics here? 17 | A4. TODO -------------------------------------------------------------------------------- /assignment7/unique_ptr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include <cstddef> 4 | #include <utility> 5 | 6 | namespace cs106l { 7 | 8 | /** 9 | * @brief A smart pointer that owns an object and deletes it when it goes out of scope. 10 | * @tparam T The type of the object to manage. 11 | * @note This class is a simpler version of `std::unique_ptr`. 12 | */ 13 | template <typename T> class unique_ptr { 14 | private: 15 | /* STUDENT TODO: What data must a unique_ptr keep track of? */ 16 | 17 | public: 18 | /** 19 | * @brief Constructs a new `unique_ptr` from the given pointer. 20 | * @param ptr The pointer to manage. 21 | * @note You should avoid using this constructor directly and instead use `make_unique()`. 22 | */ 23 | unique_ptr(T* ptr) { 24 | /* STUDENT TODO: Implement the constructor */ 25 | throw std::runtime_error("Not implemented: unique_ptr(T* ptr)"); 26 | } 27 | 28 | /** 29 | * @brief Constructs a new `unique_ptr` from `nullptr`. 30 | */ 31 | unique_ptr(std::nullptr_t) { 32 | /* STUDENT TODO: Implement the nullptr constructor */ 33 | throw std::runtime_error("Not implemented: unique_ptr(std::nullptr_t)"); 34 | } 35 | 36 | /** 37 | * @brief Constructs an empty `unique_ptr`. 38 | * @note By default, a `unique_ptr` points to `nullptr`. 39 | */ 40 | unique_ptr() : unique_ptr(nullptr) {} 41 | 42 | /** 43 | * @brief Dereferences a `unique_ptr` and returns a reference to the object. 44 | * @return A reference to the object. 45 | */ 46 | T& operator*() { 47 | /* STUDENT TODO: Implement the dereference operator */ 48 | throw std::runtime_error("Not implemented: operator*()"); 49 | } 50 | 51 | /** 52 | * @brief Dereferences a `unique_ptr` and returns a const reference to the object. 53 | * @return A const reference to the object. 54 | */ 55 | const T& operator*() const { 56 | /* STUDENT TODO: Implement the dereference operator (const) */ 57 | throw std::runtime_error("Not implemented: operator*() const"); 58 | } 59 | 60 | /** 61 | * @brief Returns a pointer to the object managed by the `unique_ptr`. 62 | * @note This allows for accessing the members of the managed object through the `->` operator. 63 | * @return A pointer to the object. 64 | */ 65 | T* operator->() { 66 | /* STUDENT TODO: Implement the arrow operator */ 67 | throw std::runtime_error("Not implemented: operator->()"); 68 | } 69 | 70 | /** 71 | * @brief Returns a const pointer to the object managed by the `unique_ptr`. 72 | * @note This allows for accessing the members of the managed object through the `->` operator. 73 | * @return A const pointer to the object. 74 | */ 75 | const T* operator->() const { 76 | /* STUDENT TODO: Implement the arrow operator */ 77 | throw std::runtime_error("Not implemented: operator->() const"); 78 | } 79 | 80 | /** 81 | * @brief Returns whether or not the `unique_ptr` is non-null. 82 | * @note This allows us to use a `unique_ptr` inside an if-statement. 83 | * @return `true` if the `unique_ptr` is non-null, `false` otherwise. 84 | */ 85 | operator bool() const { 86 | /* STUDENT TODO: Implement the boolean conversion operator */ 87 | throw std::runtime_error("Not implemented: operator bool() const"); 88 | } 89 | 90 | /** STUDENT TODO: In the space below, do the following: 91 | * - Implement a destructor 92 | * - Delete the copy constructor 93 | * - Delete the copy assignment operator 94 | * - Implement the move constructor 95 | * - Implement the move assignment operator 96 | */ 97 | }; 98 | 99 | /** 100 | * @brief Creates a new unique_ptr for a type with the given arguments. 101 | * @example auto ptr = make_unique<int>(5); 102 | * @tparam T The type to create a unique_ptr for. 103 | * @tparam Args The types of the arguments to pass to the constructor of T. 104 | * @param args The arguments to pass to the constructor of T. 105 | */ 106 | template <typename T, typename... Args> 107 | unique_ptr<T> make_unique(Args&&... args) { 108 | return unique_ptr<T>(new T(std::forward<Args>(args)...)); 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /docs/vscode-extensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/docs/vscode-extensions.png --------------------------------------------------------------------------------