├── .all-contributorsrc ├── .github ├── config.yml ├── dependabot.yml ├── stale.yml └── workflows │ ├── build-exercises.yml │ ├── build-slides.yml │ ├── check-links.yaml │ └── publish-slides.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CREDITS.md ├── LICENSE ├── README.md ├── codespell.txt ├── docker ├── Dockerfile ├── README.md ├── build.sh ├── name.txt ├── push.sh ├── run.sh ├── run_x11_linux.sh ├── run_x11_macos.sh ├── run_x11_win.sh ├── versions.sh ├── xeyes_linux.sh ├── xeyes_macos.sh └── xeyes_win.sh ├── exercises ├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── ExerciseSchedule_AdvancedCourse.md ├── ExerciseSchedule_EssentialCourse.md ├── ExercisesCheatSheet.md ├── Makefile ├── README.md ├── asan │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── asan.cpp │ └── solution │ │ └── asan.sol.cpp ├── basicTypes │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── PrintHelper.h │ ├── README.md │ ├── basicTypes.cpp │ └── solution │ │ └── basicTypes.sol.cpp ├── callgrind │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── fibocrunch.cpp │ └── solution │ │ └── fibocrunch.sol.cpp ├── check_setup.sh ├── classes │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── classes.cpp │ └── solution │ │ └── classes_sol.cpp ├── common.cmake ├── concepts │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── concepts.cpp │ └── solution │ │ ├── concepts.sol1.cpp │ │ └── concepts.sol2.cpp ├── condition_variable │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── condition_variable.cpp │ └── solution │ │ └── condition_variable.sol.cpp ├── constness │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ └── constplay.cpp ├── control │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── control.cpp │ └── solution │ │ └── control.sol.cpp ├── cppcheck │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── randomize.cpp │ └── solution │ │ └── randomize.sol.cpp ├── debug │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── debug.cpp │ └── solution │ │ └── debug.sol.cpp ├── functions │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── Structs.cpp │ ├── Structs.h │ ├── functions.cpp │ └── solution │ │ └── functions.sol.cpp ├── header_units │ ├── .gitignore │ ├── Complex.hpp │ ├── Makefile │ ├── README.md │ ├── main.cpp │ └── solution │ │ ├── Makefile │ │ └── main.cpp ├── helgrind │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── fiboMT.cpp │ └── solution │ │ └── fiboMT.sol.cpp ├── hello │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── hello.cpp │ ├── hello.hpp │ └── main.cpp ├── loopsRefsAuto │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── loopsRefsAuto.cpp │ └── solution │ │ └── loopsRefsAuto.sol.cpp ├── memcheck │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── Polygons.cpp │ ├── Polygons.hpp │ ├── README.md │ ├── memleak.cpp │ └── solution │ │ ├── Polygons.sol.cpp │ │ ├── Polygons.sol.hpp │ │ └── memleak.sol.cpp ├── modern_oo │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── particles.cpp │ └── solution │ │ └── particles.sol.cpp ├── modules │ ├── .gitignore │ ├── Complex.hpp │ ├── Makefile │ ├── README.md │ ├── main.cpp │ └── solution │ │ ├── Complex.cpp │ │ ├── Makefile │ │ └── main.cpp ├── move │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── solution │ │ └── trymove.sol.cpp │ └── trymove.cpp ├── operators │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── operators.cpp │ └── solution │ │ └── operators_sol.cpp ├── optional │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── optional.cpp │ └── solution │ │ └── optional.sol.cpp ├── polymorphism │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── Polygons.cpp │ ├── Polygons.hpp │ ├── README.md │ ├── solution │ │ └── trypoly.sol.cpp │ └── trypoly.cpp ├── python │ ├── CMakeLists.txt │ ├── Complex.hpp │ ├── Makefile │ ├── README.md │ ├── mandel.cpp │ ├── mandel.hpp │ ├── mandel.py │ ├── mandel_cwrapper.cpp │ ├── mandel_cwrapper.hpp │ ├── mandel_module.cpp │ └── solution │ │ ├── mandel.sol.py │ │ └── mandel.solctype.py ├── race │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── racing.cpp │ ├── run │ └── solution │ │ ├── racing.sol1.cpp │ │ └── racing.sol2.cpp ├── smartPointers │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── problem1.cpp │ ├── problem2.cpp │ ├── problem3.cpp │ ├── problem4.cpp │ ├── problem5.cpp │ └── solution │ │ ├── problem1.sol.cpp │ │ ├── problem2.sol.cpp │ │ ├── problem3.sol.cpp │ │ ├── problem4.sol.cpp │ │ └── problem5.sol.cpp ├── spaceship │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── solution │ │ └── spaceship.sol.cpp │ └── spaceship.cpp ├── stl │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Complex.hpp │ ├── Makefile │ ├── README.md │ ├── randomize.cpp │ ├── randomize.nostl.cpp │ └── solution │ │ └── randomize.sol.cpp ├── templates │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Complex.hpp │ ├── Makefile │ ├── OrderedVector.hpp │ ├── README.md │ ├── playwithsort.cpp │ └── solution │ │ ├── OrderedVector.sol.hpp │ │ └── playwithsort.sol.cpp ├── ubsan │ ├── README.md │ ├── solution │ │ └── undefinedBehaviour.cpp │ └── undefinedBehaviour.cpp ├── valgrind │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── debug.cpp │ └── solution │ │ └── debug.sol.cpp ├── variadic │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── solution │ │ └── variadic.sol.cpp │ └── variadic.cpp ├── variant │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── solution │ │ ├── variant.sol1.cpp │ │ └── variant.sol2.cpp │ └── variant.cpp └── virtual_inheritance │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── TextBox.cpp │ ├── TextBox.hpp │ ├── solution │ ├── TextBox.cpp │ ├── TextBox.hpp │ └── trymultiherit.sol.cpp │ └── trymultiherit.cpp ├── mlc_config.json ├── notes ├── 2016timing ├── 2017timing ├── 2018timing ├── 2019Timing └── 2020Timing └── talk ├── .latexmkrc ├── C++Course.tex ├── CERN-logo.jpg ├── Makefile ├── basicconcepts ├── arrayspointers.tex ├── assert.tex ├── auto.tex ├── classenum.tex ├── control.tex ├── coresyntax.tex ├── functions.tex ├── headersinterfaces.tex ├── inline.tex ├── operators.tex ├── references.tex └── scopesnamespaces.tex ├── concurrency ├── atomic.tex ├── condition.tex ├── mutexes.tex ├── threadlocal.tex └── threadsasync.tex ├── expert ├── coroutines.tex ├── cpp20concepts.tex ├── cpp20spaceship.tex ├── modules.tex ├── perfectforwarding.tex ├── sfinae.tex └── variadictemplate.tex ├── introduction ├── BjarneStroustrup.jpg ├── goals.tex ├── history.tex └── ritchie.jpeg ├── morelanguage ├── AtlasLego.jpg ├── constexpr.tex ├── constness.tex ├── copyelision.tex ├── exceptions.tex ├── initialization.tex ├── lambda.tex ├── morestl.tex ├── move.tex ├── raii.tex ├── random.tex ├── ranges.tex ├── stl.tex └── templates.tex ├── objectorientation ├── adl.tex ├── advancedoo.tex ├── allocations.tex ├── constructors.tex ├── functors.tex ├── inheritance.tex ├── objectsclasses.tex ├── operators.tex ├── static.tex └── typecasting.tex ├── python ├── cppyy.png ├── cppyy.tex ├── cppyy2.png ├── ctypes.tex ├── marryingcandcpp.tex └── modulewriting.tex ├── setup.tex ├── tikz-uml.sty ├── tools ├── compiling.tex ├── cppinsights.png ├── debugging.tex ├── doxygen.tex ├── editors.tex ├── formatting.tex ├── godbolt.png ├── profiling.tex ├── sanitizers.tex ├── staticanalysis.tex ├── valgrind.tex ├── vcs.tex └── webtools.tex ├── vscode-tricks.txt └── youtube.png /.github/config.yml: -------------------------------------------------------------------------------- 1 | # === IMPORTANT: CENTRALLY MAINTAINED FILE === 2 | # This file was automatically added via the scripts at the maintenance repository. 3 | # Please do not change it. Rather, update the blueprint in the maintenance repository and propagate 4 | # the changes from there. 5 | # IF YOU REALLY WANT TO MAKE REPOSITORY SPECIFIC CHANGES TO THIS FILE, PLEASE REMOVE THIS NOTICE! 6 | # Tag @klieret for any questions. 7 | # ====== 8 | 9 | # Configuration for welcome - https://github.com/behaviorbot/welcome 10 | 11 | # Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome 12 | 13 | # Comment to be posted to on first time issues 14 | newIssueWelcomeComment: > 15 | Thanks for opening your first issue here! If you have any questions, feel free 16 | to mention one of the conveners, previous contributors, or attend our weekly meeting (see 17 | https://hepsoftwarefoundation.org/workinggroups/training.html). 18 | Also, sometimes issues go unnoticed, so don't hesitate to @mention some of 19 | us, if we do not come back to you within a few days. 20 | 21 | # Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome 22 | 23 | # Comment to be posted to on PRs from first time contributors in your repository 24 | newPRWelcomeComment: > 25 | Thanks for opening your first pull request here! If you have any questions, feel free 26 | to mention one of the conveners, previous contributors, or attend our weekly meeting (see 27 | https://hepsoftwarefoundation.org/workinggroups/training.html). 28 | Also, sometimes PRs go unnoticed, so don't hesitate to @mention some of 29 | us, if we do not come back to you within a few days. 30 | 31 | # Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge 32 | 33 | # Comment to be posted to on pull requests merged by a first time user 34 | firstPRMergeComment: > 35 | Congrats on merging your first pull request! We at HSF are proud of you! 36 | 37 | # It is recommended to include as many gifs and emojis as possible! 38 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | -------------------------------------------------------------------------------- /.github/workflows/build-slides.yml: -------------------------------------------------------------------------------- 1 | name: Build LaTeX slides 2 | on: 3 | push: 4 | paths: 5 | - 'talk/**' 6 | pull_request: 7 | paths: 8 | - 'talk/**' 9 | - '.github/workflows/**' 10 | 11 | # Cancel running jobs on force-push 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | build_latex: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | version: [ essentials, full ] 22 | steps: 23 | - name: Set up Git repository 24 | uses: actions/checkout@v4 25 | - name: Compile essentials 26 | uses: xu-cheng/latex-action@v3 27 | if: matrix.version == 'essentials' 28 | with: 29 | root_file: C++Course.tex 30 | latexmk_shell_escape: true 31 | latexmk_use_xelatex: true 32 | args: -pdf -interaction=nonstopmode -halt-on-error -usepretex=\def\makebasic{} 33 | working_directory: talk 34 | extra_system_packages: "py-pygments" 35 | - name: Compile full course 36 | uses: xu-cheng/latex-action@v3 37 | if: matrix.version == 'full' 38 | with: 39 | root_file: C++Course.tex 40 | latexmk_shell_escape: true 41 | latexmk_use_xelatex: true 42 | args: -pdf -interaction=nonstopmode -halt-on-error 43 | working_directory: talk 44 | extra_system_packages: "py-pygments" 45 | - name: Upload PDF as artifact 46 | uses: actions/upload-artifact@v4 47 | with: 48 | name: PDF_${{matrix.version}} 49 | path: | 50 | talk/C++Course.pdf 51 | -------------------------------------------------------------------------------- /.github/workflows/check-links.yaml: -------------------------------------------------------------------------------- 1 | name: Check Markdown links 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: "0 0 1 * *" 8 | 9 | jobs: 10 | markdown-link-check: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@master 14 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 15 | -------------------------------------------------------------------------------- /.github/workflows/publish-slides.yml: -------------------------------------------------------------------------------- 1 | name: Publish LaTeX slides 2 | on: 3 | push: 4 | paths: 5 | - 'talk/**' 6 | branches: 7 | - master 8 | 9 | jobs: 10 | publish-slides: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | version: [ essentials, full ] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Compile Essentials document 18 | if: matrix.version == 'essentials' 19 | uses: xu-cheng/latex-action@v3 20 | with: 21 | root_file: C++Course.tex 22 | latexmk_use_xelatex: true 23 | args: -f -pdf -interaction=nonstopmode -shell-escape -usepretex=\def\makebasic{} 24 | working_directory: talk 25 | extra_system_packages: "py-pygments" 26 | - name: Compile Full document 27 | if: matrix.version == 'full' 28 | uses: xu-cheng/latex-action@v3 29 | with: 30 | root_file: C++Course.tex 31 | latexmk_use_xelatex: true 32 | args: -f -pdf -interaction=nonstopmode -shell-escape 33 | working_directory: talk 34 | extra_system_packages: "py-pygments" 35 | - name: Commit to download repository 36 | run: | 37 | set -x 38 | git clone -b download https://${GITHUB_ACTOR}:${{secrets.GITHUB_TOKEN}}@github.com/${{github.repository}}.git download 39 | cd download 40 | mkdir -p talk 41 | cp ../talk/C++Course.pdf "talk/C++Course_${{ matrix.version }}.pdf" 42 | git config --global user.name 'github-actions[bot]' 43 | git config --global user.email 'github-actions[bot]@users.noreply.github.com' 44 | git add -f "talk/C++Course_${{ matrix.version }}.pdf" 45 | if (git diff --quiet) || (git diff --staged --quiet) 46 | then 47 | git commit --amend -m "Update C++Course.pdf" 48 | until git push --force-with-lease origin download; do 49 | git pull --rebase origin download 50 | done 51 | else 52 | echo "No changes to commit" 53 | fi 54 | git push 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # just for the allcontributors cli installation... 2 | package-lock.json 3 | package.json 4 | node_modules/** 5 | 6 | # Temporary latex files: 7 | talk/C++Course.* 8 | talk/_minted 9 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: check-added-large-files 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: detect-private-key 9 | - id: end-of-file-fixer 10 | - id: trailing-whitespace 11 | - repo: https://github.com/codespell-project/codespell 12 | rev: 'v2.4.1' 13 | hooks: 14 | - id: codespell 15 | args: ["-I", "codespell.txt"] 16 | types_or: ["tex", "markdown"] 17 | 18 | exclude: '.*\.sty' 19 | 20 | ci: 21 | autoupdate_schedule: monthly 22 | -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | Credits 2 | ======= 3 | 4 | - Adaptations for CERN sessions (2020, 2025) 5 | * [Sebastien Ponce](https://github.com/sponce) [CERN](http://cern.ch)/[LHCb](http://lhcb.cern.ch) 6 | * [David Chamont](https://gitlab.cern.ch/chamont) [IN2P3](https://informatique.in2p3.fr) 7 | * [Attila Krasznahorkay](https://gitlab.cern.ch/akraszna) [CERN](http://cern.ch)/[Atlas](https://atlas.cern/) 8 | * [Ben Couturier](https://gitlab.cern.ch/bcouturi) [CERN](http://cern.ch)/[LHCb](http://lhcb.cern.ch) 9 | * [Stephan Hageboeck](https://github.com/hageboeck) [CERN](http://cern.ch)/[Atlas](https://atlas.cern/) 10 | * [Bernhard Manfred Gruber](https://gitlab.cern.ch/bgruber) [CERN](http://cern.ch)/EP-SFT 11 | * [Enrico Guiraud](https://github.com/eguiraud) [CERN](http://cern.ch)/EP-SFT 12 | * [Stefan Roiser](https://github.com/roiser) [CERN](http://cern.ch)/IT-SC-RD 13 | * [Abhishek Lekshmananit](https://github.com/theanalyst) [CERN](http://cern.ch)/IT-ST-PDS 14 | 15 | - Original content (2015-2020) 16 | * [Sebastien Ponce](https://github.com/sponce) [CERN](http://cern.ch)/[LHCb](http://lhcb.cern.ch) 17 | -------------------------------------------------------------------------------- /codespell.txt: -------------------------------------------------------------------------------- 1 | hist 2 | heist 3 | Hist 4 | Heist 5 | his 6 | ro 7 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # Version 2, en Python3 3 | 4 | FROM gcc:11.2.0 5 | 6 | # Ensure use of bash 7 | 8 | SHELL ["/bin/bash","-c"] 9 | 10 | # Timezone 11 | 12 | ENV TZ=Europe/Paris 13 | 14 | # Apt-get General Preparation 15 | 16 | RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections 17 | ARG DEBIAN_FRONTEND=noninteractive 18 | RUN apt-get update \ 19 | && apt-get install -y apt-utils 20 | 21 | # C++ Tools 22 | 23 | RUN apt-get install -y build-essential \ 24 | && apt-get install -y cmake \ 25 | && apt-get install -y cppcheck \ 26 | && apt-get install -y libc6-dbg gdb \ 27 | && apt-get install -y binutils graphviz \ 28 | && apt-get install -y valgrind kcachegrind 29 | 30 | # For GDB not to complain 31 | 32 | RUN echo "set auto-load safe-path /" > /root/.gdbinit 33 | 34 | # Python 35 | # It seems I MUST say 3.7 for dev :s 36 | 37 | RUN apt-get install -y python3 \ 38 | && apt-get install -y python3-dev \ 39 | && apt-get install -y python3-pip \ 40 | && apt-get install -y python3-tk 41 | 42 | RUN pip3 install numpy 43 | RUN pip3 install matplotlib 44 | 45 | # Other 46 | 47 | RUN rm -rf /var/lib/apt/lists/* 48 | ENV PATH=${PATH}:. 49 | ENV LD_LIBRARY_PATH=. 50 | 51 | # Start a shell by default 52 | 53 | CMD bash 54 | -------------------------------------------------------------------------------- /docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_NAME=${BASH_SOURCE[0]} 4 | SCRIPT_DIR=`dirname ${SCRIPT_NAME}` 5 | cd ${SCRIPT_DIR} 6 | 7 | docker build -f Dockerfile -t `cat name.txt` . 8 | # --force-rm --no-cache 9 | -------------------------------------------------------------------------------- /docker/name.txt: -------------------------------------------------------------------------------- 1 | cpluspluscourse:v6 2 | -------------------------------------------------------------------------------- /docker/push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_NAME=${BASH_SOURCE[0]} 4 | SCRIPT_DIR=`dirname ${SCRIPT_NAME}` 5 | 6 | docker push `cat ${SCRIPT_DIR}/name.txt` 7 | -------------------------------------------------------------------------------- /docker/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_NAME=${BASH_SOURCE[0]} 4 | SCRIPT_DIR=`dirname ${SCRIPT_NAME}` 5 | 6 | docker run --security-opt seccomp=unconfined -it --rm -v $PWD:/work -w /work `cat ${SCRIPT_DIR}/name.txt` $* 7 | -------------------------------------------------------------------------------- /docker/run_x11_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_NAME=${BASH_SOURCE[0]} 4 | SCRIPT_DIR=`dirname ${SCRIPT_NAME}` 5 | 6 | xhost + 7 | 8 | docker run -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:rw --security-opt seccomp=unconfined -it --rm -v $PWD:/work -w /work `cat ${SCRIPT_DIR}/name.txt` $* 9 | -------------------------------------------------------------------------------- /docker/run_x11_macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_NAME=${BASH_SOURCE[0]} 4 | SCRIPT_DIR=`dirname ${SCRIPT_NAME}` 5 | 6 | export DISPLAY=${MYIP}:0 7 | xhost + ${MYIP} 8 | 9 | docker run -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:rw --security-opt seccomp=unconfined -it --rm -v $PWD:/work -w /work `cat ${SCRIPT_DIR}/name.txt` $* 10 | -------------------------------------------------------------------------------- /docker/run_x11_win.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_NAME=${BASH_SOURCE[0]} 4 | SCRIPT_DIR=`dirname ${SCRIPT_NAME}` 5 | 6 | export DISPLAY=${MYIP}:0 7 | xhost + ${MYIP} 8 | 9 | docker run -e DISPLAY=$DISPLAY --security-opt seccomp=unconfined -it --rm -v $PWD:/work -w /work `cat ${SCRIPT_DIR}/name.txt` $* 10 | -------------------------------------------------------------------------------- /docker/versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo 4 | g++ --version 5 | 6 | echo 7 | make --version 8 | 9 | echo 10 | cppcheck --version 11 | 12 | echo 13 | gdb --version 14 | 15 | echo 16 | valgrind --version 17 | 18 | echo 19 | -------------------------------------------------------------------------------- /docker/xeyes_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | xhost + 4 | 5 | docker run -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:rw gns3/xeyes 6 | -------------------------------------------------------------------------------- /docker/xeyes_macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${MYIP}:0 4 | xhost + ${MYIP} 5 | 6 | docker run -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:rw gns3/xeyes 7 | -------------------------------------------------------------------------------- /docker/xeyes_win.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${MYIP}:0 4 | xhost + ${MYIP} 5 | 6 | docker run -e DISPLAY=$DISPLAY gns3/xeyes 7 | -------------------------------------------------------------------------------- /exercises/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: WebKit 2 | InsertNewlineAtEOF: True 3 | Standard: Latest 4 | -------------------------------------------------------------------------------- /exercises/.gitignore: -------------------------------------------------------------------------------- 1 | # generic files and directories 2 | build 3 | *.a 4 | *.o 5 | *.so 6 | 7 | # tools 8 | CMakeCache.txt 9 | CMakeFiles 10 | cmake_install.cmake 11 | callgrind.out.* 12 | -------------------------------------------------------------------------------- /exercises/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Main project for building all of the exercises at once. 3 | # 4 | 5 | # Set up the project. 6 | cmake_minimum_required( VERSION 3.12 ) 7 | project( cpluspluscourse LANGUAGES CXX ) 8 | 9 | # Make sure that the project is built "out of source". As an "in source" build 10 | # would interfere with the simple Makefiles coming with the code. 11 | if( "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}" ) 12 | message( FATAL_ERROR "The tutorial code must be built out of source!" ) 13 | endif() 14 | 15 | # Include the exercises that (should) work on all platforms. 16 | add_subdirectory( asan ) 17 | add_subdirectory( basicTypes ) 18 | add_subdirectory( callgrind ) 19 | add_subdirectory( condition_variable ) 20 | add_subdirectory( constness ) 21 | add_subdirectory( control ) 22 | add_subdirectory( cppcheck ) 23 | add_subdirectory( debug ) 24 | add_subdirectory( functions ) 25 | add_subdirectory( hello ) 26 | add_subdirectory( loopsRefsAuto ) 27 | add_subdirectory( memcheck ) 28 | add_subdirectory( modern_oo ) 29 | add_subdirectory( move ) 30 | add_subdirectory( operators ) 31 | add_subdirectory( polymorphism ) 32 | add_subdirectory( race ) 33 | add_subdirectory( smartPointers ) 34 | add_subdirectory( stl ) 35 | add_subdirectory( templates ) 36 | add_subdirectory( valgrind ) 37 | add_subdirectory( variadic ) 38 | add_subdirectory( virtual_inheritance ) 39 | 40 | # Include the non-Windows-native exercises. 41 | if( NOT MSVC ) 42 | add_subdirectory( helgrind ) 43 | add_subdirectory( python ) 44 | endif() 45 | -------------------------------------------------------------------------------- /exercises/Makefile: -------------------------------------------------------------------------------- 1 | TESTDIRS = asan callgrind condition_variable control cppcheck debug functions \ 2 | header_units helgrind hello loopsRefsAuto memcheck modern_oo modules \ 3 | move operators polymorphism python race smartPointers templates \ 4 | valgrind virtual_inheritance 5 | NOCOMPILETESTDIRS = basicTypes constness stl variadic 6 | 7 | all: 8 | for dir in ${TESTDIRS}; do \ 9 | cd $${dir}; \ 10 | make $@; \ 11 | cd ..; \ 12 | done 13 | 14 | solution clean: 15 | for dir in ${TESTDIRS} ${NOCOMPILETESTDIRS}; do \ 16 | cd $${dir}; \ 17 | make $@; \ 18 | cd ..; \ 19 | done 20 | 21 | clobber: clean 22 | -------------------------------------------------------------------------------- /exercises/asan/.gitignore: -------------------------------------------------------------------------------- 1 | asan 2 | asan.sol 3 | -------------------------------------------------------------------------------- /exercises/asan/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( asan LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( asan "asan.cpp" ) 10 | 11 | # Create the "solution executable". 12 | add_executable( asan.sol EXCLUDE_FROM_ALL "solution/asan.sol.cpp" ) 13 | add_dependencies( solution asan.sol ) 14 | -------------------------------------------------------------------------------- /exercises/asan/Makefile: -------------------------------------------------------------------------------- 1 | all: asan 2 | solution: asan.sol 3 | 4 | clean: 5 | rm -f *o *so asan *~ asan.sol 6 | 7 | asan : asan.cpp 8 | $(CXX) -std=c++17 -o $@ $^ 9 | 10 | asan.sol : solution/asan.sol.cpp 11 | $(CXX) -std=c++17 -o $@ $^ 12 | -------------------------------------------------------------------------------- /exercises/asan/README.md: -------------------------------------------------------------------------------- 1 | # Using address sanitizer 2 | 3 | Here, we explore address sanitizer (asan). The program `asan.cpp` has two bugs and a memory leak, 4 | which should be relatively easy to find. It might or might not crash when run in its current state. 5 | The goal is to compile the program with and without asan instrumentation and learn to read the very 6 | detailed analysis of the program it generates. 7 | 8 | ## Instructions 9 | There's three tasks listed in the source code. 10 | -------------------------------------------------------------------------------- /exercises/basicTypes/.gitignore: -------------------------------------------------------------------------------- 1 | basicTypes 2 | basicTypes.sol 3 | -------------------------------------------------------------------------------- /exercises/basicTypes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( basicTypes LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | set( CMAKE_CXX_STANDARD 20 ) 8 | 9 | # Create the user's executable. 10 | add_executable( basicTypes PrintHelper.h basicTypes.cpp ) 11 | 12 | # Create the "solution executable". 13 | add_executable( basicTypes.sol EXCLUDE_FROM_ALL PrintHelper.h solution/basicTypes.sol.cpp ) 14 | target_include_directories( basicTypes.sol PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) 15 | add_dependencies( solution basicTypes.sol ) 16 | -------------------------------------------------------------------------------- /exercises/basicTypes/Makefile: -------------------------------------------------------------------------------- 1 | all: basicTypes 2 | solution: basicTypes.sol 3 | 4 | clean: 5 | rm -f *o *so basicTypes *~ basicTypes.sol 6 | 7 | % : %.cpp PrintHelper.h 8 | $(CXX) -g -std=c++20 -Wall -Wextra -o $@ $< 9 | 10 | %.sol : solution/%.sol.cpp PrintHelper.h 11 | $(CXX) -g -std=c++20 -Wall -Wextra -o $@ $< -I . 12 | -------------------------------------------------------------------------------- /exercises/basicTypes/PrintHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* 9 | * NOTE: You don't need to understand the print helpers here. 10 | * Their purpose is to show each expression, the type it evaluates to, and the value it evaluates to. 11 | * Please go back to the main file now. :-) 12 | */ 13 | 14 | #ifdef _MSC_VER 15 | std::string demangle(std::string_view input) { return std::string{input}; } 16 | #else 17 | #include 18 | std::string demangle(std::string_view input) { 19 | int status; 20 | return abi::__cxa_demangle(input.data(), NULL, NULL, &status); 21 | } 22 | #endif 23 | 24 | // This helper prints type and value of an expression 25 | void printWithTypeInfo(std::string expression, auto const & t, bool useBitset = false) { 26 | const auto & ti = typeid(t); 27 | const std::string realname = demangle(ti.name()); 28 | 29 | std::cout << std::left << std::setw(30) << expression << " type=" << std::setw(20) << realname << "value="; 30 | if (useBitset) { 31 | std::cout << "0b" << std::bitset<16>(t) << "\n"; 32 | } else { 33 | std::cout << std::setprecision(25) << t << "\n"; 34 | } 35 | } 36 | 37 | // This macro both prints and evaluates an expression: 38 | #define print(A) printWithTypeInfo("Line " + std::to_string(__LINE__) + ": "#A, A); 39 | #define printBinary(A) printWithTypeInfo("Line " + std::to_string(__LINE__) + ": "#A, A, true); 40 | -------------------------------------------------------------------------------- /exercises/basicTypes/README.md: -------------------------------------------------------------------------------- 1 | # Fundamental types and expressions 2 | 3 | ## Tasks: 4 | - Compile the program and analyse the output of the different expressions 5 | - Discuss with other students or your tutor in case the result of an expression is a surprise 6 | - Fix the marked expressions by changing types such that they produce meaningful results 7 | - Answer the questions in the code 8 | -------------------------------------------------------------------------------- /exercises/callgrind/.gitignore: -------------------------------------------------------------------------------- 1 | fibocrunch 2 | fibocrunch.sol 3 | -------------------------------------------------------------------------------- /exercises/callgrind/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( callgrind LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( fibocrunch "fibocrunch.cpp" ) 10 | 11 | # Create the "solution executable". 12 | add_executable( fibocrunch.sol EXCLUDE_FROM_ALL "solution/fibocrunch.sol.cpp" ) 13 | add_dependencies( solution fibocrunch.sol ) 14 | -------------------------------------------------------------------------------- /exercises/callgrind/Makefile: -------------------------------------------------------------------------------- 1 | all: fibocrunch 2 | solution : fibocrunch.sol 3 | 4 | clean: 5 | rm -f *o fibocrunch *~ fibocrunch.sol fibocrunch.nostl core callgrind.out.* 6 | 7 | fibocrunch : fibocrunch.cpp 8 | ${CXX} -std=c++17 -g -O0 -Wall -Wextra -L. -o $@ $< 9 | 10 | fibocrunch.sol : solution/fibocrunch.sol.cpp 11 | ${CXX} -std=c++17 -Wall -Wextra -L. -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/callgrind/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | * compile, run, it will be slow 5 | * change nb iterations to 20 6 | * run with `valgrind --tool=callgrind` 7 | * look at output with kcachegrind 8 | * change fibo call to fibo2 9 | * observe the change in kcachegrind 10 | -------------------------------------------------------------------------------- /exercises/callgrind/fibocrunch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | constexpr auto NBITERATIONS = 20; 5 | constexpr auto MAX = 40u; 6 | 7 | unsigned int add(unsigned int a, unsigned int b) { 8 | return a + b; 9 | } 10 | 11 | unsigned int mul(unsigned int a, unsigned int b) { 12 | return a * b; 13 | } 14 | 15 | unsigned int power(unsigned int a, unsigned int b) { 16 | unsigned int res = 1; 17 | for (unsigned int i = 0; i < b; i++) res *= a; 18 | return res; 19 | } 20 | 21 | unsigned int fibo(int a) { 22 | if (a == 1 || a == 0) { 23 | return 1; 24 | } else { 25 | return fibo(a-1)+fibo(a-2); 26 | } 27 | } 28 | 29 | unsigned int fibo2(int n) { 30 | return static_cast((1/std::sqrt(5)) * (std::pow(((1 + std::sqrt(5)) / 2), n) - std::pow(((1 - std::sqrt(5)) / 2), n))); 31 | } 32 | 33 | int main() { 34 | std::default_random_engine e; 35 | std::uniform_int_distribution d{0u, MAX}; 36 | for (unsigned int i = 0; i < NBITERATIONS; i++) { 37 | unsigned int a = d(e); 38 | unsigned int b = d(e); 39 | add(a, b); 40 | mul(a, b); 41 | power(a, b); 42 | fibo(a); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /exercises/callgrind/solution/fibocrunch.sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | constexpr auto NBITERATIONS = 20; 5 | constexpr auto MAX = 40u; 6 | 7 | unsigned int add(unsigned int a, unsigned int b) { 8 | return a + b; 9 | } 10 | 11 | unsigned int mul(unsigned int a, unsigned int b) { 12 | return a * b; 13 | } 14 | 15 | unsigned int power(unsigned int a, unsigned int b) { 16 | unsigned int res = 1; 17 | for (unsigned int i = 0; i < b; i++) res *= a; 18 | return res; 19 | } 20 | 21 | unsigned int fibo(int a) { 22 | if (a == 1 || a == 0) { 23 | return 1; 24 | } else { 25 | return fibo(a-1)+fibo(a-2); 26 | } 27 | } 28 | 29 | unsigned int fibo2(int n) { 30 | return static_cast((1/std::sqrt(5)) * (std::pow(((1 + std::sqrt(5)) / 2), n) - std::pow(((1 - std::sqrt(5)) / 2), n))); 31 | } 32 | 33 | int main() { 34 | std::default_random_engine e; 35 | std::uniform_int_distribution d{0u, MAX}; 36 | for (unsigned int i = 0; i < NBITERATIONS; i++) { 37 | unsigned int a = d(e); 38 | unsigned int b = d(e); 39 | add(a, b); 40 | mul(a, b); 41 | power(a, b); 42 | fibo2(a); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /exercises/check_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | RED='\033[0;31m' 4 | YEL='\033[0;33m' 5 | NORMAL='\033[0m' 6 | COL=12 7 | 8 | exit_status=0 9 | 10 | # from: https://stackoverflow.com/a/4024263 11 | function version_less() { 12 | [ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ] 13 | } 14 | 15 | function check_tool() { 16 | min_version=$2 || "0" 17 | required=$3 || true 18 | if [ -x "$(command -v $1)" ] 19 | then 20 | version=$($1 --version | grep -o -E "[0-9]+.[0-9]+(.[0-9]+)?" | head -1) 21 | if version_less $version $min_version 22 | then 23 | printf "%-${COL}s found, ${RED}version: ${version}, need at least: ${min_version}${NORMAL}\n" $1 24 | else 25 | printf "%-${COL}s found, version: ${version}\n" $1 26 | return 0 27 | fi 28 | elif $required 29 | then 30 | printf "${RED}%-${COL}s not found, but required${NORMAL}\n" $1 31 | exit_status=1 32 | else 33 | printf "${YEL}%-${COL}s not found, but optional${NORMAL}\n" $1 34 | fi 35 | return 1 36 | } 37 | 38 | # compiler 39 | check_tool g++ 10.0.0 false || check_tool clang++ 11.0.0 false || { echo -e "${RED}No supported compiler found${NORMAL}"; exit_status=1; } # clang does not work for header_units exercise 40 | 41 | # build tools 42 | check_tool make 43 | check_tool cmake 3.12.0 44 | check_tool ccmake 3.12.0 false 45 | 46 | # debugger 47 | check_tool gdb 10.0.0 false || check_tool lldb 11.0.0 false || { echo -e "${RED}No supported debugger found${NORMAL}"; exit_status=1; } 48 | 49 | # utils 50 | check_tool nm 51 | check_tool ldd 0.0 false || check_tool otool 0.0 false || { echo -e "${RED}Missing ldd or otool${NORMAL}"; exit_status=1; } 52 | 53 | # tools 54 | check_tool valgrind 55 | check_tool kcachegrind 56 | check_tool cppcheck 57 | check_tool clang-format 58 | check_tool clang-tidy 59 | check_tool python3 60 | 61 | exit $exit_status 62 | -------------------------------------------------------------------------------- /exercises/classes/.gitignore: -------------------------------------------------------------------------------- 1 | classes 2 | classes_sol 3 | -------------------------------------------------------------------------------- /exercises/classes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( operators LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( classes "classes.cpp" ) 10 | 11 | # Create the "solution executable". 12 | add_executable( classes_sol EXCLUDE_FROM_ALL "solution/classes_sol.cpp" ) 13 | add_dependencies( solution classes_sol ) 14 | -------------------------------------------------------------------------------- /exercises/classes/Makefile: -------------------------------------------------------------------------------- 1 | all: classes 2 | solution: classes_sol 3 | 4 | clean: 5 | rm -f *o classes *~ classes_sol 6 | 7 | classes : classes.cpp 8 | ${CXX} -g -std=c++17 -O0 -Wall -Wextra -L. -o $@ $< 9 | 10 | classes_sol : solution/classes_sol.cpp 11 | ${CXX} -g -std=c++17 -O0 -Wall -Wextra -L. -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/classes/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | STEP 1 5 | - Complete the class Fraction so that a Fraction can be construct from one or two integer. 6 | Check the first two lines of main() are working (comment out the rest) 7 | - Add the function equal(). 8 | Check the second section of main() works. 9 | - Add the function multiply(). 10 | Check the whole main() works. 11 | 12 | STEP 2 13 | - Replace the function printTestResult() by a class TestResultPrinter 14 | with a method process() that take the same arguments as before. 15 | Upgrade CHECK() and main(). 16 | - Transform the WIDTH constant into a variable member of TestResultPrinter, 17 | which is initialized in its constructor. 18 | Upgrade main(). 19 | 20 | OPTIONAL STEP 3 21 | - Move multiply() and compare() as friend functions within the class Fraction. 22 | Check main() works. 23 | - Remove the accessors numerator() and denominator(). 24 | Check main() works. 25 | 26 | OPTIONAL STEP 4 27 | - Remove the systematic call to normalize(). 28 | - Add a equivalent() method to Fraction. 29 | - Upgrade the tests accordingly. 30 | - Transform the private normalize() into a public const normalized() method 31 | which return the normalized fraction. 32 | - Add some tests to check normalized(). 33 | -------------------------------------------------------------------------------- /exercises/classes/classes.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | class Fraction { 7 | 8 | public: 9 | 10 | // ADD YOUR CODE HERE 11 | 12 | std::string str() const { 13 | std::ostringstream oss; 14 | oss << m_num << '/' << m_denom; 15 | return oss.str(); 16 | } 17 | 18 | int numerator() const { 19 | return m_num; 20 | } 21 | int denominator() const { 22 | return m_denom; 23 | } 24 | 25 | private: 26 | 27 | void normalize() { 28 | const int gcd = std::gcd(m_num, m_denom); 29 | m_num /= gcd; 30 | m_denom /= gcd; 31 | } 32 | 33 | int m_num, m_denom; 34 | }; 35 | 36 | // ADD YOUR CODE HERE 37 | 38 | // This is using the cpp, the C preprocessor to expand a bit of code 39 | // (the what argument) to a pair containing a string representation 40 | // of it and the code itself. That way, print is given a string and a 41 | // value where the string is the code that lead to the value 42 | #define CHECK(print,what) print(#what, what) 43 | 44 | unsigned int WIDTH {20}; 45 | 46 | void printTestResult(std::string const & what, bool passed) { 47 | std::cout << std::setw(WIDTH) << what << ": " << (passed ? "PASS" : "** FAIL **") << '\n'; 48 | } 49 | 50 | int main() { 51 | 52 | // create a fraction with values 3 (which is 3/1) and 1/3 53 | std::cout< 2 | #include 3 | #include 4 | 5 | /* 6 | 7 | In the code below, we provide two implementations of the function increment(). 8 | The commented one is optimized for iterators which fulfill the requirements of 9 | the concept RandomAccessIterator. 10 | 11 | 1. Uncomment and complete the concept RandomAccessIterator: check that the 12 | class IterT has operator +=. 13 | 2. Find an existing concept in the standard library, which can replace the 14 | previous user-defined concept. 15 | 16 | */ 17 | 18 | /* 19 | 20 | template 21 | concept RandomAccessIterator = requires(... TO BE COMPLETED...) { 22 | ... TO BE COMPLETED... 23 | }; 24 | 25 | template 26 | void increment(IterT &iter, unsigned d) { 27 | iter += d; 28 | std::cout << "(random)\n"; 29 | } 30 | 31 | */ 32 | 33 | template 34 | void increment(IterT &iter, unsigned d) { 35 | while (d--) 36 | ++iter; 37 | std::cout << "(other)\n"; 38 | } 39 | 40 | int main() { 41 | std::list l_data = {1, 2, 3, 4, 5}; 42 | auto l_itr = l_data.begin(); 43 | increment(l_itr, 2); 44 | 45 | std::vector v_data = {1, 2, 3, 4, 5}; 46 | auto v_itr = v_data.begin(); 47 | increment(v_itr, 2); 48 | } 49 | -------------------------------------------------------------------------------- /exercises/concepts/solution/concepts.sol1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template 6 | concept RandomAccessIterator = requires(IterT itr, unsigned d) { 7 | itr += d; 8 | }; 9 | 10 | template 11 | void increment(IterT &iter, unsigned d) { 12 | iter += d; 13 | std::cout << "(random)" << std::endl; 14 | } 15 | 16 | template 17 | void increment(IterT &iter, unsigned d) { 18 | while (d--) 19 | ++iter; 20 | std::cout << "(other)" << std::endl; 21 | } 22 | 23 | int main() { 24 | 25 | std::list l_data = {1, 2, 3, 4, 5}; 26 | auto l_itr = begin(l_data); 27 | increment(l_itr, 2); 28 | 29 | std::vector v_data = {1, 2, 3, 4, 5}; 30 | auto v_itr = begin(v_data); 31 | increment(v_itr, 2); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /exercises/concepts/solution/concepts.sol2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | template 7 | void increment(IterT &iter, unsigned d) { 8 | iter += d; 9 | std::cout << "(random)" << std::endl; 10 | } 11 | 12 | template 13 | void increment(IterT &iter, unsigned d) { 14 | while (d--) 15 | ++iter; 16 | std::cout << "(other)" << std::endl; 17 | } 18 | 19 | // Main 20 | 21 | int main() { 22 | 23 | std::list l_data = {1, 2, 3, 4, 5}; 24 | auto l_itr = begin(l_data); 25 | increment(l_itr, 2); 26 | 27 | std::vector v_data = {1, 2, 3, 4, 5}; 28 | auto v_itr = begin(v_data); 29 | increment(v_itr, 2); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /exercises/condition_variable/.gitignore: -------------------------------------------------------------------------------- 1 | condition_variable 2 | condition_variable.sol 3 | -------------------------------------------------------------------------------- /exercises/condition_variable/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( condition_variable LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Figure out how to use the platform's thread capabilities. 9 | find_package( Threads REQUIRED ) 10 | 11 | # Create the user's executable. 12 | add_executable( condition_variable "condition_variable.cpp" ) 13 | target_link_libraries( condition_variable PRIVATE Threads::Threads ) 14 | 15 | # Create the "solution executable". 16 | add_executable( condition_variable.sol EXCLUDE_FROM_ALL "solution/condition_variable.sol.cpp" ) 17 | target_link_libraries( condition_variable.sol PRIVATE Threads::Threads ) 18 | add_dependencies( solution condition_variable.sol ) 19 | -------------------------------------------------------------------------------- /exercises/condition_variable/Makefile: -------------------------------------------------------------------------------- 1 | all: condition_variable 2 | solution: condition_variable.sol 3 | 4 | clean: 5 | rm -f *o condition_variable *~ core condition_variable.sol 6 | 7 | % : %.cpp 8 | ${CXX} -g -std=c++17 -O0 -pthread -Wall -Wextra -L. -o $@ $< 9 | 10 | condition_variable.sol : solution/condition_variable.sol.cpp 11 | ${CXX} -g -std=c++17 -O0 -pthread -Wall -Wextra -L. -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/constness/.gitignore: -------------------------------------------------------------------------------- 1 | constplay 2 | -------------------------------------------------------------------------------- /exercises/constness/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( constness LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( constplay "constplay.cpp" ) 10 | -------------------------------------------------------------------------------- /exercises/constness/Makefile: -------------------------------------------------------------------------------- 1 | all: constplay 2 | 3 | clean: 4 | rm -f *o constplay *~ constplay.sol 5 | 6 | constplay : constplay.cpp 7 | ${CXX} -std=c++17 -Wall -Wextra -L. -o $@ $< 8 | -------------------------------------------------------------------------------- /exercises/constness/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | * open `constplay.cpp` 5 | * try to find out which lines inside `main` will be problematic and comment them out 6 | * try to compile and check your findings 7 | -------------------------------------------------------------------------------- /exercises/constness/constplay.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* This is a dummy function to demonstrate pass by value. 5 | * Since it doesn't do anything with the argument, we suppress 6 | * possible compiler warnings using `maybe_unused`. 7 | */ 8 | void copy(int a) { 9 | [[maybe_unused]] int val = a; 10 | } 11 | 12 | void copyConst(const int a) { 13 | [[maybe_unused]] int val = a; 14 | } 15 | 16 | void write(int* a) { 17 | *a = 42; 18 | } 19 | void write(int& a) { 20 | a = 42; 21 | } 22 | 23 | void read(const int* a) { 24 | [[maybe_unused]] int val = *a; 25 | } 26 | void read(int const & a) { 27 | [[maybe_unused]] int val = a = 2; 28 | } 29 | 30 | struct Test { 31 | void hello(std::string &s) { 32 | std::cout << "Hello " << s << '\n'; 33 | } 34 | void helloConst(std::string &s) const { 35 | std::cout << "Hello " << s << '\n'; 36 | } 37 | }; 38 | 39 | int main() { 40 | // try pointer to constant 41 | int a = 1, b = 2; 42 | int const *i = &a; 43 | *i = 5; 44 | i = &b; 45 | 46 | // try constant pointer 47 | int * const j = &a; 48 | *j = 5; 49 | j = &b; 50 | 51 | // try constant pointer to constant 52 | int const * const k = &a; 53 | *k = 5; 54 | k = &b; 55 | 56 | // try constant arguments of functions 57 | int l = 0; 58 | const int m = 0; 59 | copy(l); 60 | copy(m); 61 | copyConst(l); 62 | copyConst(m); 63 | 64 | // try constant arguments of functions with pointers 65 | { 66 | int *p = &a; 67 | const int *r = &b; 68 | write(p); 69 | write(r); 70 | read(p); 71 | read(r); 72 | } 73 | 74 | // try constant arguments of functions with references 75 | { 76 | int p = 0; 77 | const int r = 0; 78 | write(2); 79 | write(r); 80 | read(2); 81 | read(r); 82 | } 83 | 84 | // try constant method in a class 85 | Test t; 86 | const Test tc; 87 | std::string s("World"); 88 | t.hello(s); 89 | tc.hello(s); 90 | t.helloConst(s); 91 | tc.helloConst(s); 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /exercises/control/.gitignore: -------------------------------------------------------------------------------- 1 | control 2 | control.sol 3 | -------------------------------------------------------------------------------- /exercises/control/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( control LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( control "control.cpp" ) 10 | 11 | # Create the "solution executable". 12 | add_executable( control.sol EXCLUDE_FROM_ALL "solution/control.sol.cpp" ) 13 | add_dependencies( solution control.sol ) 14 | -------------------------------------------------------------------------------- /exercises/control/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: control 3 | solution: control.sol 4 | 5 | clean: 6 | rm -f *o *~ control control.sol 7 | 8 | control : control.cpp 9 | $(CXX) --std=c++17 -g -Wall -Wextra -o $@ $^ 10 | 11 | control.sol: solution/control.sol.cpp 12 | $(CXX) --std=c++17 -g -Wall -Wextra -o $@ $^ 13 | -------------------------------------------------------------------------------- /exercises/control/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | ## Part 1 4 | 5 | * Replace the C++98-style for loop with a range-based loop 6 | * Replace the if / else statement with a conditional operator 7 | 8 | ## Part 2 9 | 10 | * Replace the for loop with a while loop 11 | * Try it with a do / while loop 12 | 13 | ## Part 3 14 | 15 | * Replace the if / else statement with a switch statement 16 | -------------------------------------------------------------------------------- /exercises/control/control.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const unsigned int numbers[]{1000001, 1000002, 1000003, 1000004, 1000005, 1000006, 1000007, 1000008, 1000009}; 4 | 5 | bool isodd(unsigned int i) { return i % 2 == 1; } 6 | 7 | void part1() { 8 | unsigned int sum_odd = 0; 9 | unsigned int sum_eve = 0; 10 | for (int i = 0; i < 9; ++i) { 11 | unsigned int num = numbers[i]; 12 | if (isodd(num)) { 13 | sum_odd += num; 14 | } else { 15 | sum_eve += num; 16 | } 17 | } 18 | std::cout << "Sums: odd = " << sum_odd << ", even = " << sum_eve << "\n"; 19 | } 20 | 21 | void part2() { 22 | // print smallest n for which 1 + 2 + ... + n > 10000 23 | int sum = 0; 24 | for (int i = 1; ; i++) { 25 | sum += i; 26 | if (sum > 10000) { 27 | std::cout << i << "\n"; 28 | break; 29 | } 30 | } 31 | } 32 | 33 | enum class Language { English, French, German, Italian, Other }; 34 | 35 | void part3(Language l) { 36 | if (l == Language::English) { 37 | std::cout << "Hello\n"; 38 | } else if (l == Language::French) { 39 | std::cout << "Salut\n"; 40 | } else if (l == Language::German) { 41 | std::cout << "Hallo\n"; 42 | } else if (l == Language::Italian) { 43 | std::cout << "Ciao\n"; 44 | } else { 45 | std::cout << "I don't speak your language\n"; 46 | } 47 | } 48 | 49 | int main() { 50 | part1(); 51 | part2(); 52 | part3(Language::English); 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /exercises/control/solution/control.sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const unsigned int numbers[]{1000001, 1000002, 1000003, 1000004, 1000005, 1000006, 1000007, 1000008, 1000009}; 4 | 5 | bool isodd(unsigned int i) { return i % 2 == 1; } 6 | 7 | void part1() { 8 | unsigned int sum_odd = 0; 9 | unsigned int sum_eve = 0; 10 | for (unsigned int num : numbers) { 11 | // Note the usage of ternary expression to select the sum to which we add 12 | // the ternary expression returns a reference to the right sum 13 | (isodd(num) ? sum_odd : sum_eve) += num; 14 | } 15 | std::cout << "Sums: odd = " << sum_odd << ", even = " << sum_eve << "\n"; 16 | } 17 | 18 | void part2() { 19 | // print smallest n for which 1 + 2 + ... + n > 10000 20 | int sum = 0; 21 | int i = 0; 22 | while (sum <= 10000) { 23 | ++i; 24 | sum += i; 25 | } 26 | std::cout << i << "\n"; 27 | } 28 | 29 | void part2bis() { 30 | // print smallest n for which 1 + 2 + ... + n > 10000 31 | int sum = 0; 32 | int i = 0; 33 | do { 34 | ++i; 35 | sum += i; 36 | } while (sum <= 10000); 37 | std::cout << i << "\n"; 38 | } 39 | 40 | enum class Language { English, French, German, Italian, Other }; 41 | 42 | void part3(Language l) { 43 | switch (l) { 44 | case Language::English: 45 | std::cout << "Hello\n"; 46 | break; 47 | case Language::French: 48 | std::cout << "Salut\n"; 49 | break; 50 | case Language::German: 51 | std::cout << "Hallo\n"; 52 | break; 53 | case Language::Italian: 54 | std::cout << "Ciao\n"; 55 | break; 56 | default: 57 | std::cout << "Don't speak your language\n"; 58 | } 59 | } 60 | 61 | int main() { 62 | part1(); 63 | part2(); 64 | part2bis(); 65 | part3(Language::English); 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /exercises/cppcheck/.gitignore: -------------------------------------------------------------------------------- 1 | randomize 2 | randomize.sol 3 | -------------------------------------------------------------------------------- /exercises/cppcheck/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( cppcheck LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( cppcheck_randomize "randomize.cpp" ) 10 | 11 | # Create the "solution executable". 12 | add_executable( cppcheck_randomize.sol EXCLUDE_FROM_ALL "solution/randomize.sol.cpp" ) 13 | add_dependencies( solution cppcheck_randomize.sol ) 14 | -------------------------------------------------------------------------------- /exercises/cppcheck/Makefile: -------------------------------------------------------------------------------- 1 | all: randomize 2 | solution: randomize.sol 3 | 4 | clean: 5 | rm -f *o randomize *~ randomize.sol 6 | 7 | randomize : randomize.cpp 8 | ${CXX} -std=c++17 -g -O0 -Wall -Wextra -L. -o $@ $< 9 | 10 | randomize.sol : solution/randomize.sol.cpp 11 | ${CXX} -std=c++17 -g -O0 -Wall -Wextra -L. -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/cppcheck/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | * compile, run, see that it works 5 | * use valgrind : no issue 6 | * use cppcheck, see the problem 7 | * analyze the issue, and fix it 8 | * bonus : understand why valgrind did not complain and how the standard deviation could be biased ( hint : use gdb and check addresses of v and diffs). 9 | -------------------------------------------------------------------------------- /exercises/cppcheck/randomize.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | constexpr auto LEN = 1000; 6 | constexpr auto STEP = 10; 7 | 8 | void swap(int *a, int*b) { 9 | int c = *a; 10 | *a = *b; 11 | *b = c; 12 | } 13 | 14 | void randomize(int* v, unsigned int len) { 15 | std::default_random_engine e; 16 | std::uniform_int_distribution d{0u, len}; 17 | // we randomize via len random inversions 18 | for (unsigned int i = 0; i < len; i++) { 19 | int a = d(e); 20 | int b = d(e); 21 | swap(v+a, v+b); 22 | } 23 | } 24 | 25 | void fillVector(int* v, unsigned int len) { 26 | for (unsigned int i = 0; i < len; i++) v[i] = i*STEP; 27 | } 28 | 29 | int main() { 30 | int v[LEN-1]; 31 | // create and randomize vector 32 | fillVector(v, LEN+1); 33 | randomize(v, LEN+1); 34 | 35 | // compute diffs 36 | int diffs[LEN]; 37 | for (unsigned int i = 0; i < LEN; i++) 38 | diffs[i] = v[i+1] - v[i]; 39 | 40 | // compute standard deviation of it 41 | float sum = 0; 42 | float sumsq = 0; 43 | for (unsigned int i = 0; i < LEN; i ++) { 44 | sum += diffs[i]; 45 | sumsq += diffs[i]*diffs[i]; 46 | } 47 | float mean = sum/LEN; 48 | float stddev = std::sqrt(sumsq/LEN - mean*mean) ; 49 | std::cout << "Range = [0, " << STEP*LEN << "]\n" 50 | << "Mean = " << mean 51 | << "\nStdDev = " << stddev << '\n'; 52 | } 53 | -------------------------------------------------------------------------------- /exercises/cppcheck/solution/randomize.sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | constexpr auto LEN = 1000; 6 | constexpr auto STEP = 10; 7 | 8 | void swap(int *a, int*b) { 9 | int c = *a; 10 | *a = *b; 11 | *b = c; 12 | } 13 | 14 | void randomize(int* v, unsigned int len) { 15 | std::default_random_engine e; 16 | std::uniform_int_distribution d{0u, len}; 17 | // we randomize via len random inversions 18 | for (unsigned int i = 0; i < len; i++) { 19 | int a = d(e); 20 | int b = d(e); 21 | swap(v+a, v+b); 22 | } 23 | } 24 | 25 | void fillVector(int* v, unsigned int len) { 26 | for (unsigned int i = 0; i < len; i++) v[i] = i*STEP; 27 | } 28 | 29 | int main() { 30 | int v[LEN+1]; 31 | // create and randomize vector 32 | fillVector(v, LEN+1); 33 | randomize(v, LEN+1); 34 | 35 | // compute diffs 36 | int diffs[LEN]; 37 | for (unsigned int i = 0; i < LEN; i++) 38 | diffs[i] = v[i+1] - v[i]; 39 | 40 | // compute standard deviation of it 41 | float sum = 0; 42 | float sumsq = 0; 43 | for (unsigned int i = 0; i < LEN; i ++) { 44 | sum += diffs[i]; 45 | sumsq += diffs[i]*diffs[i]; 46 | } 47 | float mean = sum/LEN; 48 | float stddev = std::sqrt(sumsq/LEN - mean*mean) ; 49 | std::cout << "Range = [0, " << STEP*LEN << "]\n" 50 | << "Mean = " << mean 51 | << "\nStdDev = " << stddev << '\n'; 52 | } 53 | -------------------------------------------------------------------------------- /exercises/debug/.gitignore: -------------------------------------------------------------------------------- 1 | debug 2 | debug.sol 3 | -------------------------------------------------------------------------------- /exercises/debug/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( debug LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( debug "debug.cpp" ) 10 | 11 | # Create the "solution executable". 12 | add_executable( debug.sol EXCLUDE_FROM_ALL "solution/debug.sol.cpp" ) 13 | add_dependencies( solution debug.sol ) 14 | -------------------------------------------------------------------------------- /exercises/debug/Makefile: -------------------------------------------------------------------------------- 1 | all: debug 2 | solution: debug.sol 3 | 4 | clean: 5 | rm -f *o debug *~ debug.sol core 6 | 7 | debug : debug.cpp 8 | ${CXX} -std=c++17 -g -O0 -Wall -Wextra -L. -o $@ $< 9 | 10 | debug.sol : solution/debug.sol.cpp 11 | ${CXX} -std=c++17 -g -O0 -Wall -Wextra -L. -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/debug/README.md: -------------------------------------------------------------------------------- 1 | ## Instructions for the "debug" exercise 2 | 3 | * compile, run, see the crash 4 | * run it in gdb 5 | * inspect backtrace, variables to understand the problem 6 | * try stepping through the code using 'step' and 'next', use breakpoints and continue to get familiar with gdb 7 | * fix the bug 8 | 9 | ### Some useful gdb commands 10 | 11 | | Command | Effect | 12 | |---------------------------|---------------------------------------------------------------| 13 | | `gdb ./randomize` | launch the debugger with the given executable | 14 | | `gdb --tui ./randomize` | same as above, but with terminal user interface enabled | 15 | | `tui enable` | enabled the terminal user interface | 16 | | `run` | runs the program | 17 | | `bt, backtrace` | show a backtrace (list of stack frames) of the current thread | 18 | | `up` | navigate one stack frame up | 19 | | `down` | navigate one stack frame down | 20 | | `s, step` | execute current line, stop at earliest next occasion | 21 | | `n, next` | continue until next line or function exits | 22 | | `c, continue` | continue execution | 23 | | `print ` | show current value of variable | 24 | | `info locals` | show the values of all local variables | 25 | | `info args` | show the values of all function arguments | 26 | | `br ` | put a breakpoint at line | 27 | | `info br`: | list active breakpoints | 28 | | `enable/disable/delete #` | enable/disable/delete breakpoints | 29 | -------------------------------------------------------------------------------- /exercises/debug/debug.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void swap(int* a, int* b) 5 | { 6 | int c = *a; 7 | *a = *b; 8 | *b = c; 9 | } 10 | 11 | void reverse(int* v, unsigned int len) 12 | { 13 | for (unsigned int i = 0; i < (len + 1) / 2; i++) { 14 | const int a = i; 15 | const int b = len - 1 - i; 16 | 17 | swap(v + a, v + b); 18 | } 19 | } 20 | 21 | int* createAndFillVector(unsigned int len) 22 | { 23 | auto v = new int[len]; 24 | for (unsigned int i = 0; i < len; i++) { 25 | v[i] = i; 26 | } 27 | return v; 28 | } 29 | 30 | int main() 31 | { 32 | constexpr auto arraySize = 100; 33 | int* v = nullptr; 34 | // create and reverse the vector of LEN numbers 35 | reverse(v, 1000); 36 | v = createAndFillVector(arraySize); 37 | 38 | // check if the revert worked: 39 | const bool isReversed = std::is_sorted(v, v + arraySize, std::greater {}); 40 | std::cout << "Vector reversed successfully: " << std::boolalpha 41 | << isReversed << "\n"; 42 | 43 | return isReversed ? 0 : 1; 44 | } 45 | -------------------------------------------------------------------------------- /exercises/debug/solution/debug.sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void swap(int* a, int* b) 5 | { 6 | int c = *a; 7 | *a = *b; 8 | *b = c; 9 | } 10 | 11 | void reverse(int* v, unsigned int len) 12 | { 13 | for (unsigned int i = 0; i < (len + 1) / 2; i++) { 14 | const int a = i; 15 | const int b = len - 1 - i; 16 | 17 | swap(v + a, v + b); 18 | } 19 | } 20 | 21 | int* createAndFillVector(unsigned int len) 22 | { 23 | auto v = new int[len]; 24 | for (unsigned int i = 0; i < len; i++) { 25 | v[i] = i; 26 | } 27 | return v; 28 | } 29 | 30 | int main() 31 | { 32 | constexpr auto arraySize = 100; 33 | int* v = nullptr; 34 | // create and reverse the vector of LEN numbers 35 | v = createAndFillVector(arraySize); 36 | reverse(v, arraySize); 37 | 38 | // check if the revert worked: 39 | const bool isReversed = std::is_sorted(v, v + arraySize, std::greater {}); 40 | std::cout << "Vector reversed successfully: " << std::boolalpha 41 | << isReversed << "\n"; 42 | 43 | delete[] v; 44 | 45 | return isReversed ? 0 : 1; 46 | } 47 | -------------------------------------------------------------------------------- /exercises/functions/.gitignore: -------------------------------------------------------------------------------- 1 | functions 2 | functions.sol 3 | -------------------------------------------------------------------------------- /exercises/functions/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( functions LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( functions "Structs.h" "Structs.cpp" "functions.cpp" ) 10 | 11 | # Create the "solution executable". 12 | add_executable( functions.sol EXCLUDE_FROM_ALL "Structs.h" "Structs.cpp" "solution/functions.sol.cpp" ) 13 | target_include_directories(functions.sol PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") 14 | add_dependencies( solution functions.sol ) 15 | 16 | if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" ) 17 | target_compile_definitions( functions PRIVATE _CRT_SECURE_NO_WARNINGS ) 18 | target_compile_definitions( functions.sol PRIVATE _CRT_SECURE_NO_WARNINGS ) 19 | endif() 20 | -------------------------------------------------------------------------------- /exercises/functions/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: functions 3 | solution: functions.sol 4 | 5 | clean: 6 | rm -f *o *~ functions functions.sol 7 | 8 | %.o: %.cpp %.h 9 | ${CXX} -std=c++17 -Wall -Wextra -c -o $@ $< 10 | 11 | functions : functions.cpp Structs.o 12 | ${CXX} -std=c++17 -Wall -Wextra -o $@ $^ 13 | 14 | functions.sol : solution/functions.sol.cpp Structs.o 15 | ${CXX} -I. -std=c++17 -Wall -Wextra -o $@ $^ 16 | -------------------------------------------------------------------------------- /exercises/functions/README.md: -------------------------------------------------------------------------------- 1 | # Functions and how arguments are passed in C++ 2 | 3 | Here, we will look a bit into how arguments are passed into functions. 4 | You will find an example where everything is passed by value (by copy), but there is a struct that is slow to copy. 5 | We will try to work with it without copying it. 6 | 7 | # Instructions 8 | 9 | ## Step 1 10 | 11 | Check out `functions.cpp`, compile it (`make`) and run the program. 12 | Check out `Structs.h`. It defines two structs that we will work with : `FastToCopy` and `SlowToCopy`. 13 | They are exactly what their name says, so let's try to avoid copying the latter. 14 | 15 | ## Step 2 16 | 17 | Using `printName()` as an example, write a function that prints the name of `SlowToCopy`. Call it in `main()`. 18 | 19 | ## Step 3 20 | 21 | Try passing by copy and passing by reference, see the difference. 22 | 23 | ## Step 4 24 | 25 | When passing by reference, ensure that your `printName` cannot inadvertently modify the original object. 26 | To test its const correctness, try adding something like `argument.name = "a";` to your print function. 27 | Try both with and without const attributes in your print function's signature. 28 | -------------------------------------------------------------------------------- /exercises/functions/Structs.cpp: -------------------------------------------------------------------------------- 1 | #include "Structs.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /// Construct a new instance of SlowToCopy. 9 | SlowToCopy::SlowToCopy() : name("SlowToCopy") {} 10 | 11 | /// Construct a new instance of SlowToCopy. 12 | SlowToCopy::SlowToCopy(const std::string& name) : name(name) {} 13 | 14 | /// Construct a new instance of SlowToCopy, copying the data from 'other'. 15 | SlowToCopy::SlowToCopy(const SlowToCopy& other) { 16 | std::cout << __func__ << ": Please don't copy me. This is slow.\n"; 17 | std::this_thread::sleep_for(std::chrono::seconds(3)); 18 | name = other.name; 19 | } 20 | -------------------------------------------------------------------------------- /exercises/functions/Structs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct FastToCopy { 6 | std::string name; 7 | }; 8 | 9 | struct SlowToCopy { 10 | std::string name; 11 | 12 | // Functions to create and copy this struct. 13 | // We go into details on the next days. 14 | SlowToCopy(); 15 | SlowToCopy(const std::string& name); 16 | SlowToCopy(const SlowToCopy& other); 17 | }; 18 | -------------------------------------------------------------------------------- /exercises/functions/functions.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* Tasks: 3 | * 1. Check out Structs.h. It defines two structs that we will work with. 4 | * FastToCopy 5 | * SlowToCopy 6 | * They are exactly what their name says, so let's try to avoid copying the latter. 7 | * 2. Using "printName()" as an example, write a function that prints the name of "SlowToCopy". 8 | * Call it in main(). 9 | * 3. Try passing by copy and passing by reference, see the difference. 10 | * 4. When passing by reference, ensure that your "printName" cannot inadvertently modify the original object. 11 | * To test its const correctness, try adding something like 12 | * argument.name = "other name"; 13 | * to your print function. 14 | * Try both with and without const attributes in your print function's signature. 15 | */ 16 | 17 | #include "Structs.h" // The data structs we will work with 18 | 19 | #include // For printing 20 | 21 | void printName(FastToCopy argument) { 22 | std::cout << argument.name << '\n'; 23 | } 24 | 25 | int main() { 26 | FastToCopy fast = {"Fast"}; 27 | printName(fast); 28 | 29 | SlowToCopy slow = {"Slow"}; 30 | // print it here 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /exercises/functions/solution/functions.sol.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* Tasks: 3 | * 1. Check out Structs.h. It defines two structs that we will work with. 4 | * FastToCopy 5 | * SlowToCopy 6 | * They are exactly what their name says, so let's try to avoid copying the latter. 7 | * 2. Using "printName()" as an example, write a function that prints the name of "SlowToCopy". 8 | * Call it in main(). 9 | * 3. Try passing by copy and passing by reference, see the difference. 10 | * 4. When passing by reference, ensure that your "printName" cannot inadvertently modify the original object. 11 | * To test its const correctness, try adding something like 12 | * argument.name = "other name"; 13 | * to your print function. 14 | * Try both with and without const attributes in your print function's signature. 15 | */ 16 | 17 | #include "Structs.h" // The data structs we will work with 18 | 19 | #include // For printing 20 | 21 | void printName(FastToCopy argument) { 22 | std::cout << argument.name << '\n'; 23 | } 24 | 25 | void inefficientPrintName(SlowToCopy argument) { 26 | std::cout << argument.name << '\n'; 27 | 28 | // We can change the argument's name because it's a copy: 29 | argument.name = "New name"; 30 | } 31 | 32 | void printName(const SlowToCopy & argument) { 33 | std::cout << argument.name << '\n'; 34 | 35 | // We are unable to change name, as we should: 36 | //argument.name = '\n'; 37 | } 38 | 39 | int main() { 40 | FastToCopy fast = {"Fast"}; 41 | printName(fast); 42 | 43 | SlowToCopy slow = {"Slow"}; 44 | printName(slow); 45 | 46 | std::cout << "Now printing with copy:\n"; 47 | inefficientPrintName(slow); 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /exercises/header_units/.gitignore: -------------------------------------------------------------------------------- 1 | header_units 2 | header_units.sol 3 | gcm.cache 4 | -------------------------------------------------------------------------------- /exercises/header_units/Makefile: -------------------------------------------------------------------------------- 1 | all: header_units 2 | solution: header_units.sol 3 | 4 | clean: 5 | rm -rf *.o header_units gcm.cache solution/header_units solution/*.o solution/gcm.cache 6 | 7 | main.o: main.cpp Complex.hpp 8 | ${CXX} -std=c++20 -fmodules-ts -g -O0 -Wall -Wextra -o $@ -c $< 9 | 10 | header_units : main.o 11 | ${CXX} -std=c++20 -fmodules-ts -g -O0 -Wall -Wextra -o $@ $^ 12 | 13 | header_units.sol: 14 | $(MAKE) -C solution header_units 15 | -------------------------------------------------------------------------------- /exercises/header_units/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | * convert the `#include "Complex.hpp"` to a header unit import 5 | * you will need to edit the Makefile and add a new target to precompile `Complex.hpp` 6 | * convert the standard library includes in `randomize.cpp` to header unit imports 7 | * you will need to precompile the standard library headers in your Makefile 8 | * with g++-11 and g++-12 you cannot yet precompile `` and ``, so keep those as a regular includes 9 | * below g++-12 you will get a linker error when building incrementally, so run `make clean` before each build 10 | -------------------------------------------------------------------------------- /exercises/header_units/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Complex.hpp" 7 | 8 | // The code below here should not need to change !! 9 | 10 | template 11 | void compute(int len, T initial, T step) { 12 | // allocate vectors 13 | std::vector v(len+1), diffs(len+1); 14 | 15 | // fill and randomize v 16 | std::generate(v.begin(), v.end(), [value = initial, step]() mutable { 17 | const T cur = value; 18 | value += step; 19 | return cur; 20 | }); 21 | std::shuffle(v.begin(), v.end(), std::default_random_engine{}); 22 | 23 | // compute differences 24 | std::adjacent_difference(v.begin(), v.end(), diffs.begin()); 25 | 26 | // compute standard deviation of it 27 | const T sum = std::reduce(diffs.begin()+1, diffs.end()); 28 | const T sumsq = std::accumulate(diffs.begin()+1, diffs.end(), T(), 29 | [](const T& s, const T& a) { return s + a * a; }); 30 | const T mean = sum/len; 31 | const T variance = sumsq/len - mean*mean; 32 | 33 | std::cout << "Range = [" << initial << ", " << step*len << "]\n" 34 | << "Mean = " << mean << '\n' 35 | << "Variance = " << variance << '\n'; 36 | } 37 | 38 | int main() { 39 | compute(1000, 0.0, 7.0); 40 | compute(1000, Complex(0,0), Complex(1,2)); 41 | } 42 | -------------------------------------------------------------------------------- /exercises/header_units/solution/Makefile: -------------------------------------------------------------------------------- 1 | all: header_units 2 | 3 | clean: 4 | rm -rf *.o header_units gcm.cache 5 | 6 | algorithm numeric random vector: 7 | ${CXX} -std=c++20 -fmodules-ts -x c++-system-header $@ 8 | 9 | Complex_header_unit: ../Complex.hpp 10 | ${CXX} -std=c++20 -g -O0 -Wall -Wextra -fmodules-ts -x c++-header $< 11 | 12 | main.o: main.cpp Complex_header_unit algorithm numeric random vector 13 | ${CXX} -std=c++20 -fmodules-ts -g -O0 -Wall -Wextra -o $@ -c $< 14 | 15 | header_units: main.o 16 | ${CXX} -std=c++20 -fmodules-ts -g -O0 -Wall -Wextra -o $@ $^ 17 | -------------------------------------------------------------------------------- /exercises/header_units/solution/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | import ; 3 | import ; 4 | import ; 5 | import ; 6 | import "../Complex.hpp"; 7 | 8 | template 9 | void compute(int len, T initial, T step) { 10 | // allocate vectors 11 | std::vector v(len+1), diffs(len+1); 12 | 13 | // fill and randomize v 14 | std::generate(v.begin(), v.end(), [value = initial, step]() mutable { 15 | const T cur = value; 16 | value += step; 17 | return cur; 18 | }); 19 | std::shuffle(v.begin(), v.end(), std::default_random_engine{}); 20 | 21 | // compute differences 22 | std::adjacent_difference(v.begin(), v.end(), diffs.begin()); 23 | 24 | // compute standard deviation of it 25 | const T sum = std::reduce(diffs.begin()+1, diffs.end()); 26 | const T sumsq = std::accumulate(diffs.begin()+1, diffs.end(), T(), 27 | [](const T& s, const T& a) { return s + a * a; }); 28 | const T mean = sum/len; 29 | const T variance = sumsq/len - mean*mean; 30 | 31 | std::cout << "Range = [" << initial << ", " << step*len << "]\n" 32 | << "Mean = " << mean << '\n' 33 | << "Variance = " << variance << '\n'; 34 | } 35 | 36 | int main() { 37 | compute(1000, 0.0, 7.0); 38 | compute(1000, Complex(0,0), Complex(1,2)); 39 | } 40 | -------------------------------------------------------------------------------- /exercises/helgrind/.gitignore: -------------------------------------------------------------------------------- 1 | fiboMT 2 | fiboMT.sol 3 | -------------------------------------------------------------------------------- /exercises/helgrind/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( helgrind LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Figure out how to use the platform's thread capabilities. 9 | find_package( Threads REQUIRED ) 10 | 11 | # Create the user's executable. 12 | add_executable( fiboMT "fiboMT.cpp" ) 13 | target_link_libraries( fiboMT PRIVATE Threads::Threads ) 14 | 15 | # Create the "solution executable". 16 | add_executable( fiboMT.sol EXCLUDE_FROM_ALL "solution/fiboMT.sol.cpp" ) 17 | target_link_libraries( fiboMT.sol PRIVATE Threads::Threads ) 18 | add_dependencies( solution fiboMT.sol ) 19 | -------------------------------------------------------------------------------- /exercises/helgrind/Makefile: -------------------------------------------------------------------------------- 1 | all: fiboMT 2 | solution: fiboMT.sol 3 | 4 | clean: 5 | rm -f *o fiboMT *~ fiboMT.sol core 6 | 7 | fiboMT : fiboMT.cpp 8 | ${CXX} -std=c++17 -g -O0 -Wall -Wextra -o $@ $< 9 | 10 | fiboMT.sol : solution/fiboMT.sol.cpp 11 | ${CXX} -std=c++17 -g -O0 -Wall -Wextra -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/helgrind/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | * compile, run 5 | * check it with valgrind. You may see strange behavior but it may be perfectly fine. 6 | * check it with valgrind --tool=helgrind 7 | * understand issue and fix 8 | -------------------------------------------------------------------------------- /exercises/helgrind/fiboMT.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | constexpr auto NBITERATIONS = 5; 9 | constexpr auto MIN = 22; 10 | constexpr auto MAX = 25; 11 | 12 | struct WorkToDo { 13 | char* title; 14 | int a; 15 | }; 16 | 17 | unsigned int fibo(unsigned a) { 18 | if (a == 1 || a == 0) { 19 | return 1; 20 | } else { 21 | return fibo(a-1)+fibo(a-2); 22 | } 23 | } 24 | 25 | void computation(WorkToDo* work, unsigned long* result) { 26 | *result = fibo(work->a); 27 | std::free(work->title); 28 | work->title = nullptr; 29 | } 30 | 31 | void launchFibo(const char* title, int a) { 32 | WorkToDo w; 33 | w.title = strdup(title); 34 | w.a = a; 35 | unsigned long result; 36 | std::thread t{computation, &w, &result}; 37 | std::this_thread::sleep_for(std::chrono::microseconds{1}); 38 | std::cout << "Computing " << w.title << '\n'; 39 | t.join(); 40 | std::cout << title << " = " << (unsigned long)result << '\n'; 41 | } 42 | 43 | int main() { 44 | std::default_random_engine e; 45 | std::uniform_int_distribution d{MIN, MAX}; 46 | for (unsigned int i = 0; i < NBITERATIONS; i++) { 47 | unsigned int a = d(e); 48 | std::stringstream ss; 49 | ss << "Fibo(" << a << ")"; 50 | launchFibo(ss.str().c_str(), a); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /exercises/helgrind/solution/fiboMT.sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | constexpr auto NBITERATIONS = 5; 9 | constexpr auto MIN = 22; 10 | constexpr auto MAX = 25; 11 | 12 | struct WorkToDo { 13 | char* title; 14 | int a; 15 | }; 16 | 17 | unsigned int fibo(unsigned a) { 18 | if (a == 1 || a == 0) { 19 | return 1; 20 | } else { 21 | return fibo(a-1)+fibo(a-2); 22 | } 23 | } 24 | 25 | void computation(WorkToDo* work, unsigned long* result) { 26 | *result = fibo(work->a); 27 | } 28 | 29 | void launchFibo(const char* title, int a) { 30 | WorkToDo w; 31 | w.title = strdup(title); 32 | w.a = a; 33 | unsigned long result; 34 | std::thread t{computation, &w, &result}; 35 | std::this_thread::sleep_for(std::chrono::microseconds{1}); 36 | std::cout << "Computing " << w.title << '\n'; 37 | std::free(w.title); 38 | w.title = nullptr; 39 | t.join(); 40 | std::cout << title << " = " << (unsigned long)result << '\n'; 41 | } 42 | 43 | int main() { 44 | std::default_random_engine e; 45 | std::uniform_int_distribution d{MIN, MAX}; 46 | for (unsigned int i = 0; i < NBITERATIONS; i++) { 47 | unsigned int a = d(e); 48 | std::stringstream ss; 49 | ss << "Fibo(" << a << ")"; 50 | launchFibo(ss.str().c_str(), a); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /exercises/hello/.gitignore: -------------------------------------------------------------------------------- 1 | hello 2 | -------------------------------------------------------------------------------- /exercises/hello/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( hello LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | set(CMAKE_CXX_STANDARD 20) 8 | 9 | # Set up the library. 10 | add_library( helloLib "hello.hpp" "hello.cpp" ) 11 | set_target_properties( helloLib PROPERTIES OUTPUT_NAME "hello" ) 12 | 13 | # Set up the executable. 14 | add_executable( hello "main.cpp" ) 15 | target_link_libraries( hello PRIVATE helloLib ) 16 | -------------------------------------------------------------------------------- /exercises/hello/Makefile: -------------------------------------------------------------------------------- 1 | all: libhello.a hello 2 | 3 | solution: all 4 | 5 | clean: 6 | rm -f *.o *.a *.so hello *~ callgrind.out.* 7 | 8 | hello.o: hello.cpp hello.hpp 9 | $(CXX) --std=c++20 -c -g -Wall -Wextra -o $@ $< 10 | 11 | libhello.a: hello.o 12 | ar rcs $@ $< 13 | 14 | hello : main.cpp libhello.a 15 | $(CXX) --std=c++20 -g -Wall -Wextra -o $@ $^ 16 | -------------------------------------------------------------------------------- /exercises/hello/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Hello World ! 3 | 4 | This example should help to check that your machine is well installed. 5 | 6 | ## make vs cmake 7 | 8 | On any linux like system, provided you have a "recent enough" g++, this should work out of the box: 9 | ``` 10 | make 11 | ./hello 12 | ``` 13 | 14 | On native Windows, build with `cmake`: 15 | ``` 16 | mkdir build 17 | cd build 18 | cmake .. 19 | cmake --build . 20 | Debug/hello.exe 21 | ``` 22 | 23 | ## valgrind & callgrind & graphical tools 24 | 25 | Try: 26 | ``` 27 | valgrind --tool=callgrind ./hello 28 | kcachegrind 29 | ``` 30 | 31 | ## cppcheck 32 | 33 | Try: 34 | ``` 35 | cppcheck *.hpp *.cpp 36 | ``` 37 | -------------------------------------------------------------------------------- /exercises/hello/hello.cpp: -------------------------------------------------------------------------------- 1 | #include "hello.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void printHello(int i) { 8 | std::cout << "Hello, world " << i << '\n'; 9 | } 10 | 11 | void checkCpp20() { 12 | std::array worldHellos{"Bonjour", "Ciao", "Guten Tag", "Hello"}; 13 | for (int i=0; auto const& hello : worldHellos) { 14 | i++; 15 | std::cout << "(" << i << ") " << hello << "\n"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /exercises/hello/hello.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void printHello(int i); 4 | void checkCpp20(); 5 | -------------------------------------------------------------------------------- /exercises/hello/main.cpp: -------------------------------------------------------------------------------- 1 | #include "hello.hpp" 2 | 3 | int main() { 4 | int n = 3; 5 | for (int i = 0; i < n; i++) { 6 | printHello(i); 7 | } 8 | checkCpp20(); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /exercises/loopsRefsAuto/.gitignore: -------------------------------------------------------------------------------- 1 | loopsRefsAuto 2 | loopsRefsAuto.sol 3 | -------------------------------------------------------------------------------- /exercises/loopsRefsAuto/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( loopsRefsAuto LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( loopsRefsAuto "loopsRefsAuto.cpp" ) 10 | 11 | # Create the "solution executable". 12 | add_executable( loopsRefsAuto.sol EXCLUDE_FROM_ALL "solution/loopsRefsAuto.sol.cpp" ) 13 | add_dependencies( solution loopsRefsAuto.sol ) 14 | -------------------------------------------------------------------------------- /exercises/loopsRefsAuto/Makefile: -------------------------------------------------------------------------------- 1 | all: loopsRefsAuto 2 | solution: loopsRefsAuto.sol 3 | 4 | clean: 5 | rm -f *o *~ loopsRefsAuto loopsRefsAuto.sol 6 | 7 | %.o: %.cpp %.h 8 | ${CXX} -std=c++17 -Wall -Wextra -c -o $@ $< 9 | 10 | loopsRefsAuto : loopsRefsAuto.cpp 11 | ${CXX} -std=c++17 -Wall -Wextra -o $@ $^ 12 | 13 | loopsRefsAuto.sol : solution/loopsRefsAuto.sol.cpp 14 | ${CXX} -std=c++17 -Wall -Wextra -o $@ $^ 15 | -------------------------------------------------------------------------------- /exercises/loopsRefsAuto/README.md: -------------------------------------------------------------------------------- 1 | # Exercise about for loops, references and the `auto` keyword. 2 | 3 | Here, we are playing with a struct that doesn't like to be copied. Imagine that this is some large data 4 | that is expensive to copy. We will learn how to work with an array of such data without copying it. 5 | 6 | ## Instructions: 7 | - Open `loopsRefsAuto.cpp`, and familiarise yourself with what happens in `main()`. 8 | - Compile (`make`) and run the program. 9 | - In the source file, you will find further tasks. 10 | -------------------------------------------------------------------------------- /exercises/loopsRefsAuto/loopsRefsAuto.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct DontCopyMe { 4 | int resultA; 5 | int resultB; 6 | 7 | // This is material for the second day: 8 | DontCopyMe() = default; 9 | DontCopyMe(const DontCopyMe& other) : 10 | resultA(other.resultA), 11 | resultB(other.resultB) 12 | { std::cout << "Please don't copy me\n"; } 13 | }; 14 | 15 | int main() { 16 | // We create an array of DontCopyMe structs: 17 | DontCopyMe collection[10]; 18 | 19 | // Task 1: 20 | // Write a for loop that initialises resultA and resultB for each element in the above array 21 | // with sensible numbers. 22 | // Verify the output of the program before and after you do this. 23 | 24 | 25 | // Task 2: 26 | // We use a range-based for loop to analyse the array of structs. 27 | // The problem is: we are copying every DontCopyMe ... 28 | // Fix this loop using references. 29 | // Hint: Fix the type declaration "auto" in the loop head. 30 | int resultA = 0; 31 | int resultB = 0; 32 | for (auto item : collection) { 33 | resultA += item.resultA; 34 | resultB += item.resultB; 35 | } 36 | std::cout << "resultA = " << resultA << "\tresultB = " << resultB << "\n"; 37 | 38 | return 0; 39 | } 40 | 41 | // Task 3: 42 | // Think about which loop needs write access to the DontCopyMe. 43 | // Make sure that all references that don't need write access are const. 44 | // Hint: C++ understands "auto const". 45 | -------------------------------------------------------------------------------- /exercises/loopsRefsAuto/solution/loopsRefsAuto.sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct DontCopyMe { 4 | int resultA; 5 | int resultB; 6 | 7 | // This is material for the second day: 8 | DontCopyMe() = default; 9 | DontCopyMe(const DontCopyMe& other) : 10 | resultA(other.resultA), 11 | resultB(other.resultB) 12 | { std::cout << "Please don't copy me\n"; } 13 | }; 14 | 15 | int main() { 16 | // We create an array of DontCopyMe structs: 17 | DontCopyMe collection[10]; 18 | 19 | // Task 1: 20 | // Write a for loop that initialises resultA and resultB for each element in the above array 21 | // with sensible numbers. 22 | // Verify the output of the program before and after you do this. 23 | 24 | for ( int i = 0 ; i<10 ; ++i ) { 25 | collection[i].resultA = i; 26 | collection[i].resultB = 2*i; 27 | } 28 | 29 | // Task 2: 30 | // We use a range-based for loop to analyse the array of structs. 31 | // The problem is: we are copying every DontCopyMe ... 32 | // Fix this loop using references. 33 | // Hint: Fix the type declaration "auto" in the loop head. 34 | int resultA = 0; 35 | int resultB = 0; 36 | for (auto const & item : collection) { 37 | resultA += item.resultA; 38 | resultB += item.resultB; 39 | } 40 | std::cout << "resultA = " << resultA << "\tresultB = " << resultB << "\n"; 41 | 42 | return 0; 43 | } 44 | 45 | // Task 3: 46 | // Think about which loop needs write access to the DontCopyMe. 47 | // Make sure that all references that don't need write access are const. 48 | // Hint: C++ understands "auto const". 49 | -------------------------------------------------------------------------------- /exercises/memcheck/.gitignore: -------------------------------------------------------------------------------- 1 | memleak 2 | memleak.sol 3 | -------------------------------------------------------------------------------- /exercises/memcheck/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( memcheck LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's library. 9 | add_library( memcheckPoly "Polygons.hpp" "Polygons.cpp" ) 10 | set_target_properties( memcheckPoly PROPERTIES OUTPUT_NAME "poly" ) 11 | if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" ) 12 | target_compile_definitions( memcheckPoly PRIVATE _USE_MATH_DEFINES ) 13 | endif() 14 | 15 | # Create the user's executable. 16 | add_executable( memleak "memleak.cpp" ) 17 | target_link_libraries( memleak PRIVATE memcheckPoly ) 18 | 19 | # Create the "solution library". 20 | add_library( memcheckPolySol EXCLUDE_FROM_ALL "solution/Polygons.sol.hpp" "solution/Polygons.sol.cpp" ) 21 | set_target_properties( memcheckPolySol PROPERTIES OUTPUT_NAME "polysol" ) 22 | if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" ) 23 | target_compile_definitions( memcheckPolySol PRIVATE _USE_MATH_DEFINES ) 24 | endif() 25 | 26 | # Create the "solution executable". 27 | add_executable( memleak.sol EXCLUDE_FROM_ALL "solution/memleak.sol.cpp" ) 28 | target_link_libraries( memleak.sol PRIVATE memcheckPolySol ) 29 | add_dependencies( solution memleak.sol ) 30 | -------------------------------------------------------------------------------- /exercises/memcheck/Makefile: -------------------------------------------------------------------------------- 1 | all: libpoly.so memleak 2 | solution: libpolysol.so memleak.sol 3 | 4 | clean: 5 | rm -f *o *so memleak *~ memleak.sol vgcore* 6 | 7 | libpoly.so: Polygons.cpp Polygons.hpp 8 | $(CXX) -g -Wall -Wextra -shared -fPIC -o $@ $< 9 | 10 | memleak : memleak.cpp libpoly.so 11 | $(CXX) -g -Wall -Wextra -o $@ $^ 12 | 13 | libpolysol.so: solution/Polygons.sol.cpp solution/Polygons.sol.hpp 14 | $(CXX) -g -Wall -Wextra -shared -fPIC -o $@ $< 15 | 16 | memleak.sol : solution/memleak.sol.cpp libpolysol.so 17 | $(CXX) -g -Wall -Wextra -o $@ $^ 18 | -------------------------------------------------------------------------------- /exercises/memcheck/Polygons.cpp: -------------------------------------------------------------------------------- 1 | #include "Polygons.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | Polygon::Polygon(int n, float radius) : m_nbSides(n), m_radius(radius) {}; 8 | 9 | float Polygon::computePerimeter() { 10 | std::cout << "Generic computePerimeter for polygons\n"; 11 | return 2*m_nbSides*std::sin(static_cast(M_PI)/m_nbSides)*m_radius; 12 | } 13 | 14 | Hexagon::Hexagon(char* name, float radius) : Polygon(6, radius) { 15 | m_name = strdup(name); 16 | } 17 | 18 | Hexagon::~Hexagon() { 19 | free(m_name); 20 | m_name = 0; 21 | } 22 | -------------------------------------------------------------------------------- /exercises/memcheck/Polygons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Polygon { 4 | public: 5 | Polygon(int n, float radius); 6 | ~Polygon(){}; 7 | float computePerimeter(); 8 | protected: 9 | int m_nbSides; 10 | float m_radius; 11 | }; 12 | 13 | class Hexagon : public Polygon { 14 | public: 15 | Hexagon(char* name, float radius); 16 | Hexagon(const Hexagon&) = delete; 17 | Hexagon& operator=(const Hexagon&) = delete; 18 | ~Hexagon(); 19 | char* name() const {return m_name;}; 20 | private: 21 | char* m_name; 22 | }; 23 | -------------------------------------------------------------------------------- /exercises/memcheck/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsf-training/cpluspluscourse/20c735b86f594b85735fd3c65e3c202fced585da/exercises/memcheck/README.md -------------------------------------------------------------------------------- /exercises/memcheck/memleak.cpp: -------------------------------------------------------------------------------- 1 | #include "Polygons.hpp" 2 | #include 3 | 4 | Polygon* getHexa(char* name, float radius) { 5 | return new Hexagon(name, radius); 6 | } 7 | 8 | int main() { 9 | // create an Hexagon, call its perimeter method 10 | Polygon *hexa = getHexa((char*)"hexa", 1.0); 11 | std::cout << "Hexa : perimeter = " << hexa->computePerimeter() << "\n\n"; 12 | 13 | // memory deallocation 14 | delete hexa; 15 | } 16 | -------------------------------------------------------------------------------- /exercises/memcheck/solution/Polygons.sol.cpp: -------------------------------------------------------------------------------- 1 | #include "Polygons.sol.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | Polygon::Polygon(int n, float radius) : m_nbSides(n), m_radius(radius) {}; 8 | 9 | float Polygon::computePerimeter() { 10 | std::cout << "Generic computePerimeter for polygons\n"; 11 | return 2*m_nbSides*std::sin(static_cast(M_PI)/m_nbSides)*m_radius; 12 | } 13 | 14 | Hexagon::Hexagon(char* name, float radius) : Polygon(6, radius) { 15 | m_name = strdup(name); 16 | } 17 | 18 | Hexagon::~Hexagon() { 19 | free(m_name); 20 | m_name = 0; 21 | } 22 | -------------------------------------------------------------------------------- /exercises/memcheck/solution/Polygons.sol.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Polygon { 4 | public: 5 | Polygon(int n, float radius); 6 | virtual ~Polygon(){}; 7 | float computePerimeter(); 8 | protected: 9 | int m_nbSides; 10 | float m_radius; 11 | }; 12 | 13 | class Hexagon : public Polygon { 14 | public: 15 | Hexagon(char* name, float radius); 16 | Hexagon(const Hexagon&) = delete; 17 | Hexagon& operator=(const Hexagon&) = delete; 18 | ~Hexagon(); 19 | char* name() const {return m_name;}; 20 | private: 21 | char* m_name; 22 | }; 23 | -------------------------------------------------------------------------------- /exercises/memcheck/solution/memleak.sol.cpp: -------------------------------------------------------------------------------- 1 | #include "Polygons.sol.hpp" 2 | #include 3 | 4 | Polygon* getHexa(char* name, float radius) { 5 | return new Hexagon(name, radius); 6 | } 7 | 8 | int main() { 9 | // create an Hexagon, call its perimeter method 10 | Polygon *hexa = getHexa((char*)"hexa", 1.0); 11 | std::cout << "Hexa : perimeter = " << hexa->computePerimeter() << "\n\n"; 12 | 13 | // memory deallocation 14 | delete hexa; 15 | } 16 | -------------------------------------------------------------------------------- /exercises/modern_oo/.gitignore: -------------------------------------------------------------------------------- 1 | particles 2 | particles.sol 3 | -------------------------------------------------------------------------------- /exercises/modern_oo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( modern_oo LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( particles "particles.cpp" ) 10 | 11 | # Create the "solution executable". 12 | add_executable( particles.sol EXCLUDE_FROM_ALL "solution/particles.sol.cpp" ) 13 | add_dependencies( solution particles.sol ) 14 | -------------------------------------------------------------------------------- /exercises/modern_oo/Makefile: -------------------------------------------------------------------------------- 1 | all: particles 2 | solution: particles.sol 3 | 4 | clean: 5 | rm -f *o particles particles.sol *~ callgrind.out.* 6 | 7 | particles: particles.cpp 8 | ${CXX} -g -std=c++17 -Wall -Wextra -o $@ $< 9 | 10 | particles.sol : solution/particles.sol.cpp 11 | ${CXX} -g -std=c++17 -Wall -Wextra -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/modern_oo/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Prerequisites 3 | 4 | * know about classes in ancient C++ 5 | 6 | ## Instructions 7 | 8 | * insert one `= delete`, one `= default` and one `override`. 9 | -------------------------------------------------------------------------------- /exercises/modern_oo/particles.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | class Particle 6 | { 7 | public : 8 | Particle( double mass ) : mass_(mass) {} 9 | double mass() const { return mass_ ; } 10 | virtual std::string name() const { return "Particle" ; } 11 | virtual ~Particle() {} 12 | private : 13 | Particle( const Particle & ) ; // non copiable 14 | double mass_ ; 15 | } ; 16 | 17 | class ChargedParticle : public Particle 18 | { 19 | public : 20 | ChargedParticle( double mass, double charge ) 21 | : Particle(mass), charge_(charge) {} 22 | double charge() const { return charge_ ; } 23 | virtual std::string name() const { return "ChargedParticle" ; } 24 | private : 25 | double charge_ ; 26 | } ; 27 | 28 | void print( Particle & p ) 29 | { 30 | std::cout << p.name() << '\n' ; 31 | std::cout << " mass = " << p.mass() << '\n' ; 32 | } 33 | 34 | int main() 35 | { 36 | std::default_random_engine e; 37 | std::uniform_real_distribution d; 38 | for ( int i = 0 ; i < 5 ; ++i ) 39 | { 40 | if ( d(e) < 0.5 ) 41 | { 42 | Particle p(2) ; 43 | print(p) ; 44 | } 45 | else 46 | { 47 | ChargedParticle p(1,1) ; 48 | print(p) ; 49 | std::cout << " charge = " << p.charge() << '\n' ; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /exercises/modern_oo/solution/particles.sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | class Particle 6 | { 7 | public : 8 | Particle( double mass ) : mass_(mass) {} 9 | Particle( const Particle & ) = delete ; 10 | virtual ~Particle() = default ; 11 | double mass() const { return mass_ ; } 12 | virtual std::string name() const { return "Particle" ; } 13 | private : 14 | double mass_ ; 15 | } ; 16 | 17 | class ChargedParticle : public Particle 18 | { 19 | public : 20 | ChargedParticle( double mass, double charge ) 21 | : Particle(mass), charge_(charge) {} 22 | double charge() const { return charge_ ; } 23 | std::string name() const override { return "ChargedParticle" ; } 24 | private : 25 | double charge_ = 0.0 ; 26 | } ; 27 | 28 | void print( Particle & p ) 29 | { 30 | std::cout << p.name() << '\n' ; 31 | std::cout << " mass = " << p.mass() << '\n' ; 32 | } 33 | 34 | int main() 35 | { 36 | std::default_random_engine e; 37 | std::uniform_real_distribution d; 38 | for ( int i = 0 ; i < 5 ; ++i ) 39 | { 40 | if ( d(e) < 0.5 ) 41 | { 42 | Particle p(2) ; 43 | print(p) ; 44 | } 45 | else 46 | { 47 | ChargedParticle p(1,1) ; 48 | print(p) ; 49 | std::cout << " charge = " << p.charge() << '\n' ; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /exercises/modules/.gitignore: -------------------------------------------------------------------------------- 1 | modules 2 | solution/modules 3 | solution/gcm.cache 4 | -------------------------------------------------------------------------------- /exercises/modules/Makefile: -------------------------------------------------------------------------------- 1 | all: modules 2 | solution: modules.sol 3 | 4 | clean: 5 | rm -rf *.o modules gcm.cache 6 | 7 | main.o: main.cpp Complex.hpp 8 | ${CXX} -std=c++20 -fmodules-ts -g -O0 -Wall -Wextra -o $@ -c $< 9 | 10 | modules: main.o 11 | ${CXX} -std=c++20 -fmodules-ts -g -O0 -Wall -Wextra -o $@ $^ 12 | 13 | modules.sol: 14 | $(MAKE) -C solution modules 15 | -------------------------------------------------------------------------------- /exercises/modules/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | * convert the `Complex.hpp` header into a module named 'math' 5 | * you will need to rename `Complex.hpp` into `Complex.cpp`, since it is a translation unit now 6 | * you will need to edit the Makefile and add a new target to compile `Complex.cpp` 7 | * import the module `math` from `main.cpp` 8 | -------------------------------------------------------------------------------- /exercises/modules/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Complex.hpp" 7 | 8 | // The code below here should not need to change !! 9 | 10 | template 11 | void compute(int len, T initial, T step) { 12 | // allocate vectors 13 | std::vector v(len+1), diffs(len+1); 14 | 15 | // fill and randomize v 16 | std::generate(v.begin(), v.end(), [value = initial, step]() mutable { 17 | const T cur = value; 18 | value += step; 19 | return cur; 20 | }); 21 | std::shuffle(v.begin(), v.end(), std::default_random_engine{}); 22 | 23 | // compute differences 24 | std::adjacent_difference(v.begin(), v.end(), diffs.begin()); 25 | 26 | // compute standard deviation of it 27 | const T sum = std::reduce(diffs.begin()+1, diffs.end()); 28 | const T sumsq = std::accumulate(diffs.begin()+1, diffs.end(), T(), 29 | [](const T& s, const T& a) { return s + a * a; }); 30 | const T mean = sum/len; 31 | const T variance = sumsq/len - mean*mean; 32 | 33 | std::cout << "Range = [" << initial << ", " << step*len << "]\n" 34 | << "Mean = " << mean << '\n' 35 | << "Variance = " << variance << '\n'; 36 | } 37 | 38 | int main() { 39 | compute(1000, 0.0, 7.0); 40 | compute(1000, Complex(0,0), Complex(1,2)); 41 | } 42 | -------------------------------------------------------------------------------- /exercises/modules/solution/Makefile: -------------------------------------------------------------------------------- 1 | all: modules 2 | 3 | clean: 4 | rm -rf *.o modules gcm.cache 5 | 6 | Complex.o: Complex.cpp 7 | ${CXX} -std=c++20 -fmodules-ts -g -O0 -Wall -Wextra -o $@ -c $< 8 | 9 | main.o: main.cpp Complex.o 10 | ${CXX} -std=c++20 -fmodules-ts -g -O0 -Wall -Wextra -o $@ -c $< 11 | 12 | modules: main.o Complex.o 13 | ${CXX} -std=c++20 -fmodules-ts -g -O0 -Wall -Wextra -o $@ $^ 14 | -------------------------------------------------------------------------------- /exercises/modules/solution/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | import math; 8 | 9 | // The code below here should not need to change !! 10 | 11 | template 12 | void compute(int len, T initial, T step) { 13 | // allocate vectors 14 | std::vector v(len+1), diffs(len+1); 15 | 16 | // fill and randomize v 17 | std::generate(v.begin(), v.end(), [value = initial, step]() mutable { 18 | const T cur = value; 19 | value += step; 20 | return cur; 21 | }); 22 | std::shuffle(v.begin(), v.end(), std::default_random_engine{}); 23 | 24 | // compute differences 25 | std::adjacent_difference(v.begin(), v.end(), diffs.begin()); 26 | 27 | // compute standard deviation of it 28 | const T sum = std::reduce(diffs.begin()+1, diffs.end()); 29 | const T sumsq = std::accumulate(diffs.begin()+1, diffs.end(), T(), 30 | [](const T& s, const T& a) { return s + a * a; }); 31 | const T mean = sum/len; 32 | const T variance = sumsq/len - mean*mean; 33 | 34 | std::cout << "Range = [" << initial << ", " << step*len << "]\n" 35 | << "Mean = " << mean << '\n' 36 | << "Variance = " << variance << '\n'; 37 | } 38 | 39 | int main() { 40 | compute(1000, 0.0, 7.0); 41 | compute(1000, Complex(0,0), Complex(1,2)); 42 | } 43 | -------------------------------------------------------------------------------- /exercises/move/.gitignore: -------------------------------------------------------------------------------- 1 | trymove 2 | trymove.sol 3 | -------------------------------------------------------------------------------- /exercises/move/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( move LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( trymove "trymove.cpp" ) 10 | 11 | # Create the "solution executable". 12 | add_executable( trymove.sol EXCLUDE_FROM_ALL "solution/trymove.sol.cpp" ) 13 | add_dependencies( solution trymove.sol ) 14 | -------------------------------------------------------------------------------- /exercises/move/Makefile: -------------------------------------------------------------------------------- 1 | all: trymove 2 | solution: trymove.sol 3 | 4 | clean: 5 | rm -f *o trymove trymove.sol *~ callgrind.out.* 6 | 7 | trymove : trymove.cpp 8 | ${CXX} -g -std=c++17 -O2 -Wall -Wextra -L. -o $@ $< 9 | 10 | trymove.sol : solution/trymove.sol.cpp 11 | ${CXX} -g -std=c++17 -O2 -Wall -Wextra -L. -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/move/README.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | - Copy and move constructors 3 | - Copy and move assignment operators 4 | 5 | ## Instructions 6 | 7 | * look at the code and run it with callgrind or time it e.g. using `/usr/bin/time ./trymove` 8 | * understand how inefficient it is 9 | * understand why and fix trymove.cpp 10 | * see efficiency improvements 11 | -------------------------------------------------------------------------------- /exercises/operators/.gitignore: -------------------------------------------------------------------------------- 1 | operators 2 | operators_sol 3 | -------------------------------------------------------------------------------- /exercises/operators/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( operators LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | set( CMAKE_CXX_STANDARD 20 ) 8 | 9 | # Create the user's executable. 10 | add_executable( operators "operators.cpp" ) 11 | 12 | # Create the "solution executable". 13 | add_executable( operators_sol EXCLUDE_FROM_ALL "solution/operators_sol.cpp" ) 14 | add_dependencies( solution operators_sol ) 15 | -------------------------------------------------------------------------------- /exercises/operators/Makefile: -------------------------------------------------------------------------------- 1 | all: operators 2 | solution: operators_sol 3 | 4 | clean: 5 | rm -f *o operators *~ operators_sol 6 | 7 | operators : operators.cpp 8 | ${CXX} -g -std=c++20 -O0 -Wall -Wextra -L. -o $@ $< 9 | 10 | operators_sol : solution/operators_sol.cpp 11 | ${CXX} -g -std=c++20 -O0 -Wall -Wextra -L. -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/operators/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | STEP 1 5 | - Add a free operator<<, reusing str(), and simplify main() first lines. 6 | - Replace equal() with operator==(), and upgrade tests. 7 | - Add operator!=(), reusing operator==(), and upgrade tests. 8 | - Replace compare() with operator<=>(), reusing <=> between doubles, 9 | and upgrade tests. 10 | - Replace multiply() with operator*(), and upgrade tests. 11 | 12 | STEP 2 13 | - Replace TestResultPrinter::process() with operator()(), and upgrade CHECK(). 14 | 15 | OPTIONAL STEP 3 16 | - Add an inplace multiplication operator*=(), and add tests. 17 | - Review operator*() so to reuse operator*=(). 18 | - Ensure calls to operator*=() can be chained, the same as operator<<(). 19 | 20 | ## Take away 21 | 22 | - Do not confuse equality and equivalence. 23 | - We can very often implement an arithmetic operator@ in terms of operator@=. 24 | - When implementing <=>, you get <, >, <=, >= for free. 25 | - Object-functions are very used with standard algorithms, 26 | yet tend to be often replaced by lambdas in modern C++. 27 | -------------------------------------------------------------------------------- /exercises/optional/.gitignore: -------------------------------------------------------------------------------- 1 | optional 2 | -------------------------------------------------------------------------------- /exercises/optional/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( optional LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | set(CMAKE_CXX_STANDARD 20) 8 | 9 | # Create the user's executable. 10 | add_executable( optional "optional.cpp" ) 11 | 12 | # Create the "solution executable". 13 | add_executable( optional.sol EXCLUDE_FROM_ALL "solution/optional.sol.cpp" ) 14 | add_dependencies( solution optional.sol ) 15 | -------------------------------------------------------------------------------- /exercises/optional/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: optional 3 | solution: optional.sol 4 | 5 | clean: 6 | rm -f *o *so optional *~ optional.sol 7 | 8 | % : %.cpp 9 | $(CXX) -g -std=c++20 -Wall -Wextra -o $@ $< 10 | 11 | %.sol : solution/%.sol.cpp 12 | $(CXX) -g -std=c++20 -Wall -Wextra -o $@ $< 13 | -------------------------------------------------------------------------------- /exercises/optional/README.md: -------------------------------------------------------------------------------- 1 | # Optional 2 | 3 | Here we explore: 4 | - how `std::optional` offers an alternative to errors codes and exceptions. 5 | - how the insertion of a `std::optional` affects following functions. 6 | 7 | ## Prerequisites 8 | 9 | - slides about `std::optional` 10 | 11 | ## Instructions 12 | 13 | In the file `optional.cpp`, modify the function `mysqrt` below so that it returns an `std::optional` rather than a double, and the program prints `nothing` rather than `nan` for the call with `-10`. It will also require to modify `square`. 14 | -------------------------------------------------------------------------------- /exercises/optional/optional.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 4 | In the code below, modify the function `mysqrt` below so that it returns 5 | an `std::optional` rather than a double, and the program prints 6 | `nothing` rather than `nan` for the call with `-10`. It will also require 7 | to update `square`. 8 | 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | double mysqrt(double d) // TO BE MODIFIED 16 | { 17 | return std::sqrt(d); // TO BE MODIFIED 18 | } 19 | 20 | double square(double d) // TO BE MODIFIED 21 | { 22 | return d * d; // TO BE MODIFIED 23 | } 24 | 25 | template 26 | std::ostream &operator<<(std::ostream &os, std::optional const &opt) { 27 | if (opt) { 28 | return os << opt.value(); 29 | } else { 30 | return os << "nothing"; 31 | } 32 | } 33 | 34 | int main() { 35 | std::cout << square(mysqrt(10)) << std::endl; 36 | std::cout << square(mysqrt(-10)) << std::endl; 37 | } 38 | -------------------------------------------------------------------------------- /exercises/optional/solution/optional.sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::optional mysqrt(double d) { 6 | if (d < 0) { 7 | return {}; 8 | } else { 9 | return std::sqrt(d); 10 | } 11 | } 12 | 13 | std::optional square(std::optional d) { 14 | if (d) { 15 | return d.value() * d.value(); 16 | } else { 17 | return {}; 18 | } 19 | } 20 | 21 | template 22 | std::ostream &operator<<(std::ostream &os, std::optional const &opt) { 23 | if (opt) { 24 | return os << opt.value(); 25 | } else { 26 | return os << "nothing"; 27 | } 28 | } 29 | 30 | int main() { 31 | std::cout << square(mysqrt(10)) << std::endl; 32 | std::cout << square(mysqrt(-10)) << std::endl; 33 | } 34 | -------------------------------------------------------------------------------- /exercises/polymorphism/.gitignore: -------------------------------------------------------------------------------- 1 | trypoly 2 | trypoly.sol 3 | -------------------------------------------------------------------------------- /exercises/polymorphism/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( polymorhism LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's library. 9 | add_library( polymorphismPoly "Polygons.hpp" "Polygons.cpp" ) 10 | target_include_directories( polymorphismPoly PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" ) 11 | set_target_properties( polymorphismPoly PROPERTIES OUTPUT_NAME "poly" ) 12 | if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" ) 13 | target_compile_definitions( polymorphismPoly PRIVATE _USE_MATH_DEFINES ) 14 | endif() 15 | 16 | # Create the user's executable. 17 | add_executable( trypoly "trypoly.cpp" ) 18 | target_link_libraries( trypoly PRIVATE polymorphismPoly ) 19 | 20 | # Create the "solution executable". 21 | add_executable( trypoly.sol EXCLUDE_FROM_ALL "solution/trypoly.sol.cpp" ) 22 | target_link_libraries( trypoly.sol PRIVATE polymorphismPoly ) 23 | add_dependencies( solution trypoly.sol ) 24 | -------------------------------------------------------------------------------- /exercises/polymorphism/Makefile: -------------------------------------------------------------------------------- 1 | all: trypoly 2 | solution: trypoly.sol 3 | 4 | clean: 5 | rm -f *o *so trypoly *~ trypoly.sol 6 | 7 | libpoly.so: Polygons.cpp Polygons.hpp 8 | $(CXX) -std=c++11 -Wsuggest-override -g -Wall -Wextra -shared -fPIC -o $@ $< 9 | 10 | trypoly : trypoly.cpp libpoly.so 11 | $(CXX) -std=c++11 -Wsuggest-override -g -Wall -Wextra -o $@ $^ 12 | 13 | trypoly.sol : solution/trypoly.sol.cpp libpoly.so 14 | $(CXX) -std=c++11 -g -Wall -Wextra -I. -o $@ $^ 15 | -------------------------------------------------------------------------------- /exercises/polymorphism/Polygons.cpp: -------------------------------------------------------------------------------- 1 | #include "Polygons.hpp" 2 | #include 3 | #include 4 | 5 | RegularPolygon::RegularPolygon(int n, float radius) : m_nbSides(n), m_radius(radius) {}; 6 | 7 | float RegularPolygon::computePerimeter() const { 8 | std::cout << "Polygon::computePerimeter is being called\n"; 9 | return 2 * m_nbSides * std::sin(static_cast(M_PI) / m_nbSides) * m_radius; 10 | } 11 | 12 | Pentagon::Pentagon(float radius) : RegularPolygon(5, radius) {} 13 | 14 | Hexagon::Hexagon(float radius) : RegularPolygon(6, radius) {} 15 | 16 | float Hexagon::computePerimeter() const { 17 | std::cout << "Hexagon::computePerimeter is being called\n"; 18 | return 6 * m_radius; 19 | } 20 | -------------------------------------------------------------------------------- /exercises/polymorphism/Polygons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class RegularPolygon { 4 | public: 5 | RegularPolygon(int n, float radius); 6 | float computePerimeter() const; 7 | protected: 8 | int m_nbSides; 9 | float m_radius; 10 | }; 11 | 12 | class Pentagon : public RegularPolygon { 13 | public: 14 | Pentagon(float radius); 15 | }; 16 | 17 | class Hexagon : public RegularPolygon { 18 | public: 19 | Hexagon(float radius); 20 | // 6*radius is easier than generic case 21 | float computePerimeter() const; 22 | }; 23 | -------------------------------------------------------------------------------- /exercises/polymorphism/README.md: -------------------------------------------------------------------------------- 1 | ## Instructions for the "polymorphism" exercise 2 | 3 | Step 1 4 | * look at the code 5 | * open `trypoly.cpp` 6 | * create a Pentagon, call its `perimeter` method 7 | * Compile via `make`, execute via `./trypoly` 8 | * you may have to add current directory to your LD_LIBRARY_PATH : 9 | ```shell 10 | export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:. 11 | ``` 12 | * create an Hexagon, call its `perimeter` method 13 | * recompile and check what happens 14 | 15 | Step 2 16 | * create an Hexagon, call its parent’s `perimeter` method 17 | * recompile and check again 18 | * retry with virtual methods 19 | 20 | 21 | ## Instructions for the "compiler chain" exercise 22 | 23 | * preprocess `Polygons.cpp` (`g++ -E -o output`) 24 | * compile `Polygons.o` and `trypoly.o` (`g++ -c -o output`) 25 | * use `nm` to check symbols in `.o` files 26 | * look at the `Makefile` 27 | * try `make clean; make` 28 | * see linking stage using `g++ -v` 29 | * just add a `-v` in the Makefile command for `trypoly` 30 | * run `make clean; make` 31 | * look at the collect 2 line, from the end up to `-o trypoly` 32 | * see library dependencies of `trypoly` and `Polygons.so` with `ldd` 33 | * On OS X, use `otool -L` for a similar effect 34 | -------------------------------------------------------------------------------- /exercises/polymorphism/solution/trypoly.sol.cpp: -------------------------------------------------------------------------------- 1 | #include "Polygons.hpp" 2 | #include 3 | 4 | int main() { 5 | // create a Pentagon, call its perimeter method 6 | Pentagon penta{1.0}; 7 | std::cout << "Penta : perimeter = " << penta.computePerimeter() << "\n\n"; 8 | 9 | // create a Hexagon, call its perimeter method 10 | Hexagon hexa{1.0}; 11 | std::cout << "Hexa : perimeter = " << hexa.computePerimeter() << "\n\n"; 12 | 13 | // create a Hexagon, call the perimeter method through a reference to Polygon 14 | Hexagon hexa2{1.0}; 15 | RegularPolygon &poly = hexa2; 16 | std::cout << "Hexa : perimeter = " << hexa2.computePerimeter() << '\n' 17 | << "Hexa as Poly : perimeter = " << poly.computePerimeter() << '\n'; 18 | 19 | // retry virtual method 20 | } 21 | -------------------------------------------------------------------------------- /exercises/polymorphism/trypoly.cpp: -------------------------------------------------------------------------------- 1 | #include "Polygons.hpp" 2 | #include 3 | 4 | int main() { 5 | // create a Pentagon, call its perimeter method 6 | 7 | 8 | // create a Hexagon, call its perimeter method 9 | 10 | 11 | // create a Hexagon, call the perimeter method through a reference to Polygon 12 | 13 | 14 | // retry virtual method 15 | 16 | } 17 | -------------------------------------------------------------------------------- /exercises/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( python LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Find Python for the build. 9 | find_package( Python3 COMPONENTS Development REQUIRED ) 10 | 11 | # Build the C++ shared library. 12 | add_library( mandel SHARED Complex.hpp mandel.hpp mandel.cpp ) 13 | 14 | # Build a "C wrapper" around the C++ shared library. 15 | add_library( mandelc SHARED mandel_cwrapper.hpp mandel_cwrapper.cpp ) 16 | target_link_libraries( mandelc PUBLIC mandel ) 17 | 18 | # Build the Python module around the C++ shared library. 19 | add_library( mandelm SHARED mandel_module.cpp ) 20 | target_link_libraries( mandelm PRIVATE Python3::Python mandel ) 21 | set_target_properties( mandelm PROPERTIES 22 | PREFIX "" 23 | OUTPUT_NAME "mandel" ) 24 | -------------------------------------------------------------------------------- /exercises/python/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: mandel.so libmandelc.so 3 | solution: mandel.so libmandelc.so 4 | 5 | mandel.so:mandel_module.o libmandel.so 6 | ${CXX} -shared -Wl,-undefined,dynamic_lookup $^ -o $@ `python3-config --ldflags` 7 | 8 | mandel_module.o:mandel_module.cpp 9 | ${CXX} -pthread -O3 -Wall -std=c++14 -fPIC -I. `python3-config --cflags` -c $< -o $@ 10 | 11 | libmandelc.so:mandel_cwrapper.o libmandel.so 12 | ${CXX} -shared $^ -o $@ 13 | 14 | mandel_cwrapper.o:mandel_cwrapper.cpp 15 | ${CXX} -O3 -Wall -std=c++14 -fPIC -c $< -o $@ 16 | 17 | libmandel.so:mandel.cpp Complex.hpp 18 | ${CXX} -shared -O3 -Wall -std=c++14 -fPIC $< -o $@ 19 | 20 | clean: 21 | rm -rf *.o *.so *~ *pyc *pyo *svg 22 | -------------------------------------------------------------------------------- /exercises/python/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | * look at the original python code `mandel.py` 5 | * time it (`time python3 mandel.py`) 6 | * look at the code in `mandel.hpp/cpp` 7 | * look at the python module mandel `mandel_module.cpp` 8 | * compile and modify `mandel.py` to use it 9 | * see the gain in time 10 | * look at the C wrapper in `mandel_cwrapper.cpp` 11 | * modify `mandel.py` to use `libmandelc` directly with ctypes 12 | -------------------------------------------------------------------------------- /exercises/python/mandel.cpp: -------------------------------------------------------------------------------- 1 | #include "mandel.hpp" 2 | 3 | int mandel(const Complex &a) { 4 | Complex z{0, 0}; 5 | for (int n = 1; n < 100; n++) { 6 | z = z*z + a; 7 | if (Complex{2, 0} < z) { 8 | return n; 9 | } 10 | } 11 | return -1; 12 | } 13 | -------------------------------------------------------------------------------- /exercises/python/mandel.hpp: -------------------------------------------------------------------------------- 1 | #include "Complex.hpp" 2 | 3 | /** 4 | * computes number of iterations of the mandelbrot 5 | * formula you need before reaching a norm > 2 6 | * returned value is -1 if 100 iterations are reached 7 | * without reaching it 8 | */ 9 | int mandel(const Complex &a); 10 | -------------------------------------------------------------------------------- /exercises/python/mandel.py: -------------------------------------------------------------------------------- 1 | from pylab import * 2 | from numpy import NaN 3 | 4 | def m(a): 5 | z = 0 6 | for n in range(1, 100): 7 | z = z**2 + a 8 | if abs(z) > 2: 9 | return n 10 | return NaN 11 | 12 | X = arange(-2, .5, .002) 13 | Y = arange(-1, 1, .002) 14 | Z = zeros((len(Y), len(X))) 15 | 16 | for iy, y in enumerate(Y): 17 | print (iy, "of", len(Y)) 18 | for ix, x in enumerate(X): 19 | Z[iy,ix] = m(x + 1j * y) 20 | 21 | imshow(Z, cmap = plt.cm.prism, interpolation = 'none', extent = (X.min(), X.max(), Y.min(), Y.max())) 22 | xlabel("Re(c)") 23 | ylabel("Im(c)") 24 | savefig("mandelbrot_python.svg") 25 | show() 26 | -------------------------------------------------------------------------------- /exercises/python/mandel_cwrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "mandel_cwrapper.hpp" 2 | 3 | extern "C" { 4 | 5 | int mandel(float r, float i) { 6 | return mandel(Complex(r, i)); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /exercises/python/mandel_cwrapper.hpp: -------------------------------------------------------------------------------- 1 | #include "mandel.hpp" 2 | 3 | extern "C" { 4 | int mandel(float r, float i); 5 | } 6 | -------------------------------------------------------------------------------- /exercises/python/mandel_module.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mandel.hpp" 3 | 4 | static PyObject * mandel_wrapper(PyObject * self, 5 | PyObject * args) { 6 | // Parse Input 7 | float r, i; 8 | if (!PyArg_ParseTuple(args, "ff", &r, &i)) return NULL; 9 | // Call C function 10 | int result = mandel(Complex(r, i)); 11 | // Build returned objects 12 | return PyLong_FromLong(result); 13 | } 14 | 15 | static PyMethodDef mandelMethods[] = { 16 | {"mandel", mandel_wrapper, METH_VARARGS, "computes nb of iterations for mandelbrot set for a given complex number"}, 17 | {NULL, NULL, 0, NULL} 18 | }; 19 | 20 | static struct PyModuleDef mandelModule = { 21 | PyModuleDef_HEAD_INIT, 22 | "mandel", /* name of module */ 23 | NULL, /* module documentation */ 24 | -1, /* size of per-interpreter state of the module, 25 | or -1 if the module keeps state in global variables. */ 26 | mandelMethods 27 | }; 28 | 29 | PyMODINIT_FUNC PyInit_mandel(void) { 30 | return PyModule_Create(&mandelModule); 31 | } 32 | -------------------------------------------------------------------------------- /exercises/python/solution/mandel.sol.py: -------------------------------------------------------------------------------- 1 | from pylab import * 2 | from numpy import NaN 3 | from mandel import mandel 4 | 5 | X = arange(-2, .5, .002) 6 | Y = arange(-1, 1, .002) 7 | Z = zeros((len(Y), len(X))) 8 | 9 | for iy, y in enumerate(Y): 10 | print (iy, "of", len(Y)) 11 | for ix, x in enumerate(X): 12 | v = mandel(x, y) 13 | if v >= 0 : 14 | Z[iy,ix] = v 15 | else: 16 | Z[iy,ix] = NaN 17 | 18 | imshow(Z, cmap = plt.cm.prism, interpolation = 'none', extent = (X.min(), X.max(), Y.min(), Y.max())) 19 | xlabel("Re(c)") 20 | ylabel("Im(c)") 21 | savefig("mandelbrot_python.svg") 22 | show() 23 | -------------------------------------------------------------------------------- /exercises/python/solution/mandel.solctype.py: -------------------------------------------------------------------------------- 1 | from pylab import * 2 | from numpy import NaN 3 | from ctypes import * 4 | 5 | # interface with C library 6 | libmandel = CDLL('libmandelc.so') 7 | 8 | X = arange(-2, .5, .002) 9 | Y = arange(-1, 1, .002) 10 | Z = zeros((len(Y), len(X))) 11 | 12 | for iy, y in enumerate(Y): 13 | print (iy, "of", len(Y)) 14 | for ix, x in enumerate(X): 15 | v = libmandel.mandel(c_float(x), c_float(y)) 16 | if v >= 0 : 17 | Z[iy,ix] = v 18 | else: 19 | Z[iy,ix] = NaN 20 | 21 | imshow(Z, cmap = plt.cm.prism, interpolation = 'none', extent = (X.min(), X.max(), Y.min(), Y.max())) 22 | xlabel("Re(c)") 23 | ylabel("Im(c)") 24 | savefig("mandelbrot_python.svg") 25 | show() 26 | -------------------------------------------------------------------------------- /exercises/race/.gitignore: -------------------------------------------------------------------------------- 1 | racing 2 | racing.sol1 3 | racing.sol2 4 | -------------------------------------------------------------------------------- /exercises/race/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( race LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Figure out how to use the platform's thread capabilities. 9 | find_package( Threads REQUIRED ) 10 | 11 | # Create the user's executable. 12 | add_executable( racing "racing.cpp" ) 13 | target_link_libraries( racing PRIVATE Threads::Threads ) 14 | 15 | # Create the "solution executable". 16 | add_executable( racing.sol1 EXCLUDE_FROM_ALL "solution/racing.sol1.cpp" ) 17 | target_link_libraries( racing.sol1 PRIVATE Threads::Threads ) 18 | add_custom_target( solution1 ) 19 | add_dependencies( solution1 racing.sol1 ) 20 | add_executable( racing.sol2 EXCLUDE_FROM_ALL "solution/racing.sol2.cpp" ) 21 | target_link_libraries( racing.sol2 PRIVATE Threads::Threads ) 22 | add_custom_target( solution2 ) 23 | add_dependencies( solution2 racing.sol2 ) 24 | add_dependencies( solution solution1 solution2 ) 25 | -------------------------------------------------------------------------------- /exercises/race/Makefile: -------------------------------------------------------------------------------- 1 | PROGRAM_NAME=racing 2 | 3 | all: $(PROGRAM_NAME) 4 | solution: $(PROGRAM_NAME).sol1 $(PROGRAM_NAME).sol2 5 | 6 | 7 | clean: 8 | rm -f *o $(PROGRAM_NAME) *~ core $(PROGRAM_NAME).sol? 9 | 10 | $(PROGRAM_NAME) : $(PROGRAM_NAME).cpp 11 | ${CXX} ${CXXFLAGS} -g -std=c++17 -O2 -pthread -Wall -Wextra -L. -o $@ $< 12 | 13 | $(PROGRAM_NAME).sol1 : solution/$(PROGRAM_NAME).sol1.cpp 14 | ${CXX} ${CXXFLAGS} -g -std=c++17 -O2 -pthread -Wall -Wextra -L. -o $@ $< 15 | 16 | $(PROGRAM_NAME).sol2 : solution/$(PROGRAM_NAME).sol2.cpp 17 | ${CXX} ${CXXFLAGS} -g -std=c++17 -O2 -pthread -Wall -Wextra -L. -o $@ $< 18 | -------------------------------------------------------------------------------- /exercises/race/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | The program `racing.cpp` is incrementing a shared integer many times, within several threads, which should lead to race conditions if no specific protection is used. The values of the global parameters `nThread`, `nInc` and `nRepeat` can be custommized for your own computer. 5 | 6 | Tasks 7 | - Compile and run the executable, check it races. 8 | - If you have a bash shell, try `./run ./racing`, which keeps invoking the executable until a race condition is detected. 9 | - (Optional) You can use `valgrind --tool=helgrind ./racing` to prove your assumption 10 | - (Optional) If your operating system supports it, recompile with thread sanitizer. 11 | With Makefile, use e.g. `make CXXFLAGS="-fsanitize=thread"` 12 | - Use a `std::mutex` to fix the issue. 13 | - See the difference in execution time, for example with `time ./racing`. 14 | You might have to increase `nRepeat` if it completes too fast, or lower it if it takes too long. 15 | - (Optional) Check again with `valgrind` or thread sanitizer if the problem is fixed. 16 | - Try to use `std::atomic` instead of the mutex, and compare the execution time. 17 | -------------------------------------------------------------------------------- /exercises/race/racing.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | /* 7 | * This program tries to increment an integer `nInc` times in `nThread` threads. 8 | * If the result comes out at `nInc*nThread`, it stays silent, but it will print 9 | * an error if a race condition is detected. 10 | * If you don't see it racing, try ./run ./racing, which keeps invoking the 11 | * executable until a race condition is detected. 12 | */ 13 | 14 | constexpr std::size_t nThread = 10; 15 | constexpr std::size_t nInc = 1000; 16 | constexpr std::size_t nRepeat = 1000; 17 | 18 | int main() { 19 | int nError = 0; 20 | 21 | for (std::size_t j = 0; j < nRepeat; j++) { 22 | int a = 0; 23 | 24 | // Increment the variable a 100 times: 25 | auto increment = [&a](){ 26 | for (std::size_t i = 0; i < nInc; ++i) { 27 | a++; 28 | } 29 | }; 30 | 31 | // Start up all threads 32 | std::vector threads; 33 | for (std::size_t i = 0; i < nThread; ++i) threads.emplace_back(increment); 34 | for (auto & thread : threads) thread.join(); 35 | 36 | // Check 37 | if (a != nThread * nInc) { 38 | std::cerr << "Race detected! Result: " << a << '\n'; 39 | nError++; 40 | } 41 | } 42 | 43 | return nError; 44 | } 45 | -------------------------------------------------------------------------------- /exercises/race/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROGRAM="./racing" 4 | if [ $# -ge 1 ]; then 5 | PROGRAM=$1 6 | fi 7 | 8 | while true; do 9 | $PROGRAM || break; 10 | done 11 | -------------------------------------------------------------------------------- /exercises/race/solution/racing.sol1.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | constexpr std::size_t nThread = 10; 8 | constexpr std::size_t nInc = 1000; 9 | constexpr std::size_t nRepeat = 1000; 10 | 11 | int main() { 12 | int nError = 0; 13 | 14 | for (std::size_t j = 0; j < nRepeat; j++) { 15 | int a = 0; 16 | std::mutex aMutex; 17 | 18 | // Increment the variable a 100 times: 19 | auto increment = [&a,&aMutex](){ 20 | for (std::size_t i = 0; i < nInc; ++i) { 21 | std::scoped_lock lock{aMutex}; 22 | a++; 23 | } 24 | }; 25 | 26 | // Start up all threads: 27 | std::vector threads; 28 | for (std::size_t i = 0; i < nThread; ++i) threads.emplace_back(increment); 29 | for (auto & thread : threads) thread.join(); 30 | 31 | // Check 32 | if (a != nThread * nInc) { 33 | std::cerr << "Race detected! Result: " << a << '\n'; 34 | nError++; 35 | } 36 | } 37 | 38 | return nError; 39 | } 40 | -------------------------------------------------------------------------------- /exercises/race/solution/racing.sol2.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | constexpr std::size_t nThread = 10; 8 | constexpr std::size_t nInc = 1000; 9 | constexpr std::size_t nRepeat = 1000; 10 | 11 | int main() { 12 | int nError = 0; 13 | 14 | for (std::size_t j = 0; j < nRepeat; j++) { 15 | std::atomic a{0}; 16 | 17 | // Increment the variable a 100 times: 18 | auto increment = [&a](){ 19 | for (std::size_t i = 0; i < nInc; ++i) { 20 | a++; 21 | } 22 | }; 23 | 24 | // Start up all threads 25 | std::vector threads; 26 | for (std::size_t i = 0; i < nThread; ++i) threads.emplace_back(increment); 27 | for (auto & thread : threads) thread.join(); 28 | 29 | // Check 30 | if (a != nThread * nInc) { 31 | std::cerr << "Race detected! Result: " << a << '\n'; 32 | nError++; 33 | } 34 | } 35 | 36 | return nError; 37 | } 38 | -------------------------------------------------------------------------------- /exercises/smartPointers/.gitignore: -------------------------------------------------------------------------------- 1 | problem1 2 | problem2 3 | problem3 4 | problem4 5 | problem5 6 | problem1.sol 7 | problem2.sol 8 | problem3.sol 9 | problem4.sol 10 | problem5.sol 11 | -------------------------------------------------------------------------------- /exercises/smartPointers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( smartPointers LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | set( CMAKE_CXX_STANDARD 20 ) 8 | 9 | # Create the user's executable. 10 | add_executable( problem1 "problem1.cpp" ) 11 | add_executable( problem2 "problem2.cpp" ) 12 | add_executable( problem3 "problem3.cpp" ) 13 | add_executable( problem4 "problem4.cpp" ) 14 | add_executable( problem5 "problem5.cpp" ) 15 | 16 | # Create the "solution executables". 17 | add_executable( problem1.sol EXCLUDE_FROM_ALL "solution/problem1.sol.cpp" ) 18 | add_executable( problem2.sol EXCLUDE_FROM_ALL "solution/problem2.sol.cpp" ) 19 | add_executable( problem3.sol EXCLUDE_FROM_ALL "solution/problem3.sol.cpp" ) 20 | add_executable( problem4.sol EXCLUDE_FROM_ALL "solution/problem4.sol.cpp" ) 21 | add_executable( problem5.sol EXCLUDE_FROM_ALL "solution/problem5.sol.cpp" ) 22 | add_dependencies( solution problem1.sol problem2.sol problem3.sol problem4.sol problem5.sol ) 23 | -------------------------------------------------------------------------------- /exercises/smartPointers/Makefile: -------------------------------------------------------------------------------- 1 | all: problem1 problem2 problem3 problem4 problem5 2 | solution: problem1.sol problem2.sol problem3.sol problem4.sol problem5.sol 3 | 4 | clean: 5 | rm -f *o *so *~ problem? problem?.sol 6 | 7 | % : %.cpp 8 | $(CXX) -g -std=c++20 -Wall -Wextra -o $@ $< 9 | 10 | %.sol : solution/%.sol.cpp 11 | $(CXX) -g -std=c++20 -Wall -Wextra -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/smartPointers/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Writing leak-free and fault-free C++ 3 | 4 | Here we have five code snippets that will benefit from using smart pointers. By replacing every explicit `new` with `make_unique` or `make_shared`, (alternatively by explicitly instantiating smart pointers) we will fix memory leaks, segmentation faults, and make most cleanup code unnecessary. 5 | 6 | ## Prerequisites 7 | 8 | * Do you know which kind of pointer is used for what? 9 | * Raw pointer 10 | * [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr) 11 | * [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr) 12 | * C++-14 for `std::make_unique` / `std::make_shared`. Understand what these functions do. 13 | * Helpful: Move semantics for `problem2()`, but one can do without. 14 | 15 | ## Instructions 16 | 17 | * In the **essentials course**, work on `problem1` and `problem2`, and fix the leaks using smart pointers. 18 | * In the **advanced course**, work on `problem1` to `problem5`. Skip `problem4` and `problem5` if you don't have enough time. 19 | * Dedicated instructions are given in each cpp file. 20 | * Each one is written so that you easily check if the problem is solved or not. 21 | * If seen in course before, you are also advised to try external tools such as valgrind: 22 | ``` 23 | valgrind --leak-check=full --track-origins=yes ./problem1 24 | ``` 25 | -------------------------------------------------------------------------------- /exercises/smartPointers/problem1.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | 6 | 7 | /* -------------------------------------------------------------------------------------------- 8 | * Unique ownership. 9 | * 10 | * Always use smart pointers instead of `new`. A frequent source of memory leaks is a function 11 | * that terminates in an unexpected way. 12 | * 13 | * Tasks 14 | * 1) Compile and run the code below. Notice that the final count is `1`, 15 | * showing that the instance of LargeObject has not been deallocated. 16 | * 2) Modify `doStuff()` (only) so to use a `std::unique_ptr` instead of a raw pointer. 17 | * The final count should be `0`, and the memory leak solved. 18 | * -------------------------------------------------------------------------------------------- 19 | */ 20 | 21 | 22 | // The class LargeObject emulates a large object. 23 | // One should avoid to copy it, and rather use 24 | // a pointer to pass it around. 25 | 26 | struct LargeObject { 27 | 28 | std::array data ; 29 | 30 | // So to check for some potential memory leak, 31 | // we count the constructions and destructions 32 | inline static std::size_t count = 0; 33 | LargeObject() { count++ ; } 34 | ~LargeObject() { count-- ; } 35 | 36 | } ; 37 | 38 | // A function to do something with a large object. 39 | // Here we simulate that an error happens. 40 | 41 | void changeLargeObject( LargeObject & object ) { 42 | 43 | object.data[0] = 1. ; 44 | throw std::invalid_argument("Error when changing object data.") ; 45 | 46 | } 47 | 48 | // Often, data are owned by one entity, and merely used by others. 49 | // In this case, we hand the data to changeLargeObject(), 50 | // and unfortunately, something goes wrong... 51 | 52 | void doStuff() { 53 | 54 | // MAKE YOUR CHANGES IN THIS FUNCTION 55 | 56 | auto obj = new LargeObject ; 57 | changeLargeObject(*obj) ; 58 | delete obj ; 59 | 60 | } 61 | 62 | int main() { 63 | 64 | try { 65 | doStuff() ; 66 | } catch ( const std::exception & e ) { 67 | std::cerr<< "Terminated with exception: " << e.what() << "\n" ; 68 | } 69 | 70 | std::cout<<"Leaked large objects: "< 3 | #include 4 | #include 5 | 6 | struct LargeObject { 7 | 8 | std::array data ; 9 | 10 | // So to check for some potential memory leak, 11 | // we count the constructions and destructions 12 | inline static std::size_t count = 0 ; 13 | LargeObject() { count++ ; } 14 | ~LargeObject() { count-- ; } 15 | 16 | } ; 17 | 18 | void changeLargeObject( LargeObject & object ) { 19 | 20 | object.data[0] = 1. ; 21 | throw std::invalid_argument("Error when changing object data.") ; 22 | 23 | } 24 | 25 | void doStuff() { 26 | 27 | auto obj = std::make_unique() ; 28 | changeLargeObject(*obj) ; 29 | 30 | } 31 | 32 | int main() { 33 | 34 | try { 35 | doStuff() ; 36 | } catch ( const std::exception & e ) { 37 | std::cerr<< "Terminated with exception: " << e.what() << "\n" ; 38 | } 39 | 40 | std::cout<<"Leaked large objects: "< 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | struct LargeObject { 9 | 10 | std::array data ; 11 | 12 | // So to check for some potential memory leak, 13 | // we count the constructions and destructions 14 | inline static std::size_t count = 0 ; 15 | LargeObject() { count++ ; } 16 | ~LargeObject() { count-- ; } 17 | 18 | } ; 19 | 20 | std::unique_ptr newLargeObject() { 21 | 22 | auto object = std::make_unique() ; 23 | // Imagine there is more setup steps of "object" here 24 | // ... 25 | return object ; 26 | } 27 | 28 | void changeLargeObject( LargeObject & object ) { 29 | 30 | object.data[0] = 1. ; 31 | 32 | } 33 | 34 | void doStuff() { 35 | 36 | std::vector> largeObjects ; 37 | 38 | for ( unsigned int i = 0 ; i < 10 ; ++i ) { 39 | largeObjects.push_back(newLargeObject()); 40 | // ... additional largeObjects.back() setup ... 41 | 42 | // Alternatively, when the object is ready, 43 | // one can "give up" newObj 44 | // by moving it into the vector. 45 | // auto newObj = createLargeObject() ; 46 | // ... additional newObj setup ... 47 | // largeObjects.push_back(std::move(newObj)); 48 | 49 | } 50 | 51 | for (const auto& obj : largeObjects) { 52 | changeLargeObject(*obj); 53 | } 54 | } 55 | 56 | int main() { 57 | doStuff() ; 58 | std::cout<<"Leaked large objects: "< 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct LargeObject { 10 | 11 | std::array data ; 12 | 13 | // So to check for some potential memory leak, 14 | // we count the constructions and destructions 15 | inline static std::size_t count = 0 ; 16 | LargeObject() { count++ ; } 17 | ~LargeObject() { count-- ; } 18 | 19 | } ; 20 | 21 | void removeRandom(std::vector>& collection, std::default_random_engine & engine) { 22 | 23 | auto pos = collection.begin() + engine() % collection.size(); 24 | collection.erase(pos); 25 | 26 | } 27 | 28 | void changeLargeObject( LargeObject & object ) { 29 | 30 | object.data[0] = 1. ; 31 | 32 | } 33 | 34 | void doStuff() { 35 | 36 | // Prepare a non deterministic random engine 37 | 38 | std::random_device device ; 39 | std::default_random_engine engine(device()) ; 40 | 41 | // Original collection 42 | 43 | std::vector> objVector(10); 44 | for ( auto & ptr : objVector ) { 45 | ptr = std::make_shared(); 46 | } 47 | 48 | // Less copies : 49 | // std::vector> objVector ; 50 | // objVector.reserve(10); 51 | // for ( unsigned int i = 0 ; i < 10 ; ++i ) { 52 | // objVector.emplace_back(new LargeObject()) ; 53 | // } 54 | 55 | // Let's copy the whole collection 56 | 57 | auto objVectorCopy(objVector); 58 | 59 | // Random work with the objects 60 | 61 | removeRandom(objVector,engine); 62 | removeRandom(objVectorCopy,engine); 63 | removeRandom(objVectorCopy,engine); 64 | // ... 65 | // ... 66 | for ( auto const & objPtr : objVector ) { 67 | changeLargeObject(*objPtr) ; 68 | } 69 | 70 | } 71 | 72 | int main() { 73 | 74 | doStuff() ; 75 | std::cout<<"Leaked large objects: "< 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | struct LargeObject { 10 | 11 | std::array data ; 12 | 13 | } ; 14 | 15 | class Owner { 16 | 17 | public: 18 | 19 | Owner() : _largeObject( new LargeObject() ) {} 20 | LargeObject * getLargeObject() const { return _largeObject.get() ; } 21 | 22 | private: 23 | 24 | std::shared_ptr _largeObject ; 25 | 26 | } ; 27 | 28 | void doStuff() { 29 | 30 | std::vector owners ; 31 | 32 | for ( int i = 0 ; i < 5 ; ++i ) { 33 | Owner owner ; 34 | // ... additional owner setup ... 35 | owners.push_back(owner) ; 36 | } 37 | } 38 | 39 | int main() { 40 | 41 | doStuff() ; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /exercises/smartPointers/solution/problem5.sol.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | struct LargeObject { 8 | 9 | std::array data ; 10 | 11 | } ; 12 | 13 | class Owner { 14 | 15 | public: 16 | 17 | Owner() : _largeObject( new LargeObject() ) {} 18 | auto getLargeObject() const { return _largeObject ; } 19 | 20 | private: 21 | 22 | std::shared_ptr _largeObject ; 23 | 24 | } ; 25 | 26 | class Observer { 27 | 28 | public: 29 | 30 | Observer( const Owner & owner ) : _largeObject(owner.getLargeObject()) {} 31 | 32 | void setValue( double v ) { 33 | std::shared_ptr wptr = _largeObject.lock(); 34 | if (wptr) { wptr->data[0] = v ; } 35 | else { wptr->data[0] = 0. ; } 36 | } 37 | 38 | double getValue() const { 39 | std::shared_ptr wptr = _largeObject.lock(); 40 | if (wptr) { return wptr->data[0] ; } 41 | else { return -1. ; } 42 | } 43 | 44 | private: 45 | 46 | std::weak_ptr _largeObject ; 47 | 48 | } ; 49 | 50 | void doStuff() { 51 | 52 | // Owners and observers 53 | 54 | std::vector owners(5) ; 55 | std::vector observers ; 56 | for ( auto & owner : owners ) { 57 | observers.emplace_back(owner) ; 58 | } 59 | 60 | // Write through observers 61 | 62 | for ( auto & observer : observers ) { 63 | observer.setValue(1.) ; 64 | } 65 | 66 | // Let's destroy the 2 last owners 67 | 68 | owners.resize(3) ; 69 | 70 | // Read through observers 71 | 72 | std::cout << "Values:"; 73 | for ( auto const & observer : observers ) { 74 | std::cout<<" "< 2 | 3 | Here we explore: 4 | - how <=> are == differing. 5 | - when to use or not to use the compiler default implementations. 6 | 7 | ## Prerequisites 8 | 9 | * Being able to overload an operator, especially `==` and `<=>`. 10 | 11 | ## Instructions 12 | 13 | 0. Compile and run the program. We are not fully happy with the default implementation of `<=>` for our `Complex` class, which considers 1|2 as smaller than 1.5|1.5. 14 | 1. Modify it so that the ordering is based on the norm of the complexes. 15 | 2. Because you do not use any more the default implementation of `<=>` you had to provide also an implementation for `==`. What happens if you deduce it from `<=>` (using `(((*this)<=>other)==0)`) ? 16 | 3. Try to restore the default implementation for `==` only. 17 | -------------------------------------------------------------------------------- /exercises/spaceship/solution/spaceship.sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct Complex 6 | { 7 | double r, i ; 8 | double norm() const { return std::sqrt(r*r+i*i) ; } 9 | auto operator<=>( Complex const & other ) const 10 | { return (norm()<=>other.norm()) ; } 11 | bool operator==( Complex const & other ) const = default ; 12 | } ; 13 | 14 | std::ostream & operator<<( std::ostream & os, std::partial_ordering cmp ) 15 | { return (os<<'<'<<(cmp<0)<<'|'<<(cmp==0)<<'|'<<(cmp>0)<<'>') ; } 16 | 17 | std::ostream & operator<<( std::ostream & os, Complex const & c ) 18 | { return (os< 21 | void compare( T lhs, T rhs ) 22 | { 23 | std::cout< "<rhs)<({ 1., 2. },{ 1.5, 1.5 }) ; 31 | compare({ 1., 0. },{ 0., 1. }) ; 32 | std::cout< 2 | #include 3 | #include 4 | 5 | /* 6 | 7 | We are not fully happy with the default implementation of `<=>`for our `Complex` class below. 8 | 1. Modify it so that the ordering is based on the norm of the complexes. 9 | 2. Because you do not use any more the default implementation of `<=>` you had to provide also an implementation for `==`. What happens if you deduce it from `<=>` (using `(((*this)<=>other)==0)`) ? 10 | 3. Try to restore the default implementation for `==` only. 11 | 12 | */ 13 | 14 | struct Complex 15 | { 16 | double r, i ; 17 | double norm() const { return std::sqrt(r*r+i*i) ; } 18 | auto operator<=>( Complex const & other ) const = default ; 19 | } ; 20 | 21 | std::ostream & operator<<( std::ostream & os, std::partial_ordering cmp ) 22 | { return (os<<'<'<<(cmp<0)<<'|'<<(cmp==0)<<'|'<<(cmp>0)<<'>') ; } 23 | 24 | std::ostream & operator<<( std::ostream & os, Complex const & c ) 25 | { return (os< 28 | void compare( T lhs, T rhs ) 29 | { 30 | std::cout< "<rhs)<({ 1., 2. },{ 1.5, 1.5 }) ; 38 | compare({ 1., 0. },{ 0., 1. }) ; 39 | std::cout< 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Complex.hpp" 7 | 8 | template 9 | void compute(int len, T initial, T step) { 10 | // allocate vectors 11 | std::vector v(len+1), diffs(len+1); 12 | 13 | // fill and randomize v 14 | std::generate(, , [](...) { ...; }); 15 | std::shuffle(..., std::default_random_engine{}); 16 | 17 | // compute differences 18 | std::adjacent_difference(...); 19 | 20 | // compute standard deviation of all differences 21 | const T sum = std::reduce(...); 22 | const T sumsq = std::accumulate(..., [](...) { ...; }); 23 | const T mean = sum/len; 24 | const T variance = sumsq/len - mean*mean; 25 | 26 | std::cout << "Range = [" << initial << ", " << step*len << "]\n" 27 | << "Mean = " << mean << '\n' 28 | << "Variance = " << variance << '\n'; 29 | } 30 | 31 | int main() { 32 | compute(1000, 0.0, 7.0); 33 | // call compute here with Complex 34 | } 35 | -------------------------------------------------------------------------------- /exercises/stl/randomize.nostl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | constexpr auto LEN = 1000; 6 | constexpr auto STEP = 7; 7 | 8 | void randomize(int* v, unsigned int len) { 9 | // we randomize via len random inversions 10 | for (unsigned int i = 0; i < len; i++) { 11 | int a = rand()%len; 12 | int b = rand()%len; 13 | int mem = v[a]; 14 | v[a] = v[b]; 15 | v[b] = mem; 16 | } 17 | } 18 | 19 | int main() { 20 | // create vector 21 | int *v = new int[LEN+1]; 22 | for (unsigned int i = 0; i <= LEN; i++) v[i] = i*STEP; 23 | 24 | // randomize it 25 | randomize(v, LEN+1); 26 | 27 | // compute diffs 28 | int *diffs = new int[LEN]; 29 | for (unsigned int i = 0; i < LEN; i++) 30 | diffs[i] = v[i+1] - v[i]; 31 | 32 | // compute standard deviation of it 33 | float sum = 0; 34 | float sumsq = 0; 35 | for (unsigned int i = 0; i < LEN; i ++) { 36 | sum += diffs[i]; 37 | sumsq += diffs[i]*diffs[i]; 38 | } 39 | float mean = sum/LEN; 40 | float stddev = std::sqrt(sumsq/LEN - mean*mean) ; 41 | std::cout << "Range = [0, " << STEP*LEN << "]\n" 42 | << "Mean = " << mean 43 | << "\nStdDev = " << stddev << '\n'; 44 | 45 | delete[] v; 46 | delete[] diffs; 47 | } 48 | -------------------------------------------------------------------------------- /exercises/stl/solution/randomize.sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Complex.hpp" 7 | 8 | template 9 | struct Generator { 10 | T m_value, m_step; 11 | Generator(T initial, T step):m_value(initial), m_step(step){}; 12 | T operator()() { 13 | T cur_value = m_value; 14 | m_value += m_step; 15 | return cur_value; 16 | } 17 | }; 18 | 19 | template 20 | struct sumsquare { 21 | T operator()(const T& s, const T& a) { return s + a * a; }; 22 | }; 23 | 24 | template 25 | void compute(int len, T initial, T step) { 26 | // allocate vectors 27 | std::vector v(len+1), diffs(len+1); 28 | 29 | // fill and randomize v 30 | std::generate(v.begin(), v.end(), Generator(initial, step)); 31 | // Alternatively: 32 | // std::generate(v.begin(), v.end(), [value = initial, step]() mutable { 33 | // const T cur = value; 34 | // value += step; 35 | // return cur; 36 | // }); 37 | 38 | std::shuffle(v.begin(), v.end(), std::default_random_engine{}); 39 | 40 | // compute differences 41 | std::adjacent_difference(v.begin(), v.end(), diffs.begin()); 42 | 43 | // compute standard deviation of all differences. 44 | // Note that the first element is just the original element itself, so we need to skip it. 45 | const T sum = std::reduce(diffs.begin()+1, diffs.end()); 46 | const T sumsq = std::accumulate(diffs.begin()+1, diffs.end(), T(), sumsquare()); 47 | // Alternatively: 48 | // const T sumsq = std::accumulate(diffs.begin()+1, diffs.end(), T(), 49 | // [](const T& s, const T& a) { return s + a * a; }); 50 | const T mean = sum/len; 51 | const T variance = sumsq/len - mean*mean; 52 | 53 | std::cout << "Range = [" << initial << ", " << step*len << "]\n" 54 | << "Mean = " << mean << '\n' 55 | << "Variance = " << variance << '\n'; 56 | } 57 | 58 | int main() { 59 | compute(1000, 0.0, 7.0); 60 | compute(1000, Complex(0,0), Complex(1,2)); 61 | } 62 | -------------------------------------------------------------------------------- /exercises/templates/.gitignore: -------------------------------------------------------------------------------- 1 | playwithsort 2 | playwithsort.sol 3 | -------------------------------------------------------------------------------- /exercises/templates/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( templates LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | set(CMAKE_CXX_STANDARD 20) 8 | 9 | # Create the user's executable. 10 | add_executable( playwithsort "Complex.hpp" "OrderedVector.hpp" "playwithsort.cpp" ) 11 | 12 | # Create the "solution executable". 13 | add_executable( playwithsort.sol EXCLUDE_FROM_ALL 14 | "Complex.hpp" "solution/OrderedVector.sol.hpp" "solution/playwithsort.sol.cpp" ) 15 | target_include_directories( playwithsort.sol PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" ) 16 | add_dependencies( solution playwithsort.sol ) 17 | -------------------------------------------------------------------------------- /exercises/templates/Complex.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | class Complex_t { 5 | public: 6 | Complex_t() = default; 7 | 8 | Complex_t(T r, T i) 9 | : m_r(r), m_i(i) {} 10 | 11 | T real() const { return m_r; } 12 | 13 | T imaginary() const { return m_i; } 14 | 15 | Complex_t operator+(const Complex_t& other) const { 16 | return Complex_t(m_r + other.m_r, m_i + other.m_i); 17 | } 18 | 19 | Complex_t operator-(const Complex_t& other) const { 20 | return Complex_t(m_r - other.m_r, m_i - other.m_i); 21 | } 22 | 23 | Complex_t operator*(const Complex_t& other) const { 24 | return Complex_t(m_r * other.m_r - m_i * other.m_i, 25 | m_r * other.m_i + m_i * other.m_r); 26 | } 27 | 28 | Complex_t operator*(const T factor) const { 29 | return Complex_t(m_r * factor, m_i * factor); 30 | } 31 | 32 | Complex_t operator/(const T dividend) const { 33 | return Complex_t(m_r / dividend, m_i / dividend); 34 | } 35 | 36 | Complex_t& operator+=(const Complex_t& other) { 37 | m_r += other.m_r; 38 | m_i += other.m_i; 39 | return *this; 40 | } 41 | 42 | friend bool operator<(const Complex_t& a, const Complex_t& b) { 43 | return (a.m_r * a.m_r + a.m_i * a.m_i) < (b.m_r * b.m_r + b.m_i * b.m_i); 44 | } 45 | 46 | friend std::ostream& operator<<(std::ostream& os, const Complex_t& c) { 47 | return os << "(" << c.real() << ", " << c.imaginary() << ")"; 48 | } 49 | 50 | private: 51 | T m_r{}, m_i{}; 52 | }; 53 | 54 | using Complex = Complex_t<>; 55 | -------------------------------------------------------------------------------- /exercises/templates/Makefile: -------------------------------------------------------------------------------- 1 | all: playwithsort 2 | solution: playwithsort.sol 3 | 4 | clean: 5 | rm -f *o *so playwithsort *~ playwithsort.sol 6 | 7 | playwithsort : playwithsort.cpp OrderedVector.hpp Complex.hpp 8 | $(CXX) -std=c++20 -g -O0 -Wall -Wextra -o $@ $< 9 | 10 | playwithsort.sol : solution/playwithsort.sol.cpp solution/OrderedVector.sol.hpp Complex.hpp 11 | $(CXX) -std=c++20 -g -O0 -Wall -Wextra -I. -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/templates/OrderedVector.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | class OrderedVector { 6 | public: 7 | OrderedVector(unsigned int maxLen) 8 | : m_maxLen(maxLen), m_data(std::make_unique(m_maxLen)) { } 9 | 10 | OrderedVector(const OrderedVector&) = delete; 11 | OrderedVector& operator=(const OrderedVector&) = delete; 12 | 13 | bool add(ElementType value); 14 | 15 | ElementType& at(unsigned int n) { 16 | if (n >= m_len) { 17 | throw std::out_of_range("too big"); 18 | } 19 | return m_data[n]; 20 | } 21 | 22 | ElementType& operator[](unsigned int n) { 23 | return at(n); 24 | } 25 | 26 | private: 27 | unsigned int m_len = 0; 28 | unsigned int m_maxLen; 29 | std::unique_ptr m_data; 30 | }; 31 | 32 | template 33 | bool OrderedVector::add(ElementType value) { 34 | if (m_len >= m_maxLen) { 35 | return false; 36 | } 37 | // find insertion point 38 | unsigned int insertIndex = 0; 39 | while (insertIndex < m_len && m_data[insertIndex] < value) 40 | insertIndex++; 41 | // move end of vector 42 | unsigned int index = m_len; 43 | while (index > insertIndex) { 44 | m_data[index] = m_data[index-1]; 45 | index--; 46 | } 47 | // actual insertion 48 | m_data[index] = value; 49 | m_len++; 50 | return true; 51 | } 52 | -------------------------------------------------------------------------------- /exercises/templates/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | Beginners 5 | * Look at the `OrderedVector` code 6 | * Compile and run `playwithsort.cpp`. See the printed ordering 7 | * Inspect the `Complex_t` class template and the alias `Complex`. 8 | Then, in `playwithsort.cpp`, create a third `OrderedVector` with `Complex` as element type, 9 | fill it with some complex values and print the vector. 10 | No modifications to `Complex_t` and `OrderedVector` are required! 11 | 12 | Intermediary 13 | * Extend `OrderedVector` to allow to customize the ordering via an additional template parameter. 14 | This template parameter should be a comparison object that defaults to `std::less`. 15 | Hint: 16 | You have to customize the loop in OrderedVector::add where the insertion point is defined. 17 | * Try ordering by reversed strings (from the last letter, don't change the strings!) 18 | * Test order based on [Manhattan distance](https://en.wikipedia.org/wiki/Taxicab_geometry) with complex type 19 | 20 | Bonus 21 | * Check the implementation of `Complex` 22 | * Try ordering complex of complex 23 | -------------------------------------------------------------------------------- /exercises/templates/playwithsort.cpp: -------------------------------------------------------------------------------- 1 | #include "OrderedVector.hpp" 2 | #include "Complex.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct ReverseStringLess { 9 | bool operator() (const std::string &s, const std::string &t) const { 10 | // TODO: compare reversed strings 11 | // hint: you can use: 12 | // - std::views::reverse 13 | // - std::ranges::lexicographical_compare 14 | // or, if your compiler does not support those yet, 15 | // take copies of the strings and reverse them using std::reverse 16 | return s < t; 17 | } 18 | }; 19 | 20 | int main() { 21 | std::cout << "Integer\n"; 22 | OrderedVector v(10); 23 | for (int i = 10; i > 0; i--) 24 | v.add(i); 25 | for (int i = 0; i < 10; i++) 26 | std::cout << v[i] << " "; 27 | std::cout << "\n\n"; 28 | 29 | std::cout << "String\n"; 30 | OrderedVector vs(5); 31 | vs.add(std::string("one")); 32 | vs.add(std::string("two")); 33 | vs.add(std::string("three")); 34 | vs.add(std::string("four")); 35 | vs.add(std::string("five")); 36 | for (int i = 0; i < 5; i++) 37 | std::cout << vs[i] << " "; 38 | std::cout << "\n\n"; 39 | 40 | // TODO: Demonstrate OrderedVector with Complex as element type similar to above 41 | 42 | 43 | // TODO: Extend OrderedVector to allow to customize the ordering via an additional template paramter. 44 | // Then, demonstrate the new functionality by ordering an OrderedVector, 45 | // where the strings are compared starting at their last letters. 46 | 47 | 48 | // TODO: Order an OrderedVector of Complex based on the Manhattan distance 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /exercises/templates/solution/OrderedVector.sol.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template> 6 | class OrderedVector { 7 | public: 8 | OrderedVector(unsigned int maxLen) 9 | : m_maxLen(maxLen), m_data(std::make_unique(m_maxLen)) { } 10 | 11 | OrderedVector(const OrderedVector&) = delete; 12 | OrderedVector& operator=(const OrderedVector&) = delete; 13 | 14 | bool add(ElementType value); 15 | 16 | ElementType& at(unsigned int n) { 17 | if (n >= m_len) { 18 | throw std::out_of_range("too big"); 19 | } 20 | return m_data[n]; 21 | } 22 | 23 | ElementType& operator[](unsigned int n) { 24 | return at(n); 25 | } 26 | 27 | private: 28 | unsigned int m_len = 0; 29 | unsigned int m_maxLen; 30 | Compare m_compare; 31 | std::unique_ptr m_data; 32 | }; 33 | 34 | template 35 | bool OrderedVector::add(ElementType value) { 36 | if (m_len >= m_maxLen) { 37 | return false; 38 | } 39 | // find insertion point 40 | unsigned int insertIndex = 0; 41 | while (insertIndex < m_len && m_compare(m_data[insertIndex], value)) 42 | insertIndex++; 43 | // move end of vector 44 | unsigned int index = m_len; 45 | while (index > insertIndex) { 46 | m_data[index] = m_data[index-1]; 47 | index--; 48 | } 49 | // actual insertion 50 | m_data[index] = value; 51 | m_len++; 52 | return true; 53 | } 54 | -------------------------------------------------------------------------------- /exercises/ubsan/README.md: -------------------------------------------------------------------------------- 1 | # Undefined Behaviour Sanitizer 2 | 3 | This directory contains a program that's full of evil bugs. Many of those would cause compiler warnings, 4 | but the problems can easily be obscured by making the code slightly more complicated, e.g. by passing 5 | pointers or indices through functions or similar. 6 | 7 | UBSan is a run-time tool, so it can catch these bugs even if the compilers don't emit warnings. 8 | This should illustrate why every larger project should have a UBSan and a ASan build as part of 9 | it Continuous Integration. 10 | 11 | ## Instructions 12 | 13 | - Compile and run this program using a compiler invocation like 14 | ` -g -std=c++17 -o undefinedBehaviour undefinedBehaviour.cpp` 15 | 16 | - You might see warnings depending on the compiler, but on most platforms the program runs without observable issues. 17 | 18 | - Recompile with "-fsanitize=undefined", and observe that almost every second line contains a serious bug. 19 | NOTE: If this fails, your compiler does not support UBSan (yet). In this case, you can try to read the 20 | program and catch the bugs, or continue with the next exercise. 21 | 22 | - Try to understand what's wrong. The solution contains a few comments what kind of bugs are hidden in the program. 23 | -------------------------------------------------------------------------------- /exercises/ubsan/solution/undefinedBehaviour.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // You don't need to change any code in these structs: 5 | struct Base { 6 | virtual void print() = 0; 7 | virtual ~Base() {} 8 | }; 9 | struct Derived1 : public Base { 10 | void print() override { 11 | std::cout << "Derived1::print()\n"; 12 | } 13 | }; 14 | struct Derived2 : public Base { 15 | void print() override { 16 | std::cout << "Derived2::print()\n"; 17 | } 18 | }; 19 | 20 | 21 | /** 22 | * *************** 23 | * Instructions: 24 | * *************** 25 | * 26 | * Compile and run this program using a compiler invocation like 27 | * -g -std=c++17 -o undefinedBehaviour undefinedBehaviour.cpp 28 | * 29 | * You might see warnings depending on the compiler, but on most platforms the program runs without observable issues. 30 | * Smart compilers will warn with most of these bugs, but the compilers can easily be deceived by hiding a nullptr in 31 | * a variable, or passing it as a function argument or similar. 32 | * 33 | * Since UBSan does runtime checks, it will catch these errors even if they are obscured. 34 | * Recompile with "-fsanitize=undefined", and observe that almost every second line contains a serious bug. 35 | * Try to understand what's wrong. 36 | */ 37 | 38 | int runTests() { 39 | int arr[] = {1, 2, 3, 4}; 40 | std::cout << "arr[4]=" << arr[4] << "\n"; // Array overrun 41 | 42 | unsigned int s = 1; 43 | std::cout << "s << 33=" << (s << 33) << "\n"; // We cannot shift a type with 32 bits by 33 bits. 44 | 45 | int i = std::numeric_limits::max(); 46 | std::cout << "i + 1 =" << i + 1 << "\n"; // Adding 1 to max int overflows to min int 47 | 48 | Derived1 d1; 49 | Base & base = d1; 50 | auto & d2 = static_cast(base); // Casting an original d1 to d2 is wrong 51 | d2.print(); // Calling a function on a wrongly-cast object is wrong 52 | 53 | Derived2 * d2ptr = nullptr; 54 | Derived2 & nullref = static_cast(*d2ptr); // Cannot bind references to a wrong address 55 | } // Forgot to return a value 56 | 57 | int main() { 58 | const auto result = runTests(); 59 | return result; 60 | } 61 | -------------------------------------------------------------------------------- /exercises/ubsan/undefinedBehaviour.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // You don't need to change any code in these structs: 5 | struct Base { 6 | virtual void print() = 0; 7 | virtual ~Base() {} 8 | }; 9 | struct Derived1 : public Base { 10 | void print() override { 11 | std::cout << "Derived1::print()\n"; 12 | } 13 | }; 14 | struct Derived2 : public Base { 15 | void print() override { 16 | std::cout << "Derived2::print()\n"; 17 | } 18 | }; 19 | 20 | 21 | /** 22 | * *************** 23 | * Instructions: 24 | * *************** 25 | * 26 | * Compile and run this program using a compiler invocation like 27 | * -g -std=c++17 -o undefinedBehaviour undefinedBehaviour.cpp 28 | * 29 | * You might see warnings depending on the compiler, but on most platforms the program runs without observable issues. 30 | * Smart compilers will warn with most of these bugs, but the compilers can easily be deceived by hiding a nullptr in 31 | * a variable, or passing it as a function argument or similar. 32 | * 33 | * Since UBSan does runtime checks, it will catch these errors even if they are obscured. 34 | * Recompile with "-fsanitize=undefined", and observe that almost every second line contains a serious bug. 35 | * Try to understand what's wrong. 36 | */ 37 | 38 | int runTests() { 39 | int arr[] = {1, 2, 3, 4}; 40 | std::cout << "arr[4]=" << arr[4] << "\n"; 41 | 42 | unsigned int s = 1; 43 | std::cout << "s << 33=" << (s << 33) << "\n"; 44 | 45 | int i = std::numeric_limits::max(); 46 | std::cout << "i + 1 =" << i + 1 << "\n"; 47 | 48 | Derived1 d1; 49 | Base & base = d1; 50 | auto & d2 = static_cast(base); 51 | d2.print(); 52 | 53 | Derived2 * d2ptr = nullptr; 54 | Derived2 & nullref = static_cast(*d2ptr); 55 | } 56 | 57 | int main() { 58 | const auto result = runTests(); 59 | return result; 60 | } 61 | -------------------------------------------------------------------------------- /exercises/valgrind/.gitignore: -------------------------------------------------------------------------------- 1 | debug 2 | debug.sol 3 | -------------------------------------------------------------------------------- /exercises/valgrind/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( valgrind LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's executable. 9 | add_executable( valgrind "debug.cpp" ) 10 | 11 | # Create the "solution executable". 12 | add_executable( valgrind.sol EXCLUDE_FROM_ALL "solution/debug.sol.cpp" ) 13 | add_dependencies( solution valgrind.sol ) 14 | -------------------------------------------------------------------------------- /exercises/valgrind/Makefile: -------------------------------------------------------------------------------- 1 | all: debug 2 | solution: debug.sol 3 | 4 | clean: 5 | rm -f *o debug *~ debug.sol core 6 | 7 | debug : debug.cpp 8 | ${CXX} -std=c++17 -g -O0 -L. -o $@ $< 9 | 10 | debug.sol : solution/debug.sol.cpp 11 | ${CXX} -std=c++17 -g -O0 -Wall -Wextra -L. -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/valgrind/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | * compile, run, it shouldn't crash 5 | * run with valgrind (`valgrind ./debug`) 6 | * fix an out-of-bounds access 7 | * check for memory leaks 8 | -------------------------------------------------------------------------------- /exercises/valgrind/debug.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void swap(int* a, int* b) 5 | { 6 | int c = *a; 7 | *a = *b; 8 | *b = c; 9 | } 10 | 11 | void reverse(int* v, unsigned int len) 12 | { 13 | for (unsigned int i = 0; i < (len + 1) / 2; i++) { 14 | const int a = i; 15 | const int b = len - i; 16 | 17 | swap(v + a, v + b); 18 | } 19 | } 20 | 21 | int* createAndFillVector(unsigned int len) 22 | { 23 | auto v = new int[len]; 24 | for (unsigned int i = 0; i < len; i++) { 25 | v[i] = i; 26 | } 27 | return v; 28 | } 29 | 30 | int main() 31 | { 32 | constexpr auto arraySize = 100; 33 | int* v = nullptr; 34 | // create and reverse the vector of LEN numbers 35 | v = createAndFillVector(arraySize); 36 | reverse(v, arraySize); 37 | 38 | // check if the revert worked: 39 | const bool isReversed = std::is_sorted(v, v + arraySize, std::greater {}); 40 | std::cout << "Vector reversed successfully: " << std::boolalpha 41 | << isReversed << "\n"; 42 | 43 | return isReversed ? 0 : 1; 44 | } 45 | -------------------------------------------------------------------------------- /exercises/valgrind/solution/debug.sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void swap(int* a, int* b) 5 | { 6 | int c = *a; 7 | *a = *b; 8 | *b = c; 9 | } 10 | 11 | void reverse(int* v, unsigned int len) 12 | { 13 | for (unsigned int i = 0; i < (len + 1) / 2; i++) { 14 | const int a = i; 15 | const int b = len - 1 - i; 16 | 17 | swap(v + a, v + b); 18 | } 19 | } 20 | 21 | int* createAndFillVector(unsigned int len) 22 | { 23 | auto v = new int[len]; 24 | for (unsigned int i = 0; i < len; i++) { 25 | v[i] = i; 26 | } 27 | return v; 28 | } 29 | 30 | int main() 31 | { 32 | constexpr auto arraySize = 100; 33 | int* v = nullptr; 34 | // create and reverse the vector of LEN numbers 35 | v = createAndFillVector(arraySize); 36 | reverse(v, arraySize); 37 | 38 | // check if the revert worked: 39 | const bool isReversed = std::is_sorted(v, v + arraySize, std::greater {}); 40 | std::cout << "Vector reversed successfully: " << std::boolalpha 41 | << isReversed << "\n"; 42 | 43 | delete[] v; 44 | 45 | return isReversed ? 0 : 1; 46 | } 47 | -------------------------------------------------------------------------------- /exercises/variadic/.gitignore: -------------------------------------------------------------------------------- 1 | variadic 2 | variadic.sol 3 | -------------------------------------------------------------------------------- /exercises/variadic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( variadic LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | set(CMAKE_CXX_STANDARD 20) 8 | 9 | # Create the user's executable. 10 | add_executable( variadic "variadic.cpp" ) 11 | 12 | # Create the "solution executable". 13 | add_executable( variadic.sol EXCLUDE_FROM_ALL 14 | "solution/variadic.sol.cpp" ) 15 | add_dependencies( solution variadic.sol ) 16 | -------------------------------------------------------------------------------- /exercises/variadic/Makefile: -------------------------------------------------------------------------------- 1 | all: variadic 2 | solution: variadic.sol 3 | 4 | clean: 5 | rm -f *o variadic *~ variadic.sol 6 | 7 | variadic : variadic.cpp 8 | $(CXX) -std=c++20 -g -O0 -Wall -Wextra -o $@ $< 9 | 10 | variadic.sol : solution/variadic.sol.cpp 11 | $(CXX) -std=c++20 -g -O0 -Wall -Wextra -o $@ $< 12 | -------------------------------------------------------------------------------- /exercises/variadic/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | In this exercise, we will take the `tuple` implementation from the slides 5 | and extend it step by step to make the example code in `main` compile. 6 | 7 | Feel free to comment out parts of main to test parts of your implementation sooner. 8 | All instructions are given in more detail in the source code as well, especially inside `main`. 9 | 10 | This example neglects const correctness so there is less code to write. 11 | Making it const correct would require adding several overloads with almost the same code as the non-const version. 12 | You can try this is a bonus exercise at the end! 13 | 14 | 1. make `tuple` default-constructible 15 | 2. avoid implicit construction from a single value 16 | 3. write `head(tuple)`, which returns the first element of a tuple 17 | 4. implement `get(tuple)` 18 | 5. specialize `std::tuple_size` 19 | 6. specialize `std::tuple_element` 20 | 7. implement a binary `tuple_cat(tuple, tuple)` 21 | 8. implement `sum(tuple)` 22 | 9. implement `operator<<`, producing comma separated output 23 | -------------------------------------------------------------------------------- /exercises/variant/.gitignore: -------------------------------------------------------------------------------- 1 | variant 2 | -------------------------------------------------------------------------------- /exercises/variant/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( variant LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | set(CMAKE_CXX_STANDARD 20) 8 | 9 | # Create the user's executable. 10 | add_executable( variant "variant.cpp" ) 11 | 12 | # Create the "solution executable". 13 | add_executable( variant.sol1 EXCLUDE_FROM_ALL "solution/variant.sol1.cpp" ) 14 | add_custom_target( solution1 ) 15 | add_dependencies( solution1 variant.sol1 ) 16 | add_executable( variant.sol2 EXCLUDE_FROM_ALL "solution/variant.sol2.cpp" ) 17 | add_custom_target( solution2 ) 18 | add_dependencies( solution2 variant.sol2 ) 19 | add_dependencies( solution solution1 solution2 ) 20 | -------------------------------------------------------------------------------- /exercises/variant/Makefile: -------------------------------------------------------------------------------- 1 | all: variant 2 | solution: solution1 solution2 3 | solution1: variant.sol1 4 | solution2: variant.sol2 5 | 6 | clean: 7 | rm -f *o *so variant *~ variant.sol1 variant.sol2 8 | 9 | % : %.cpp 10 | $(CXX) -g -std=c++20 -Wall -Wextra -o $@ $< 11 | 12 | %.sol1 : solution/%.sol1.cpp 13 | $(CXX) -g -std=c++20 -Wall -Wextra -o $@ $< 14 | 15 | %.sol2 : solution/%.sol2.cpp 16 | $(CXX) -g -std=c++20 -Wall -Wextra -o $@ $< 17 | -------------------------------------------------------------------------------- /exercises/variant/README.md: -------------------------------------------------------------------------------- 1 | # Variants 2 | 3 | Here we explore: 4 | - how `std::variant` offers an alternative to inheritance. 5 | 6 | ## Prerequisites 7 | 8 | - slides about `std::variant` 9 | - slides about lambda functions (for solution2) 10 | 11 | ## Instructions 12 | 13 | In the file `variant.cpp`, replace inheritance with the use of a `std::variant`. 14 | Two solutions are provided : 15 | 1. with `std::get_if`, 16 | 2. with `std::visit`. 17 | -------------------------------------------------------------------------------- /exercises/variant/solution/variant.sol1.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | struct Electron { 7 | void print() const { std::cout << "E\n"; } 8 | }; 9 | 10 | struct Proton { 11 | void print() const { std::cout << "P\n"; } 12 | }; 13 | 14 | struct Neutron { 15 | void print() const { std::cout << "N\n"; } 16 | }; 17 | 18 | using Particle = std::variant; 19 | 20 | template void print_if(Particle const &p) { 21 | // if the real type of p is not T, 22 | // get_if returns nullptr 23 | T const *ptr = std::get_if(&p); 24 | if (ptr) 25 | ptr->print(); 26 | } 27 | 28 | int main() { 29 | std::vector ps = {Electron{}, Proton{}, Neutron{}}; 30 | 31 | for (auto p : ps) { 32 | print_if(p); 33 | print_if(p); 34 | print_if(p); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /exercises/variant/solution/variant.sol2.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | struct Electron { 7 | void print() const { std::cout << "E\n"; } 8 | }; 9 | 10 | struct Proton { 11 | void print() const { std::cout << "P\n"; } 12 | }; 13 | 14 | struct Neutron { 15 | void print() const { std::cout << "N\n"; } 16 | }; 17 | 18 | using Particle = std::variant; 19 | 20 | int main() { 21 | std::vector ps = {Electron{}, Proton{}, Neutron{}}; 22 | 23 | for (auto const &p : ps) { 24 | // With "auto" as argument, we provide a generic lambda. The compiler 25 | // will instantiate it for all the types that the variant can hold, and at run 26 | // time, the correct overload will be chosen automatically: 27 | std::visit([](const auto &p) { p.print(); }, p); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /exercises/variant/variant.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | In the code below, replace inheritance with the use of a `std::variant`. 4 | There is no more need for a base class, and no more need pointers. 5 | Two solutions are provided : 6 | 1. with `std::get_if`, 7 | 2. with `std::visit`. 8 | 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | struct Particle { 16 | virtual void print() const = 0; 17 | virtual ~Particle() = default; 18 | }; 19 | 20 | struct Electron : Particle { 21 | void print() const override { std::cout << "E\n"; } 22 | }; 23 | 24 | struct Proton : Particle { 25 | void print() const override { std::cout << "P\n"; } 26 | }; 27 | 28 | struct Neutron : Particle { 29 | void print() const override { std::cout << "N\n"; } 30 | }; 31 | 32 | int main() { 33 | std::vector> ps; 34 | ps.push_back(std::make_unique()); 35 | ps.push_back(std::make_unique()); 36 | ps.push_back(std::make_unique()); 37 | 38 | for (auto const &p : ps) { 39 | p->print(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /exercises/virtual_inheritance/.gitignore: -------------------------------------------------------------------------------- 1 | trymultiherit 2 | trymultiherit.sol 3 | -------------------------------------------------------------------------------- /exercises/virtual_inheritance/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up the project. 2 | cmake_minimum_required( VERSION 3.12 ) 3 | project( virtual_inheritance LANGUAGES CXX ) 4 | 5 | # Set up the compilation environment. 6 | include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" ) 7 | 8 | # Create the user's library. 9 | add_library( textbox "TextBox.hpp" "TextBox.cpp" ) 10 | target_include_directories( textbox PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" ) 11 | 12 | # Create the user's executable. 13 | add_executable( trymultiherit "trymultiherit.cpp" ) 14 | target_link_libraries( trymultiherit PRIVATE textbox ) 15 | 16 | # Create the solution's library. 17 | add_library( textboxsol EXCLUDE_FROM_ALL "solution/TextBox.hpp" "solution/TextBox.cpp" ) 18 | target_include_directories( textboxsol PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/solution" ) 19 | add_dependencies( solution textboxsol ) 20 | 21 | # Create the "solution executable". 22 | add_executable( trymultiherit.sol EXCLUDE_FROM_ALL "solution/trymultiherit.sol.cpp" ) 23 | target_link_libraries( trymultiherit.sol PRIVATE textboxsol ) 24 | add_dependencies( solution trymultiherit.sol ) 25 | -------------------------------------------------------------------------------- /exercises/virtual_inheritance/Makefile: -------------------------------------------------------------------------------- 1 | all: trymultiherit 2 | solution: trymultiherit.sol 3 | 4 | clean: 5 | rm -f *o *so trymultiherit *~ trymultiherit.sol 6 | 7 | libtextbox.so: TextBox.cpp TextBox.hpp 8 | $(CXX) -Wall -Wextra -shared -fPIC -o $@ $< 9 | 10 | trymultiherit : trymultiherit.cpp libtextbox.so 11 | $(CXX) -Wall -Wextra -L. -o $@ $^ 12 | 13 | libtextboxsol.so: solution/TextBox.cpp solution/TextBox.hpp 14 | $(CXX) -Wall -Wextra -shared -fPIC -o $@ $< 15 | 16 | trymultiherit.sol : solution/trymultiherit.sol.cpp libtextboxsol.so 17 | $(CXX) -Wall -Wextra -I. -L. -o $@ $^ 18 | -------------------------------------------------------------------------------- /exercises/virtual_inheritance/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Instructions 3 | 4 | Step 1 5 | * look at the code 6 | * open `trymultiherit.cpp` 7 | * create a `TextBox` and call `draw` 8 | * Fix the code to call both draws by using types 9 | 10 | Step 2 11 | * retry with virtual inheritance 12 | -------------------------------------------------------------------------------- /exercises/virtual_inheritance/TextBox.cpp: -------------------------------------------------------------------------------- 1 | #include "TextBox.hpp" 2 | #include 3 | 4 | Drawable::Drawable(int id) : m_id(id) {}; 5 | 6 | void Drawable::draw() const { 7 | std::cout << "Drawing " << m_id << '\n'; 8 | }; 9 | 10 | Rectangle::Rectangle(int id, float width, float height) : 11 | Drawable(id), m_width(width), m_height(height) {} 12 | 13 | Text::Text(int id, const std::string &content) : 14 | Drawable(id), m_content(content) {} 15 | 16 | TextBox::TextBox(const std::string &content, 17 | float width, float height) : 18 | Rectangle(1, width, height), Text(2, content) {} 19 | -------------------------------------------------------------------------------- /exercises/virtual_inheritance/TextBox.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Drawable { 6 | public: 7 | Drawable(int id); 8 | void draw() const; 9 | private: 10 | int m_id; 11 | }; 12 | 13 | class Rectangle : public Drawable { 14 | public: 15 | Rectangle(int id, float width, float height); 16 | protected: 17 | float m_width; 18 | float m_height; 19 | }; 20 | 21 | class Text : public Drawable { 22 | public: 23 | Text(int id, const std::string &content); 24 | protected: 25 | std::string m_content; 26 | }; 27 | 28 | class TextBox : public Rectangle, public Text { 29 | public: 30 | TextBox(const std::string &content, 31 | float width, float height); 32 | }; 33 | -------------------------------------------------------------------------------- /exercises/virtual_inheritance/solution/TextBox.cpp: -------------------------------------------------------------------------------- 1 | #include "TextBox.hpp" 2 | #include 3 | 4 | Drawable::Drawable(int id) : m_id(id) {}; 5 | 6 | void Drawable::draw() const { 7 | std::cout << "Drawing " << m_id << '\n'; 8 | }; 9 | 10 | Rectangle::Rectangle(int id, float width, float height) : 11 | Drawable(id), m_width(width), m_height(height) {} 12 | 13 | Text::Text(int id, std::string content) : 14 | Drawable(id), m_content(content) {} 15 | 16 | TextBox::TextBox(std::string content, 17 | float width, float height) : 18 | Drawable(3), Rectangle(1, width, height), Text(2, content) {} 19 | -------------------------------------------------------------------------------- /exercises/virtual_inheritance/solution/TextBox.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Drawable { 6 | public: 7 | Drawable(int id); 8 | void draw() const; 9 | private: 10 | int m_id; 11 | }; 12 | 13 | class Rectangle : public virtual Drawable { 14 | public: 15 | Rectangle(int id, float width, float height); 16 | protected: 17 | float m_width; 18 | float m_height; 19 | }; 20 | 21 | class Text : public virtual Drawable { 22 | public: 23 | Text(int id, std::string content); 24 | protected: 25 | std::string m_content; 26 | }; 27 | 28 | class TextBox : public Rectangle, public Text { 29 | public: 30 | TextBox(std::string content, 31 | float width, float height); 32 | }; 33 | -------------------------------------------------------------------------------- /exercises/virtual_inheritance/solution/trymultiherit.sol.cpp: -------------------------------------------------------------------------------- 1 | #include "TextBox.hpp" 2 | #include 3 | 4 | int main() { 5 | // create a TextBox and call draw 6 | TextBox tb("my text", 10, 5); 7 | // tb.draw(); 8 | // error: request for member ‘draw’ is ambiguous 9 | 10 | // Fix the code to call both draws by using types 11 | Rectangle &r = tb; 12 | r.draw(); 13 | Text &t = tb; 14 | t.draw(); 15 | 16 | // retry with virtual inheritance 17 | // error: no matching function for call to ‘Drawable::Drawable()’ 18 | // add a default constructor to Drawable or call the constructor from TextBox 19 | } 20 | -------------------------------------------------------------------------------- /exercises/virtual_inheritance/trymultiherit.cpp: -------------------------------------------------------------------------------- 1 | #include "TextBox.hpp" 2 | #include 3 | 4 | int main() { 5 | // create a TextBox and call draw 6 | 7 | 8 | // Fix the code to call both draws by using types 9 | 10 | 11 | // try with virtual inheritance 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /mlc_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": [ 3 | { 4 | "pattern": "https://github.com/issues?.*" 5 | }, 6 | { 7 | "pattern": ".*cookiecutter.*" 8 | }, 9 | { 10 | "pattern": ".*opensource.*" 11 | }, 12 | { 13 | "pattern": "https://github.com/hsf-training/cpluspluscourse/raw/download/.*" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /notes/2016timing: -------------------------------------------------------------------------------- 1 | did 1-2-3 + object orientation in first 3h 2 | stopped after memcheck on day 2 3 | added 20mn on day 3 to finish (not doing helgrind/ccpcheck) 4 | 5 | 1 exercise done for real (templates, 1-4 only) 6 | others done together, and some skipped completely 7 | -------------------------------------------------------------------------------- /notes/2017timing: -------------------------------------------------------------------------------- 1 | First day : 1, 2 fast, 3 and 4 up to constness + went through functors a first time 2 | did together virtual inheritance exercise 3 | skipped the others 4 | Second day : went to the end of tools 5 | did all exercises but all on the screen 6 | Third day : the rest, with only 5mn of delay at the end 7 | did more or ess the exercises on the screen (basically looking at the solution) 8 | -------------------------------------------------------------------------------- /notes/2018timing: -------------------------------------------------------------------------------- 1 | Day 1 : 2 | Started from start, fast on basics 3 | Did no exercise (only looked at virtual inheritance one) 4 | stopped after constness, but went through functors and templates a 1st time 5 | 6 | Day 2 : 7 | Redid functors and templates 8 | went through all tools 9 | Did fully the exercise on templates 10 | Did all exercises on tools except helgrind 11 | 12 | Day 3 : 13 | C++11 and 14 in first half up to move semantic 14 | Redid move semantic in 2nd half and finished C++11/14 15 | Then did python and finally C++17 16 | only 5mn late 17 | No exercises whatsoever 18 | -------------------------------------------------------------------------------- /notes/2019Timing: -------------------------------------------------------------------------------- 1 | Day 1 : 2 | pause after 1:40 before static members in OO 3 | stopped at end of More C++ features (after move and copy elision) 4 | 5 | Day 2 : 6 | repeated move semantic and started tools up to debugging. Did advanced OO up to pure virtual methods then pause 7 | end of advanced OO, operators, templates (and exercise), STL, functors 8 | 9 | Day 3 : 10 | repeated functors and templates quickly, finished advanced C++ 11 | valgrind and cppcheck exercise (minus helgrind), python and concurrency (minus condition variables) 12 | 13 | Corrections : 14 | slide 188 15 | -------------------------------------------------------------------------------- /notes/2020Timing: -------------------------------------------------------------------------------- 1 | Day 1 : 2 | pause after 1:20 before starting Object Orientation 3 | stopped after constness 4 | 5 | Day 2 (on video conference) : 6 | quick recap (15 mn) of course 1 7 | pause after advanced OO 8 | Then tools until callgrind (included) 9 | 10 | Day 3 (on video conference) : 11 | static code analysis (helgrind was skipped) 12 | pause after templates, before STL 13 | finished advanced C++ and did C++ and python 14 | -------------------------------------------------------------------------------- /talk/.latexmkrc: -------------------------------------------------------------------------------- 1 | $clean_ext = "cut lex lgb lgp mw nav snm vrb _minted-%R/* _minted-%R"; 2 | -------------------------------------------------------------------------------- /talk/CERN-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsf-training/cpluspluscourse/20c735b86f594b85735fd3c65e3c202fced585da/talk/CERN-logo.jpg -------------------------------------------------------------------------------- /talk/Makefile: -------------------------------------------------------------------------------- 1 | all: C++Course.pdf 2 | 3 | LATEXMK=latexmk 4 | OPTIONS=-pdf -xelatex -shell-escape -halt-on-error 5 | 6 | essentials: all 7 | essentials: OPTIONS+=-usepretex='\def\makebasic{}' 8 | 9 | preview: all 10 | preview: OPTIONS+=-pvc 11 | 12 | ifeq ($(QUIET), 1) 13 | OPTIONS+=-quiet 14 | else 15 | OPTIONS+=-interaction=nonstopmode 16 | endif 17 | 18 | ifeq ($(HEPCPP_ESSENTIALS), 1) 19 | OPTIONS+=-usepretex='\def\makebasic{}' 20 | endif 21 | 22 | .PHONY: clean clobber FORCE 23 | 24 | %.pdf: %.tex FORCE 25 | $(LATEXMK) $(OPTIONS) $< 26 | 27 | clean: 28 | $(LATEXMK) -C 29 | 30 | clobber: 31 | $(LATEXMK) -C 32 | $(RM) C++Course.pdf 33 | -------------------------------------------------------------------------------- /talk/basicconcepts/auto.tex: -------------------------------------------------------------------------------- 1 | \subsection[auto]{Auto keyword} 2 | 3 | \begin{frame}[fragile] 4 | \frametitlecpp[11]{Auto keyword} 5 | \begin{block}{Reason of being} 6 | \begin{itemize} 7 | \item Many type declarations are redundant 8 | \item They are often a source for compiler warnings and errors 9 | \item Using auto prevents unwanted/unnecessary type conversions 10 | \end{itemize} 11 | \begin{cppcode*}{} 12 | std::vector v; 13 | float a = v[3]; // conversion intended? 14 | int b = v.size(); // bug? unsigned to signed 15 | \end{cppcode*} 16 | \end{block} 17 | \pause 18 | \begin{block}{Practical usage} 19 | \begin{cppcode*}{} 20 | std::vector v; 21 | auto a = v[3]; 22 | const auto b = v.size(); // std::size_t 23 | int sum{0}; 24 | for (auto n : v) { sum += n; } 25 | \end{cppcode*} 26 | \end{block} 27 | \end{frame} 28 | 29 | \begin{frame}[fragile] 30 | \frametitlecpp[98]{Loops, references, auto} 31 | \begin{exerciseWithShortcut}{Loops, references, auto}{Loops, refs, auto} 32 | Familiarise yourself with range-based for loops and references 33 | \begin{itemize} 34 | \item Go to \texttt{exercises/loopsRefsAuto} 35 | \item Look at \texttt{loopsRefsAuto.cpp} 36 | \item Compile it (\texttt{make}) and run the program (\texttt{./loopsRefsAuto}) 37 | \item Work on the tasks that you find in \texttt{loopsRefsAuto.cpp} 38 | \end{itemize} 39 | \end{exerciseWithShortcut} 40 | \end{frame} 41 | -------------------------------------------------------------------------------- /talk/basicconcepts/references.tex: -------------------------------------------------------------------------------- 1 | \subsection[Refs]{References} 2 | 3 | \begin{frame}[fragile] 4 | \frametitlecpp[98]{References} 5 | \begin{block}{References} 6 | \begin{itemize} 7 | \item References allow for direct access to another object 8 | \item They can be used as shortcuts / better readability 9 | \item They can be declared \cppinline{const} to allow only read access 10 | \end{itemize} 11 | \end{block} 12 | 13 | \begin{exampleblock}{Example:} 14 | \begin{cppcode*}{} 15 | int i = 2; 16 | int &iref = i; // access to i 17 | iref = 3; // i is now 3 18 | 19 | // const reference to a member: 20 | struct A { int x; int y; } a; 21 | const int &x = a.x; // direct read access to A's x 22 | x = 4; // doesn't compile 23 | a.x = 4; // fine 24 | \end{cppcode*} 25 | \end{exampleblock} 26 | \end{frame} 27 | 28 | \begin{frame}[fragile] 29 | \frametitlecpp[98]{Pointers vs References} 30 | \begin{block}{Specificities of reference} 31 | \begin{itemize} 32 | \item Natural syntax 33 | \item Cannot be \cppinline{nullptr} 34 | \item Must be assigned when defined, cannot be reassigned 35 | \item References to temporary objects must be \cppinline{const} 36 | \end{itemize} 37 | \end{block} 38 | \begin{block}{Advantages of pointers} 39 | \begin{itemize} 40 | \item Can be \cppinline{nullptr} 41 | \item Can be initialized after declaration, can be reassigned 42 | \end{itemize} 43 | \end{block} 44 | \pause 45 | \begin{goodpractice}{References} 46 | \begin{itemize} 47 | \item Prefer using references instead of pointers 48 | \item Mark references \cppinline{const} to prevent modification 49 | \end{itemize} 50 | \end{goodpractice} 51 | \end{frame} 52 | -------------------------------------------------------------------------------- /talk/concurrency/threadlocal.tex: -------------------------------------------------------------------------------- 1 | \subsection[TLS]{Thread-local storage} 2 | 3 | \begin{frame}[fragile] 4 | \frametitlecpp[20]{Thread-local storage} 5 | \begin{block}{\cppinline{thread_local} keyword} 6 | \begin{itemize} 7 | \item A variable can be declared \cppinline{thread-local} 8 | \item Then every thread will have its own copy 9 | \item Most useful for ``working memory'' in each thread 10 | \item Note: Can also be declared directly on the stack of a thread 11 | \begin{itemize} 12 | \item and will be faster, thus should be preferred when possible 13 | \end{itemize} 14 | \end{itemize} 15 | \end{block} 16 | \begin{exampleblock}{} 17 | \begin{cppcode*}{} 18 | thread_local int a{0}; 19 | { 20 | std::jthread t1([&] { a++; }); 21 | std::jthread t2([&] { a++; }); 22 | a += 2; 23 | } 24 | assert( a == 2 ); // Guaranteed to succeed 25 | \end{cppcode*} 26 | \end{exampleblock} 27 | \end{frame} 28 | -------------------------------------------------------------------------------- /talk/introduction/BjarneStroustrup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsf-training/cpluspluscourse/20c735b86f594b85735fd3c65e3c202fced585da/talk/introduction/BjarneStroustrup.jpg -------------------------------------------------------------------------------- /talk/introduction/goals.tex: -------------------------------------------------------------------------------- 1 | \subsection[Use]{Why we use it?} 2 | 3 | \begin{frame} 4 | \frametitle{Why is \cpp{} our language of choice?} 5 | \begin{block}{Adapted to large projects} 6 | \begin{itemize} 7 | \item statically and strongly typed 8 | \item object oriented 9 | \item widely used (and taught) 10 | \item many available libraries 11 | \end{itemize} 12 | \end{block} 13 | \pause 14 | \begin{block}{Fast} 15 | \begin{itemize} 16 | \item compiled to native machine code 17 | \begin{itemize} 18 | \item unlike Java, C\#, Python, ... 19 | \end{itemize} 20 | \item allows to go close to hardware when needed 21 | \end{itemize} 22 | \end{block} 23 | \pause 24 | \begin{alertblock}{What we get} 25 | \begin{itemize} 26 | \item the most powerful language 27 | \item the most complicated one 28 | \item the most error prone? 29 | \end{itemize} 30 | \end{alertblock} 31 | \end{frame} 32 | -------------------------------------------------------------------------------- /talk/introduction/ritchie.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsf-training/cpluspluscourse/20c735b86f594b85735fd3c65e3c202fced585da/talk/introduction/ritchie.jpeg -------------------------------------------------------------------------------- /talk/morelanguage/AtlasLego.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsf-training/cpluspluscourse/20c735b86f594b85735fd3c65e3c202fced585da/talk/morelanguage/AtlasLego.jpg -------------------------------------------------------------------------------- /talk/morelanguage/copyelision.tex: -------------------------------------------------------------------------------- 1 | \subsection[copy]{Copy elision} 2 | 3 | \begin{frame}[fragile] 4 | \frametitlecpp[17]{Copy elision} 5 | \begin{block}{Where does it take place ?} 6 | \small 7 | \begin{cppcode*}{} 8 | struct Foo { ... }; 9 | Foo f() { 10 | return Foo(); // return-value opt. (RVO) 11 | } 12 | Foo g() { 13 | Foo foo; 14 | return foo; // named return-value opt. (NRVO) 15 | } 16 | int main() { 17 | // compiler must avoid these copies: 18 | Foo a = f(); 19 | Foo b = Foo(); 20 | Foo c = Foo(Foo()); 21 | // copy elision allowed, but not guaranteed: 22 | Foo d = g(); 23 | } 24 | \end{cppcode*} 25 | \end{block} 26 | \end{frame} 27 | 28 | \begin{frame}[fragile] 29 | \frametitlecpp[17]{Guaranteed copy elision} 30 | Allows to write code not allowed in \cpp14 (would not compile) 31 | \begin{block}{Guaranteed: Return value optimization (RVO)} 32 | \begin{cppcode*}{} 33 | struct Foo { 34 | Foo() { ... } 35 | Foo(const Foo &) = delete; 36 | Foo(Foo &&) = delete; 37 | Foo& operator=(const Foo &) = delete; 38 | Foo& operator=(Foo &&) = delete; 39 | }; 40 | Foo f() { 41 | return Foo(); // ok, no move 42 | } 43 | int main() { 44 | Foo foo = f(); // ok, no move 45 | } 46 | \end{cppcode*} 47 | \end{block} 48 | \end{frame} 49 | -------------------------------------------------------------------------------- /talk/objectorientation/functors.tex: -------------------------------------------------------------------------------- 1 | \subsection[()]{Function objects} 2 | 3 | \begin{frame}[fragile] 4 | \frametitlecpp[98]{Function objects} 5 | \begin{block}{Concept} 6 | \begin{itemize} 7 | \item also known as functors (no relation to functors in math) 8 | \item a class that implements \cppinline{operator()} 9 | \item allows to use objects in place of functions 10 | \item with constructors and data members 11 | \end{itemize} 12 | \end{block} 13 | \begin{cppcode} 14 | struct Adder { 15 | int m_increment; 16 | Adder(int increment) : m_increment(increment) {} 17 | int operator()(int a) { return a + m_increment; } 18 | }; 19 | Adder inc1{1}, inc10{10}; 20 | int i = 3; 21 | int j = inc1(i); // 4 22 | int k = inc10(i); // 13 23 | int l = Adder{25}(i); // 28 24 | \end{cppcode} 25 | \end{frame} 26 | 27 | \begin{frame}[fragile] 28 | \frametitlecpp[20]{Function objects} 29 | \begin{exampleblockGB}{Function objects as function arguments}{https://godbolt.org/z/zxqYG6xzT}{function objects} 30 | \begin{cppcode} 31 | int count_if(const auto& range, auto predicate) { 32 | int count = 0; // ↑ template (later) 33 | for (const auto& e : range) 34 | if (predicate(e)) count++; 35 | return count; 36 | } 37 | struct IsBetween { 38 | int lower, upper; 39 | bool operator()(int value) const { 40 | return lower < value && value < upper; 41 | } 42 | }; 43 | int arr[]{1, 2, 3, 4, 5, 6, 7}; 44 | std::cout << count_if(arr, IsBetween{2, 6}); // 3 45 | // prefer: std::ranges::count_if 46 | \end{cppcode} 47 | \end{exampleblockGB} 48 | \end{frame} 49 | -------------------------------------------------------------------------------- /talk/objectorientation/static.tex: -------------------------------------------------------------------------------- 1 | \subsection[static]{Static members} 2 | 3 | \begin{frame}[fragile] 4 | \frametitlecpp[98]{Static members} 5 | \begin{block}{Concept} 6 | \begin{itemize} 7 | \item members attached to a class rather than to an object 8 | \item usable with or without an instance of the class 9 | \item identified by the \cppinline{static} keyword 10 | \end{itemize} 11 | \end{block} 12 | 13 | \vspace{-1\baselineskip} 14 | \begin{overprint} 15 | \onslide<1> 16 | \begin{exampleblock}{Static.hpp} 17 | \begin{cppcode} 18 | class Text { 19 | public: 20 | static std::string upper(std::string); 21 | private: 22 | static int callsToUpper; // add `inline` in C++17 23 | }; 24 | \end{cppcode} 25 | \end{exampleblock} 26 | 27 | \onslide<2> 28 | \begin{exampleblock}{Static.cpp} 29 | \begin{cppcode} 30 | #include "Static.hpp" 31 | int Text::callsToUpper = 0; // required before C++17 32 | 33 | std::string Text::upper(std::string lower) { 34 | callsToUpper++; 35 | // convert lower to upper case 36 | // return ...; 37 | } 38 | std::string uppers = Text::upper("my text"); 39 | // now Text::callsToUpper is 1 40 | \end{cppcode} 41 | \end{exampleblock} 42 | \end{overprint} 43 | \end{frame} 44 | -------------------------------------------------------------------------------- /talk/python/cppyy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsf-training/cpluspluscourse/20c735b86f594b85735fd3c65e3c202fced585da/talk/python/cppyy.png -------------------------------------------------------------------------------- /talk/python/cppyy.tex: -------------------------------------------------------------------------------- 1 | \subsection[cppyy]{The cppyy project} 2 | 3 | \begin{frame} 4 | \frametitle{Automatic Python-C++ bindings} 5 | \begin{block}{The {\color{blue!50!white} \href{https://cppyy.readthedocs.io}{cppyy}} project} 6 | \begin{itemize} 7 | \item originated from the ROOT project 8 | \item still young, version 1.0 from Mid 2018 9 | \item but very active, current version 2.1.0 10 | \item extremely powerful for interfacing \cpp and python 11 | \end{itemize} 12 | \end{block} 13 | \begin{block}{How it works} 14 | \begin{itemize} 15 | \item uses Just in Time compilation through {\color{blue!50!black} \href{https://github.com/vgvassilev/cling}{cling}} 16 | \begin{itemize} 17 | \item an interactive \cpp interpreter 18 | \end{itemize} 19 | \end{itemize} 20 | \end{block} 21 | \end{frame} 22 | 23 | \begin{frame} 24 | \frametitle{cppyy crash course(1)} 25 | Shamelessly copied from the cppyy documentation 26 | \includegraphics[width=.8\textwidth]{python/cppyy2.png} 27 | \includegraphics[trim={0 3.2cm 0 0},clip,width=\textwidth]{python/cppyy.png} 28 | \end{frame} 29 | 30 | \begin{frame} 31 | \frametitle{cppyy crash course(1)} 32 | \includegraphics[trim={0 0 0 2.45cm},clip,width=\textwidth]{python/cppyy.png} 33 | \end{frame} 34 | -------------------------------------------------------------------------------- /talk/python/cppyy2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsf-training/cpluspluscourse/20c735b86f594b85735fd3c65e3c202fced585da/talk/python/cppyy2.png -------------------------------------------------------------------------------- /talk/python/ctypes.tex: -------------------------------------------------------------------------------- 1 | \subsection[ctypes]{The ctypes module} 2 | 3 | \begin{frame}[fragile] 4 | \frametitle{The ctypes python module} 5 | \begin{block}{From the documentation} 6 | \begin{itemize} 7 | \item provides C compatible data types 8 | \item allows calling functions in DLLs or shared libraries 9 | \item can be used to wrap these libraries in pure Python 10 | \end{itemize} 11 | \end{block} 12 | \end{frame} 13 | 14 | \begin{frame}[fragile] 15 | \frametitle{ctypes: usage example} 16 | \begin{block}{\cpp code : mandel.hpp} 17 | \begin{cppcode*}{} 18 | int mandel(Complex const & a); 19 | \end{cppcode*} 20 | \end{block} 21 | \begin{block}{``C'' code : mandel\_cwrapper.hpp} 22 | \begin{cppcode*}{} 23 | extern "C" { 24 | int mandel(float r, float i) { 25 | return mandel(Complex(r, i)); 26 | }; 27 | } 28 | \end{cppcode*} 29 | \end{block} 30 | \begin{exampleblock}{calling the mandel library} 31 | \begin{minted}{python} 32 | from ctypes import * 33 | libmandel = CDLL('libmandelc.so') 34 | v = libmandel.mandel(c_float(0.3), c_float(1.2)) 35 | \end{minted} 36 | \end{exampleblock} 37 | \end{frame} 38 | 39 | \begin{frame} 40 | \frametitle{Marrying \cpp and python} 41 | \begin{exercise}{\cpp and python} 42 | \begin{itemize} 43 | \item go to \texttt{exercises/python} 44 | \item look at the original python code mandel.py 45 | \item time it (`time python3 mandel.py`) 46 | \item look at the code in mandel.hpp/cpp 47 | \item look at the python module mandel\_module.cpp 48 | \item compile and modify mandel.py to use it 49 | \item see the gain in time 50 | \item look at the C wrapper in mandel\_cwrapper.cpp 51 | \item modify mandel.py to use libmandelc directly with ctypes 52 | \end{itemize} 53 | \end{exercise} 54 | \tiny Note : you may have to add '.' to LD\_LIBRARY\_PATH and PYTHONPATH 55 | \end{frame} 56 | -------------------------------------------------------------------------------- /talk/python/modulewriting.tex: -------------------------------------------------------------------------------- 1 | \subsection[module]{Writing a module} 2 | 3 | \begin{frame}[fragile] 4 | \frametitle{How to build a python 3 module around \cpp code} 5 | \begin{block}{\cpp code : mandel.hpp} 6 | \begin{cppcode*}{} 7 | int mandel(Complex const & a); 8 | \end{cppcode*} 9 | \end{block} 10 | \end{frame} 11 | 12 | \begin{frame}[fragile] 13 | \frametitle{Basic Module(1): wrap your method} 14 | \begin{block}{mandelModule.cpp - see exercises/python exercise} 15 | \begin{cppcode*}{} 16 | #include 17 | #include "mandel.hpp" 18 | PyObject * mandel_wrapper(PyObject * self, 19 | PyObject * args) { 20 | // Parse Input 21 | float r, i; 22 | if (!PyArg_ParseTuple(args, "ff", &r, &i)) 23 | return nullptr; 24 | // Call C++ function 25 | int result = mandel(Complex(r, i)); 26 | // Build returned objects 27 | return PyLong_FromLong(result); 28 | } 29 | \end{cppcode*} 30 | \end{block} 31 | \end{frame} 32 | 33 | \begin{frame}[fragile] 34 | \frametitle{Basic Module(2): create the python module} 35 | \begin{block}{mandelModule.cpp - see exercises/python exercise} 36 | \begin{cppcode*}{} 37 | // declare the modules' methods 38 | PyMethodDef mandelMethods[] = { 39 | {"mandel", mandel_wrapper, METH_VARARGS, 40 | "computes nb of iterations for mandelbrot set"}, 41 | {nullptr, nullptr, 0, nullptr} 42 | }; 43 | // declare the module 44 | struct PyModuleDef mandelModule = { 45 | PyModuleDef_HEAD_INIT, 46 | "mandel", nullptr, -1, mandelMethods 47 | }; 48 | PyMODINIT_FUNC PyInit_mandel() { 49 | return PyModule_Create(&mandelModule); 50 | } 51 | \end{cppcode*} 52 | \end{block} 53 | \end{frame} 54 | 55 | \begin{frame}[fragile] 56 | \frametitle{Basic Module(3): use it} 57 | \begin{block}{First compile the module} 58 | \begin{itemize} 59 | \item as a regular shared library 60 | \item with '\mintinline{bash}{-I \$(PYTHON\_INCLUDE)}' 61 | \end{itemize} 62 | \end{block} 63 | \begin{block}{mandel.py - see exercises/python exercise} 64 | \begin{minted}{python} 65 | from mandel import mandel 66 | v = mandel(0.7, 1.2) 67 | \end{minted} 68 | \end{block} 69 | \end{frame} 70 | -------------------------------------------------------------------------------- /talk/tools/cppinsights.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsf-training/cpluspluscourse/20c735b86f594b85735fd3c65e3c202fced585da/talk/tools/cppinsights.png -------------------------------------------------------------------------------- /talk/tools/editors.tex: -------------------------------------------------------------------------------- 1 | \subsection[edit]{\cpp editor} 2 | 3 | \begin{frame}[fragile] 4 | \frametitle{\cpp editors and IDEs} 5 | \begin{block}{Can dramatically improve your efficiency by} 6 | \begin{itemize} 7 | \item Coloring the code for you to ``see'' the structure 8 | \item Helping with indenting and formatting properly 9 | \item Allowing you to easily navigate in the source tree 10 | \item Helping with compilation/debugging, profiling, static analysis 11 | \item Showing you errors and suggestions while typing 12 | \end{itemize} 13 | \end{block} 14 | \begin{block}{} 15 | \begin{description} 16 | \item[\href{http://www.microsoft.com/}{\beamergotobutton{Visual Studio}}] 17 | Heavy, fully fledged IDE for Windows 18 | \item[\href{https://code.visualstudio.com/}{\beamergotobutton{Visual Studio Code}}] 19 | Editor, open source, portable, many plugins 20 | \item[\href{https://www.eclipse.org/}{\beamergotobutton{Eclipse}}] 21 | IDE, open source, portable 22 | \item[\href{http://www.gnu.org/software/emacs/}{\beamergotobutton{Emacs}} \href{https://www.vim.org/}{\beamergotobutton{Vim}}] 23 | Editors for experts, extremely powerful. \\ 24 | They are to IDEs what latex is to PowerPoint 25 | \item[CLion, Code::Blocks, Atom, NetBeans, Sublime Text, ...] 26 | \end{description} 27 | Choosing one is mostly a matter of taste 28 | \end{block} 29 | \end{frame} 30 | -------------------------------------------------------------------------------- /talk/tools/formatting.tex: -------------------------------------------------------------------------------- 1 | \subsection[format]{Code formatting} 2 | 3 | \begin{frame}[fragile] 4 | \frametitle{clang-format} 5 | \begin{block}{.clang-format} 6 | \begin{itemize} 7 | \item File describing your formatting preferences 8 | \item Should be checked-in at the repository root (project wide) 9 | \item \mintinline{bash}{clang-format -style=LLVM -dump-config >} \\ 10 | \mintinline{bash}{.clang-format} 11 | \item Adapt style options with help from: \url{https://clang.llvm.org/docs/ClangFormatStyleOptions.html} 12 | \end{itemize} 13 | \end{block} 14 | \begin{block}{Run clang-format} 15 | \begin{itemize} 16 | \item \mintinline{bash}{clang-format --style=LLVM -i } 17 | \item \mintinline{bash}{clang-format -i } (looks for .clang-format file) 18 | \item \mintinline{bash}{git clang-format} (formats local changes) 19 | \item \mintinline{bash}{git clang-format } (formats changes since git \textless{}ref\textgreater{}) 20 | \item Most editors/IDEs can find a .clang-format file and adapt 21 | \end{itemize} 22 | \end{block} 23 | \end{frame} 24 | 25 | \begin{frame}[fragile] 26 | \frametitle{clang-format} 27 | \begin{exercise}{clang-format} 28 | \begin{itemize} 29 | \item Go to any example 30 | \item Format code with: \mintinline{bash}{clang-format --style=GNU -i } 31 | \item Inspect changes, try \mintinline{bash}{git diff .} 32 | \item Revert changes using \mintinline{bash}{git checkout -- } or \mintinline{bash}{git checkout .} 33 | \item Go to \texttt{exercises} directory and create a .clang-format file \\ 34 | \mintinline{bash}{clang-format -style=LLVM -dump-config >} \\ 35 | \mintinline{bash}{.clang-format} 36 | \item Run \mintinline{bash}{clang-format -i /*.cpp} 37 | \item Revert changes using \mintinline{bash}{git checkout } 38 | \end{itemize} 39 | \end{exercise} 40 | \end{frame} 41 | -------------------------------------------------------------------------------- /talk/tools/godbolt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsf-training/cpluspluscourse/20c735b86f594b85735fd3c65e3c202fced585da/talk/tools/godbolt.png -------------------------------------------------------------------------------- /talk/tools/profiling.tex: -------------------------------------------------------------------------------- 1 | \subsection[prof]{Profiling} 2 | 3 | \begin{frame}[fragile] 4 | \frametitle{Profiling} 5 | \begin{block}{Conceptually} 6 | \begin{itemize} 7 | \item take a measurement of a performance aspect of a program 8 | \begin{itemize} 9 | \item where in my code is most of the time spent? 10 | \item is my program compute or memory bound? 11 | \item does my program make good use of the cache? 12 | \item is my program using all cores most of the time? 13 | \item how often are threads blocked and why? 14 | \item which API calls are made and in which order? 15 | \item ... 16 | \end{itemize} 17 | \item the goal is to find performance bottlenecks 18 | \item is usually done on a compiled program, not on source code 19 | \end{itemize} 20 | \end{block} 21 | \end{frame} 22 | 23 | \begin{frame}[fragile] 24 | \frametitle{perf, VTune and uProf} 25 | \begin{block}{perf} 26 | \begin{itemize} 27 | \item perf is a powerful command line profiling tool for linux 28 | \item compile with \mintinline{bash}{-g -fno-omit-frame-pointer} 29 | \item \mintinline{bash}{perf stat -d } gathers performance statistics while running \mintinline{bash}{} 30 | \item \mintinline{bash}{perf record -g } starts profiling \mintinline{bash}{} 31 | \item \mintinline{bash}{perf report} displays a report from the last profile 32 | \item More information in \href{https://perf.wiki.kernel.org/index.php/Main_Page}{this wiki}, \href{https://www.brendangregg.com/linuxperf.html}{this website} or \href{https://indico.cern.ch/event/980497/contributions/4130271/attachments/2161581/3647235/linux-systems-performance.pdf}{this talk}. 33 | \end{itemize} 34 | \end{block} 35 | \begin{block}{Intel VTune and AMD uProf} 36 | \begin{itemize} 37 | \item Graphical profilers from CPU vendors with rich features 38 | \item Needs vendor's CPU for full experience 39 | \item More information on \href{https://www.intel.com/content/www/us/en/developer/tools/oneapi/vtune-profiler.html}{Intel's website} and \href{https://developer.amd.com/amd-uprof/}{AMD's website} 40 | \end{itemize} 41 | \end{block} 42 | \end{frame} 43 | -------------------------------------------------------------------------------- /talk/tools/vcs.tex: -------------------------------------------------------------------------------- 1 | \subsection[VCS]{Version control} 2 | 3 | \begin{frame}[fragile] 4 | \frametitle{Version control} 5 | \begin{alertblock}{Please use one!} 6 | \begin{itemize} 7 | \item Even locally 8 | \item Even on a single file 9 | \item Even if you are the only committer 10 | \end{itemize} 11 | It will soon save your day 12 | \end{alertblock} 13 | \begin{block}{A few tools} 14 | \begin{description} 15 | \item[\href{http://git-scm.com/}{\beamergotobutton{git}}] 16 | THE mainstream choice. Fast, light, easy to use 17 | \item[\href{http://mercurial.selenic.com/}{\beamergotobutton{mercurial}}] 18 | The alternative to git 19 | \item[\href{http://bazaar.canonical.com/en/}{\beamergotobutton{Bazaar}}] 20 | Another alternative 21 | \item[\href{https://subversion.apache.org/}{\beamergotobutton{Subversion}}] 22 | Historical, not distributed - don't use 23 | \item[\href{https://cvs.nongnu.org/}{\beamergotobutton{CVS}}] 24 | Archeological, not distributed - don't use 25 | \end{description} 26 | \end{block} 27 | \end{frame} 28 | 29 | \begin{frame}[fragile] 30 | \frametitle{Git crash course} 31 | \begin{minted}{shell-session} 32 | $ git init myProject 33 | Initialized empty Git repository in myProject/.git/ 34 | 35 | $ vim file.cpp; vim file2.cpp 36 | $ git add file.cpp file2.cpp 37 | $ git commit -m "Committing first 2 files" 38 | [master (root-commit) c481716] Committing first 2 files 39 | ... 40 | 41 | $ git log --oneline 42 | d725f2e Better STL test 43 | f24a6ce Reworked examples + added stl one 44 | bb54d15 implemented template part 45 | ... 46 | 47 | $ git diff f24a6ce bb54d15 48 | \end{minted} 49 | \end{frame} 50 | -------------------------------------------------------------------------------- /talk/tools/webtools.tex: -------------------------------------------------------------------------------- 1 | \subsection[web]{Web tools} 2 | 3 | \begin{frame} 4 | \frametitle{Godbolt / Compiler Explorer } 5 | \begin{block}{Concept} 6 | An online generic compiler with immediate feedback. 7 | Allows: 8 | \begin{itemize} 9 | \item trying various compilers in the browser 10 | \item inspecting the assembly generated 11 | \item use of external libraries (over 50 available !) 12 | \item running the produced code 13 | \item sharing small pieces of code via permanent short links 14 | \end{itemize} 15 | \end{block} 16 | \begin{exampleblock}{Typical usage} 17 | \begin{itemize} 18 | \item check small pieces of code on different compilers 19 | \item check some new \cpp functionality and its support 20 | \item optimize small pieces of code 21 | \item NOT relevant for large codes 22 | \end{itemize} 23 | \end{exampleblock} 24 | \end{frame} 25 | 26 | \begin{frame} 27 | \frametitle{Godbolt by example} 28 | \begin{block}{Check effect of optimization flags} 29 | \url{https://godbolt.org/z/Pb8WsWjEx} 30 | \begin{itemize} 31 | \item Check generated code with -O0, -O1, -O2, -O3 32 | \item See how it gets shorter and simpler 33 | \end{itemize} 34 | \end{block} 35 | \includegraphics[width=\textwidth]{tools/godbolt.png} 36 | \end{frame} 37 | 38 | \begin{frame} 39 | \frametitle{cppinsights} 40 | \begin{block}{Concept} 41 | Reveals the actual code behind \cpp syntactic sugar 42 | \begin{itemize} 43 | \item lambdas 44 | \item range-based loops 45 | \item templates 46 | \item initializations 47 | \item auto 48 | \item ... 49 | \end{itemize} 50 | \end{block} 51 | \begin{exampleblock}{Typical usage} 52 | \begin{itemize} 53 | \item understand how things work behind the \cpp syntax 54 | \item debug some non working pieces of code 55 | \end{itemize} 56 | \end{exampleblock} 57 | \end{frame} 58 | 59 | \begin{frame} 60 | \frametitle{cppinsights by example} 61 | \begin{block}{Check how range-based loop work} 62 | \url{https://cppinsights.io/s/b886aa76} 63 | \begin{itemize} 64 | \item See how they map to regular iterators 65 | \item And how operators are converted to function calls 66 | \end{itemize} 67 | \end{block} 68 | \includegraphics[width=\textwidth]{tools/cppinsights.png} 69 | \end{frame} 70 | -------------------------------------------------------------------------------- /talk/vscode-tricks.txt: -------------------------------------------------------------------------------- 1 | 2 | # From markdown to latex, with vscode replace 3 | 4 | `(.*?)` -> \\mintinline{cpp}{$1} 5 | \*(.*?)\* -> \\textbf{$1} 6 | 7 | # Other 8 | 9 | ## Replace {\it <=} with \mintinline{cpp}{<=} 10 | 11 | \{\\it (.*?)\} -> \\mintinline{cpp}{$1} 12 | -------------------------------------------------------------------------------- /talk/youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsf-training/cpluspluscourse/20c735b86f594b85735fd3c65e3c202fced585da/talk/youtube.png --------------------------------------------------------------------------------