# PHAS0100 - Week 1 :::info ## :tada: Welcome to the first day! ### Today Today we will - introduce the course - make sure we all can use the tools we need - build a c++ project - run some tests with catch2 šŸž:ok_hand: ::: ### Course and tools #### Tools - [Introduction to Zoom](/stXF4-TuQdqWbTu-vPp2rw) - [Introduction to HackMD](/KHGuLhyARXOAn6MmgjhoUA) - We will also use [GitHub](https://github.com) - create an account now if you've not done it already. - Get [your computer ready](https://moodle.ucl.ac.uk/mod/page/view.php?id=2714313) - We recommend to use [VSCode with Remote-containers](https://code.visualstudio.com/docs/remote/containers-tutorial) - if that fails you can use an Ubuntu virtual machine - Or a [terminal on CoCalc]( https://cocalc.com/doc/terminal.html ). ### Timetable for Monday Group 2 session <details> | Start | End | Topic | | -------- | -------- | -------- | | 10:05 | 10:25 | Lecture part 1: Introduction to PHAS0100 and git | | 10:25 | 10:55 | Breakout/coding session 1 | | 10:55 | 11:05 | Break | --- | | 11:05 | 11:25 | Lecture part 2: Building with CMake | | 11:25 | 11:55 | Breakout/coding session 2 | | 11:55 | 12:05 | Break | --- | | 12:00 | 12:25 | Lecture part 3: Unit testing with Catch2 | | 12:25 | 12:55 | Breakout/coding session 3 | </details> ### Timetable for Friday Group 1 session <details> | Start | End | Topic | | -------- | -------- | -------- | | 14:00 | 14:15 | Lecture part 1: Introduction to PHAS0100 | | 14:25 | 14:55 | Breakout session 1 (computer setup and Git) | | 14:55 | 15:05 | Break | --- | | 15:05 | 15:25 | Lecture part 2: Building with CMake | | 15:25 | 15:55 | Breakout/coding session 2 | | 15:55 | 16:05 | Break | --- | | 16:05 | 16:25 | Lecture part 3: Unit testing with Catch2 | | 16:25 | 16:55 | Breakout/coding session 3 | </details> ## Breakout session 1 ### Class Exercise 0: Computer setup This first breakout/class coding session is a chance for you to check you are setup with your VS Code and docker development environment and ask questions if you are stuck on any points. Don't worry if you haven't worked it out by the end of this session - as a short term solution for the remainder of today you can use a CoCalc web based interactive terminal. ## Breakout session 2: git and cmake ### Class Exercise 1: git refresher Try creating your own repository making use of these commands: ``` git init git add git commit git log git status git remote add git push ``` Register for GitHub if you have not done so already. Create a new empty repository there and push your local one to it. See: ``` git remote add git push ``` If there is time find a public repository on github and: - Find project of interest - try cloning it, make edits, can you push? :::success To get the VS code containers to work with each project you can do: 1. Create a directory where you will keep everything ```bash $ cd ~/Desktop $ mkdir phas0100 $ cd phas0100 ``` 2. Clone the devcontainer and the CMakeHelloWorld repositories ```bash $ git clone https://github.com/UCL/rc-cpp-vscode-dev-container $ git clone https://github.com/UCL/CMakeHelloWorld.git ``` listing that directory should show two directories ```bash $ ls CMakeHelloWorld rc-cpp-vscode-dev-container ``` 3. Copy the `.devcontainer` directory to the repo you are going to work on ```bash $ cp -r rc-cpp-vscode-dev-container/.devcontainer CMakeHelloWorld ``` 4. Open a folder on VS Code: `file` / `Open folder` and browse to choose the `CMakeHelloWorld` directory. 5. VS Code should show a dialog in the bottom left corner asking whether to reopen that directory into the container. ::: #### Solution <details> First try `git config --list` Do you get an output? No? ``` git config --global user.name "Your Name" git config --global user.email "your_email@email.com" git config --global core.editor "nano -w" ``` Then ``` mkdir test_project cd test_project git init . vi README.md # or other text editor git add README.md git commit ``` Create a repository on github and then push your local repo to it: ``` git remote add origin https://github.com/jeydobson/test_project.git git branch -M main git push -u origin main ``` When cloning from a public repo you should not be able to push to it. When forking you should be able to push to your forked project. </details> ### Class Exercise 2: cmake hello world build Clone and build https://github.com/UCL/CMakeHelloWorld.git Ensure you do "out-of-source" builds Use CMake to configure separate Debug and Release versions Add code to `hello.cpp`: On Linux/Mac re-compile just using make #### Solution <details> * An ā€œout-of-sourceā€ build means that the cmake build files, libraries and executable are kept separate from the original source code. This directory can be anywhere but it is often convenient to have it as a subdirectory within the original source code directory * From the command line or the terminal in VS Code you can perform an "out-of-source" build like: ``` $ git clone https://github.com/UCL/CMakeHelloWorld.git $ cd CMakeHelloWorld $ mkdir build $ cd build $ cmake .. # you will see lots of output as cmake creates the build files (Makefile's in this case)_ # always check this step has completed successfully_ $ make # check for any compile errors # and then run the hello executable $ ./build/hello ``` * ā€œOut-of-sourceā€ builds keep the source code separate from any temporary and environment specific files generated by cmake and enable you to build multiple versions of a project side-by-side. For example you could build a Debug and Release version of CMakeHelloWorld using the CMAKE_BUILD_TYPE to set the build type from these options Debug, Release, RelWithDebInfo, MinSizeRel ``` $ mkdir -p builds/Debug # -p option recursively creates both directories $ cd builds/Debug $ cmake -DCMAKE_BUILD_TYPE=Debug ../.. # the ../.. tells cmake to look for CMakeLists.txt two directories back # build files will be generated inside builds/Debug with CMAKE_BUILD_TYPE set to Debug # so that various CMAKE variables such as CMAKE_C_FLAGS_DEBUG will control the build type $ make VERBOSE=1 # running make in verbose mode you will see the actual c++ compile commands now include # the -g option that tells the g++ compiler to generate additional information for use in # a debugger such as gdb ``` * You could do similar for the other build config options listed above, see CMAKE_BUILD_TYPE * After running cmake and compiling with make try making a change to hello.cpp and then re-running make to confirm that the change is picked up so you don’t need to rerun the cmake step if you have simple changes to an existing source code file and want to recompile </details> ### Class Exercise 3: CMake hello world edit Clone and build https://github.com/UCL/CMakeHelloWorld.git Exit all code editors Rename `hello.cpp` Change `CMakeLists.txt` accordingly Notice: The executable name and `.cpp` file name can be different In your build folder, just try rebuilding. You should see that CMake is re-triggered, so you get a cmake/compile cycle. #### Solution <details> * Run the following from the CMakeHelloWorld directory: ``` $ mv hello.cpp bonjour.cpp ``` * Then edit CMakeLists.txt to work with this new file name: change the following line to `add_executable(hello bonjour.cpp)` * You will see that `bonjour.cpp` is now compiled but that the output executable is still named `hello` - this is because we didn't bother updating the target name to `bonjour` in the add_executable CMakeLists.txt line </details> ## Breakout session 3: cmake continued and catch2 unit tests ### Class Exercise 4: CMake with Library and App Clone and build: https://github.com/UCL/CMakeLibraryAndApp Look through `.cpp/.h` code. Ask questions if you don't understand it. - What is an "include guard"? - What is a namespace? - Look at .travis.yml and appveyor.yml - cross platform testing, free for open-source - Look at myApp.cpp, does it make sense? - Look at CMakeLists.txt, does it make sense? #### Solution <details> * Things to consider when looking through the .cpp/.h code: check you understand how #includes are used to let each piece of code know about variables, functions and classes defined in other pieces of code, check all .h files have an include guard, how variables within namespaces are accessed using the :: operator, note where the main function is defined. * The .travis.yml and appveyor.yml files are configuration files telling the continuous integration Travis CI and AppVeyor contonuous integration services how to build and run test on your code - to make use of these you need to fork CMakeLibraryAndApp so you have a copy on Github.com and then link this to Travis CI and AppVeyor (by activating your Github repository via their web interfaces). N.B. this information on continuous integration is included for those that are interested and will not be required in the Assignments. * Include guards, also known as header guards, are used to prevent the definitions included in a header file being included more than once, for example if you have a header file `A.h` that includes `B.h`, and a main function C.cpp that includes both `A.h` and `B.h` then without a header guard in `B.h` it would be included multiple times by the preprocessor causing compilation errors. You can see an example of a header guard in `mpAdding.h` ``` #ifndef mpAdding_h #define mpAdding_h namespace mp { int AddTwoNumbers(int a, int b); } #endif ``` * When including `mpAdding.h` in another file the preprocessor step only includes the body of the header file (the code between the `#ifndef` and the final `#endif`) if the `mpAdding_h` variable is not set (which would be the case for the first include). `mpAdding_h` is then defined using `#define mpAdding_h` so that the next time a piece of code tries to include this the preprocessor does not include the body of the header file. The `mpAdding_h` can be any unique name but by convention and for convenience it is usual to just set it to the name of the file `mpAdding.h` replacing the underscore with a `.` * A namespace is a way of avoiding name conflicts across large code bases with many files. Symbols (variables, functions, classes) defined within a namespace exist in a named scope and are accessed outside of that scope using the :: operator. For example in the following the duplicated definition of `a` would fail compilation without the namespaces: ``` namespace ns1 { int a {2}; } namespace ns2 { int a {-5}; } std::cout << "ns1::a = " << ns1::a << std::endl; std::cout << "ns2::a = " << ns2::a << std::endl; ``` * When looking at the `CMakeLists.txt` file: ``` // Defines the name of the project (by convention, every cmake project should typically have a name) project(CMakeLibraryAndApp) // The following adds a library named MyMaths based on mpAdding.cpp mpMultiplying.cpp add_library(MyMaths mpAdding.cpp mpMultiplying.cpp) // As above but now just one input source file add_library(MyPrinting mpPrinting.cpp) // Link the target MyPrinting with the item MyMaths (as MyPrinting uses MyMaths) // When linking MyPrinting the MyMaths library will be included. The link item // can also be a compiler linking option, a system library target_link_libraries(MyPrinting MyMaths) // Create an executable myApp from myApp.cpp add_executable(myApp myApp.cpp) // The MyMaths is optional here as it is already linked to MyPrinting target_link_libraries(myApp MyPrinting MyMaths) ``` </details> ### Class Exercise 5: CMake with Catch2 unit tests Clone and build https://github.com/UCL/CMakeCatch2.git If open-source, consider using travis and appveyor from the start. Things to note: - See separate Code and Testing folders - Separate Lib and CommandLineApps and 3rdParty Try adding a few simple unit tests following the examples in the lecture slides. Try renaming stuff to create a library of your choice. #### Solution <details> * The main purpose of this exercise is to look through the example and try to understand what each part does * CMakeCatch2 serves as an example you can build upon that demonstrates: * cmake setup for separate directories for library and command line apps * using an external header-only library * running unit tests using catch2 * For completeness it does more things but for the purposes of PHAS0100 these are the most important features to make sure you have understood * The main differences with respect to the CMakeLibraryAndApp.git example are that we now have separate `Code` and `Testing` folder and each one has its own `CMakeLists.txt` file * Within the `Code` folder there are separate subdirectories for external libraries, the user defined library code and the command line app * You should look at the files in each of these and convince yourself that they belong in that subdirectory * Compile and check you can run the command line apps and tests * Check you can see where a new test would be added: both to an exisiting set of tests and as a new set of tests with their own testing executable * The add_subdirectory commands tell cmake to go into these subdirectories and follow the instructions within their `CMakeList.txt` files, these in turn can include other subdirectories and so on * Other new commands to note in this `CMakeCatch2` example are: * A requirement on the minimum version of CMake that can be used: `cmake_minimum_required` -> you could test this out by setting it to a value higher than your version of CMake (run `cmake --version` from the command line to see what version you have) and then trying to run cmake again * Specify the project name and version with `project`(`MYPROJECT VERSION 0.0.0`) * Using the list command to append the CMake folder to the `CMAKE_MODULE_PATH` variable `list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMake)` so that code and macros inside the CMake can be used. See the `include(mitkFunctionCheckCompilerFlags)` which loads the macros to help check compiler flags in `CMake/mitkFunctionCheckCompilerFlags.cmake` * Using the `option` command to toggle on/off the building of Testing code and whether or not shared libraries are generated * Option to set C++ standard with `set(MYPROJECT_CXX_STANDARD 11)` (this needs to be updated to 17) * To create your own library create your own version based on `mpMyFunctions.h` and `mpMyFunctions.cpp` and make sure to add them to the list of .cpp files to be compiled in `Code/Lib/CMakeLists.txt`. You can then call make use of them in your app by simply `#include "yourNewLibrary.h"` the new header file in `Code/CommandLineApps/mpMyFirstApp.cpp` </details> ### Homework Exercise 6: Linking to Continuous Integration on GitHub (optional exercise) _This homework exercise is optional - running CI on GitHub will not be examined, it is included for your interest_ * Clone locally https://github.com/UCL/CMakeCatch2 * Create an empty private repository on GitHub and push your local version of CMakeCatch2 to it (see `git remote add` and `git push` from class/follow instructions on GitHub when you make a new project) * Sign up for a Travis CI account https://www.travis-ci.com/ (if you are using Windows for your development environment you could use [Appveyor](https://www.appveyor.com) as the CI service) * Take a look at the `.travis.yml` build configuration file in CMakeCatch2 and see if you can follow the logic * Try to get this your CMakeCatch2 linked to and running with your Travis CI account (you can use your GitHub account) see https://docs.travis-ci.com/user/tutorial/ #### Solution <details> * Creating a Travis account (as we are developing for our Ubuntu Linux environment) and setting up the CI tests to run is not required for the immediate exercises but it will be useful for later on in the course - a video will be released going through these steps * Follow the tutorial above to get a Travis account and authorise it to access your GitHub projects * Once you have a version of CMakeCatch2 on GitHub you may need to enable travis CI for this particular project * Then make a change, commit it and push to your repo and the travis CI should run automatically * You will need to update the following lines in the `README.md` to point to your travis-ci.com project URLs: ``` [![Build Status](https://travis-ci.com/MattClarkson/CMakeCatch2.svg?branch=master)](https://travis-ci.com/MattClarkson/CMakeCatch2) ``` * The above prints a simple .svg graphic to indicate if the build succeeded or failed </details> ### Homework Exercise 7: Unit tests for an overloaded function * Imagine a simple function, e.g. to add two numbers. * Play with unit tests until you understand the difference between: ``` int AddTwoNumbers(int a, int b); int AddTwoNumbers(const int& a, const int&b); void AddTwoNumbers(int* a, int*b, int* output); void AddTwoNumbers(const int* const a, const int* const b); ``` #### Solution <details> * `int AddTwoNumbers(int a, int b);` * `a` and `b` are passed by value so a copy is made within the `AddTwoNumbers` scope * The result is the returned value of the function * Confirm that the returned number is indeed the sum of the two input numbers * Check special cases such as either `a` or `b` being set to 0 * Confirm that if you alter `a` and `b` from within `AddTwoNumbers` that the variables passed to the function in the calling scope have not changed * `int AddTwoNumbers(const int& a, const int&b);` * Passing values by const reference * The `const` prevents `AddTwoNumbers` modifying the values of the original `a` and `b` * In general this is a good default way to pass values to a function as it is efficient and avoids accidental changes to original variables * The result is the return value of the function * Write unit tests to confirm the above * `void AddTwoNumbers(int* a, int*b, int* output)` * Passes a pointer to memory address of inputs and output * Make sure you dereference the pointer using the `*` operator * What happens if you didn’t do this and instead performed pointer arithmetic * It is possible to change the value that each pointer points to * `void AddTwoNumbers(const int* const a, const int* const b)` * const pointer to const variable, achieves similar to the const reference case * no way to return the sum to calling code * to do this would have to change function return type to an integer </details> ### Homework Exercise 8: Unit tests for a fraction class * Write a simple `Fraction` class * It should have private data members `int m_Numerator` and `int m_Denominator` * And some way to set the values of these: either via the constructor `Fraction::Fraction(int numerator, int denominator)` (preferred) or with setter methods `void Fraction::SetNumerator(int numerator)` and `void Fraction::SetDenominator(int denominator)` * Write a `Print` function to print to screen a nicely formatted fractions * Does the print function live inside or outside of the class? * Write a method `Simplify()` which will simplify the fraction by finding the greatest common denominator of the numerator and denominator * Unit test until you have at least got the hang of unit testing * Implement unit tests that check the behavious of the constructor/setter methods and the Simplify method. Check edge cases * You could use getter methods to retrieve the values of `m_Numerator` and `m_Denominator` #### Solution <details> * The print function should live within the class * See the example [https://www.learncpp.com/cpp-tutorial/93-overloading-the-io-operators/](https://www.learncpp.com/cpp-tutorial/93-overloading-the-io-operators/) for how to implement as a print function and by overload- ing the `<<` operator so that the fraction class can be used with `std::cout` * When implementing the `Simplify()` you need to find the greatest common denominator (gcd) and then divide both by this: you can either implement your own function to find the gcd or you could use the `std::gcd` included in `<algorithm>` * When unit testing make sure to test a range of cases: irreducible fractions (prime/prime), unity fractions (denom = num), fractions where you know the result of a simplify (`num = prime1 * prime0`, `denom = prime2 * prime0` and check result of `Simplify` is `prime1/prime2`) etc </details> # Questions Here you can post any question you have while we are going through this document. A summary of the questions and the answers will be posted in moodle after each session. Please, use a new bullet point for each question and sub-bullet points for their answers. For example writing like this: ``` - Example question - [name=student_a] Example answer - [name=TA_1] Example answer ``` produces the following result: - Example question - [name=student_a] Example answer - [name=TA_1] Example answer Write new questions below :point_down: ###### tags: `phas0100` `teaching` `class`