diff --git a/.appends/.github/labels.yml b/.appends/.github/labels.yml index 211246a5a..5e702f9bd 100644 --- a/.appends/.github/labels.yml +++ b/.appends/.github/labels.yml @@ -11,6 +11,10 @@ description: "" color: "fc2929" +- name: "on-hold" + description: "" + color: "cccccc" + - name: "dependencies" description: "Pull requests that update a dependency file" color: "0366d6" diff --git a/.clang-format b/.clang-format index 9a9cbf817..ed9165255 100644 --- a/.clang-format +++ b/.clang-format @@ -3,4 +3,4 @@ BasedOnStyle: Google # Customize with larger indents IndentWidth: 4 -ObjCBlockIndentWidth: 4 \ No newline at end of file +ObjCBlockIndentWidth: 4 diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ed8f4a432..234b07e76 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,4 +6,4 @@ updates: - package-ecosystem: 'github-actions' directory: '/' schedule: - interval: 'daily' + interval: 'monthly' diff --git a/.github/labels.yml b/.github/labels.yml index 6d6d460c1..bc8b1ec98 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -157,16 +157,16 @@ description: "Work on Documentation" color: "ffffff" -# This label can be added to accept PRs as part of Hacktoberfest -- name: "hacktoberfest-accepted" - description: "Make this PR count for hacktoberfest" - color: "ff7518" - # This Exercism-wide label is added to all automatically created pull requests that help migrate/prepare a track for Exercism v3 - name: "v3-migration 🤖" description: "Preparing for Exercism v3" color: "e99695" +# This Exercism-wide label can be used to bulk-close issues in preparation for pausing community contributions +- name: "paused" + description: "Work paused until further notice" + color: "e4e669" + # ----------------------------------------------------------------------------------------- # # These are the repository-specific labels that augment the Exercise-wide labels defined in # # https://github.com/exercism/org-wide-files/blob/main/global-files/.github/labels.yml. # @@ -180,6 +180,10 @@ description: "" color: "fc2929" +- name: "on-hold" + description: "" + color: "cccccc" + - name: "dependencies" description: "Pull requests that update a dependency file" color: "0366d6" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 427110410..867639527 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,16 +11,16 @@ on: jobs: linux-min: name: Linux Min Config - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 strategy: matrix: - compiler: [clang++-6.0, g++-7] + compiler: [clang++-15, g++-12] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Install Dependencies # Boost must be installed only because the CMake version (3.12) can't # detect the installed Boost version (1.69) - run: sudo apt-get update && sudo apt-get -y install ninja-build libboost-date-time-dev clang-6.0 g++-7 g++-multilib + run: sudo apt-get update && sudo apt-get -y install ninja-build libboost-date-time-dev g++-multilib - name: Run tests with common Catch run: | cmake -G Ninja . @@ -32,12 +32,12 @@ jobs: linux-latest: name: Linux Latest Config needs: [linux-min] - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 strategy: matrix: compiler: [clang++, g++] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Install Dependencies # Boost must be installed only because the CMake version (3.12) can't # detect the installed Boost version (1.69) @@ -62,9 +62,9 @@ jobs: windows: name: Windows needs: [linux-min] - runs-on: windows-latest + runs-on: windows-2022 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Run Tests shell: powershell # Delete the exercises that require Boost to avoid issues with Windows setup. @@ -80,7 +80,7 @@ jobs: needs: [linux-min] runs-on: macOS-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Install Boost run: brew install boost - name: Run Tests @@ -88,4 +88,3 @@ jobs: cmake . cmake --build . -- test_hello-world cmake --build . - diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index 1790710fd..5fac99bbc 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -7,7 +7,7 @@ on: jobs: format: name: "Format C++ code" - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: github.event.issue.pull_request != '' && (contains(github.event.comment.body, '/format') || contains(github.event.comment.body, '/clang-format')) steps: - name: "Checkout code" diff --git a/.github/workflows/hello-world.yml b/.github/workflows/hello-world.yml index fd863f6fa..e75d287e1 100644 --- a/.github/workflows/hello-world.yml +++ b/.github/workflows/hello-world.yml @@ -19,10 +19,10 @@ on: jobs: hello-world-fails: name: Hello World Fails - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 - name: Check Hello World Fails run: bin/check-hello-world.sh env: - CXX: ${{ matrix.compiler }} \ No newline at end of file + CXX: ${{ matrix.compiler }} diff --git a/.github/workflows/no-important-files-changed.yml b/.github/workflows/no-important-files-changed.yml new file mode 100644 index 000000000..812e91296 --- /dev/null +++ b/.github/workflows/no-important-files-changed.yml @@ -0,0 +1,23 @@ +name: No important files changed + +on: + pull_request_target: + types: [opened] + branches: [main] + paths: + - "exercises/concept/**" + - "exercises/practice/**" + - "!exercises/*/*/.approaches/**" + - "!exercises/*/*/.articles/**" + - "!exercises/*/*/.docs/**" + - "!exercises/*/*/.meta/**" + +permissions: + pull-requests: write + +jobs: + check: + uses: exercism/github-actions/.github/workflows/check-no-important-files-changed.yml@main + with: + repository: ${{ github.event.pull_request.head.repo.owner.login }}/${{ github.event.pull_request.head.repo.name }} + ref: ${{ github.head_ref }} diff --git a/.github/workflows/pause-community-contributions.yml b/.github/workflows/pause-community-contributions.yml new file mode 100644 index 000000000..4798e29d3 --- /dev/null +++ b/.github/workflows/pause-community-contributions.yml @@ -0,0 +1,25 @@ +name: Pause Community Contributions + +on: + issues: + types: + - opened + pull_request_target: + types: + - opened + paths-ignore: + - 'exercises/*/*/.approaches/**' + - 'exercises/*/*/.articles/**' + +permissions: + issues: write + pull-requests: write + +jobs: + pause: + if: github.repository_owner == 'exercism' # Stops this job from running on forks + uses: exercism/github-actions/.github/workflows/community-contributions.yml@main + with: + forum_category: cpp + secrets: + github_membership_token: ${{ secrets.COMMUNITY_CONTRIBUTIONS_WORKFLOW_TOKEN }} diff --git a/.github/workflows/ping-cross-track-maintainers-team.yml b/.github/workflows/ping-cross-track-maintainers-team.yml new file mode 100644 index 000000000..b6ec9c566 --- /dev/null +++ b/.github/workflows/ping-cross-track-maintainers-team.yml @@ -0,0 +1,16 @@ +name: Ping cross-track maintainers team + +on: + pull_request_target: + types: + - opened + +permissions: + pull-requests: write + +jobs: + ping: + if: github.repository_owner == 'exercism' # Stops this job from running on forks + uses: exercism/github-actions/.github/workflows/ping-cross-track-maintainers-team.yml@main + secrets: + github_membership_token: ${{ secrets.COMMUNITY_CONTRIBUTIONS_WORKFLOW_TOKEN }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 000000000..777d3396c --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,24 @@ +name: Pre-commit checks + +on: + # Run workflow for PRs. + pull_request: + + # Whenever we have a new commit on main, run the workflow for that. + push: + branches: [main] + +jobs: + pre-commit: + name: Pre-commit checks + runs-on: ubuntu-26.04 + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{github.event.pull_request.head.sha}} + repository: ${{github.event.pull_request.head.repo.full_name}} + - name: Set up Python + uses: actions/setup-python@v7 + - name: Run pre-commit checks on PR + uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/run-configlet-sync.yml b/.github/workflows/run-configlet-sync.yml new file mode 100644 index 000000000..c175036c1 --- /dev/null +++ b/.github/workflows/run-configlet-sync.yml @@ -0,0 +1,10 @@ +name: Run Configlet Sync + +on: + workflow_dispatch: + schedule: + - cron: '0 0 15 * *' + +jobs: + configlet-sync: + uses: exercism/github-actions/.github/workflows/configlet-sync.yml@main diff --git a/.github/workflows/verify-code-formatting.yml b/.github/workflows/verify-code-formatting.yml deleted file mode 100644 index c07e3f019..000000000 --- a/.github/workflows/verify-code-formatting.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: "Verify C++ code formatting" - -on: - push: - paths: - - "**/*.cpp" - - "**/*.h" - - "**/*.hpp" - - ".clang-format" - - "!**/catch.hpp" - pull_request: - paths: - - "**/*.cpp" - - "**/*.h" - - "**/*.hpp" - - ".clang-format" - - "!**/catch.hpp" - -jobs: - verify-code-formatting: - runs-on: [ubuntu-latest] - steps: - - name: "Checkout code" - uses: actions/checkout@v3 - - - name: "Verify formatting of all files" - if: github.ref == 'refs/heads/master' - run: | - find . -type f \( -iname '*.cpp' -o -iname '*.h' -o -iname '*.hpp' \) -a ! -iname "catch.hpp" | \ - xargs bin/check-formatting.sh - - - name: "Verify formatting of the PR's files" - if: github.ref != 'refs/heads/master' && github.event.pull_request.url - run: | - PULL_REQUEST_URL=$(jq -r ".pull_request.url" "$GITHUB_EVENT_PATH") - curl --url $"${PULL_REQUEST_URL}/files" --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' | \ - jq -c '.[] | select(.status == "added" or .status == "modified") | select(.filename | match("(?&2 + return 1 + fi + + local os + case "$(uname -s)" in + Darwin*) os='macos' ;; + Linux*) os='linux' ;; + Windows*) os='windows' ;; + MINGW*) os='windows' ;; + MSYS_NT-*) os='windows' ;; + *) os='linux' ;; + esac + + local ext + case "${os}" in + windows) ext='zip' ;; + *) ext='tar.gz' ;; + esac -case "${ext}" in - *zip) unzip "${output_path}" -d "${output_dir}" ;; - *) tar xzf "${output_path}" -C "${output_dir}" ;; -esac + echo "Fetching configlet..." >&2 + local download_url + download_url="$(get_download_url "${os}" "${ext}")" + local output_path="${output_dir}/latest-configlet.${ext}" + curl "${curlopts[@]}" --output "${output_path}" "${download_url}" + + case "${ext}" in + zip) unzip "${output_path}" -d "${output_dir}" ;; + *) tar xzf "${output_path}" -C "${output_dir}" ;; + esac + + rm -f "${output_path}" + + local executable_ext + case "${os}" in + windows) executable_ext='.exe' ;; + *) executable_ext='' ;; + esac + + local configlet_path="${output_dir}/configlet${executable_ext}" + local configlet_version + configlet_version="$(${configlet_path} --version)" + echo "Downloaded configlet ${configlet_version} to ${configlet_path}" +} -rm -f "${output_path}" +main diff --git a/bin/fetch-configlet.ps1 b/bin/fetch-configlet.ps1 index 291e57eb2..a7896b225 100755 --- a/bin/fetch-configlet.ps1 +++ b/bin/fetch-configlet.ps1 @@ -1,26 +1,42 @@ +# This file is a copy of the +# https://github.com/exercism/configlet/blob/main/scripts/fetch-configlet.ps1 file. +# Please submit bugfixes/improvements to the above file to ensure that all tracks +# benefit from the changes. + $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" $requestOpts = @{ - Headers = If ($env:GITHUB_TOKEN) { @{ Authorization = "Bearer ${env:GITHUB_TOKEN}" } } Else { @{ } } + Headers = If ($env:GITHUB_TOKEN) { @{ Authorization = "Bearer ${env:GITHUB_TOKEN}" } } Else { @{ } } MaximumRetryCount = 3 - RetryIntervalSec = 1 + RetryIntervalSec = 1 } -$arch = If ([Environment]::Is64BitOperatingSystem) { "64bit" } Else { "32bit" } -$fileName = "configlet-windows-$arch.zip" - Function Get-DownloadUrl { + $arch = If ([Environment]::Is64BitOperatingSystem) { "x86-64" } Else { "i386" } $latestUrl = "https://api.github.com/repos/exercism/configlet/releases/latest" - Invoke-RestMethod -Uri $latestUrl -PreserveAuthorizationOnRedirect @requestOpts - | Select-Object -ExpandProperty assets - | Where-Object { $_.browser_download_url -match $FileName } - | Select-Object -ExpandProperty browser_download_url + Invoke-RestMethod -Uri $latestUrl -PreserveAuthorizationOnRedirect @requestOpts ` + | Select-Object -ExpandProperty assets ` + | Where-Object { $_.name -match "^configlet_.+_windows_${arch}.zip$" } ` + | Select-Object -ExpandProperty browser_download_url -First 1 } -$downloadUrl = Get-DownloadUrl $outputDirectory = "bin" -$outputFile = Join-Path -Path $outputDirectory -ChildPath $fileName -Invoke-WebRequest -Uri $downloadUrl -OutFile $outputFile @requestOpts -Expand-Archive $outputFile -DestinationPath $outputDirectory -Force -Remove-Item -Path $outputFile +if (!(Test-Path -Path $outputDirectory)) { + Write-Output "Error: no ./bin directory found. This script should be ran from a repo root." + exit 1 +} + +Write-Output "Fetching configlet..." +$downloadUrl = Get-DownloadUrl +$outputFileName = "configlet.zip" +$outputPath = Join-Path -Path $outputDirectory -ChildPath $outputFileName +Invoke-WebRequest -Uri $downloadUrl -OutFile $outputPath @requestOpts + +$configletPath = Join-Path -Path $outputDirectory -ChildPath "configlet.exe" +if (Test-Path -Path $configletPath) { Remove-Item -Path $configletPath } +[System.IO.Compression.ZipFile]::ExtractToDirectory($outputPath, $outputDirectory) +Remove-Item -Path $outputPath + +$configletVersion = (Select-String -Pattern "/releases/download/(.+?)/" -InputObject $downloadUrl -AllMatches).Matches.Groups[1].Value +Write-Output "Downloaded configlet ${configletVersion} to ${configletPath}" diff --git a/bin/make-stub-files.sh b/bin/make-stub-files.sh index e38ddc880..76eab0b27 100755 --- a/bin/make-stub-files.sh +++ b/bin/make-stub-files.sh @@ -21,10 +21,8 @@ for exercise_dir in "${exercises_dir}"/*; do header="$exercise_name.h" source="$exercise_name.cpp" if ! test -f "$header"; then - printf "#if !defined(${exercise_name^^}_H)\n" >> $header - printf "#define ${exercise_name^^}_H\n" >> $header + printf "#pragma once\n" >> $header printf "\nnamespace $exercise_name {\n\n} // namespace $exercise_name\n" >> $header - printf "\n#endif // ${exercise_name^^}_H" >> $header git add $header fi if ! test -f "$source"; then diff --git a/concepts/auto/.meta/config.json b/concepts/auto/.meta/config.json new file mode 100644 index 000000000..9a4474216 --- /dev/null +++ b/concepts/auto/.meta/config.json @@ -0,0 +1,6 @@ +{ + "blurb": "The `auto` keyword in C++ allows automatic type deduction, simplfying code by letting the compiler infer variable types from their initializers.", + "authors": [ + "vaeng" + ] +} diff --git a/concepts/auto/about.md b/concepts/auto/about.md new file mode 100644 index 000000000..90892a5ab --- /dev/null +++ b/concepts/auto/about.md @@ -0,0 +1,48 @@ +# About + +## The `auto` Keyword in C++ + +In C++, the `auto` keyword is a powerful feature introduced in C++11, used to declare variables with an inferred data type. +The compiler deduces the type of the variable based on its initializer, which can make code more readable and easier to maintain. + +## Example Usage + +Consider the following example where `auto` is used to declare variables: + +```cpp +auto dragon_population{3}; // dragon_population is deduced as an integer +auto westeros{7.7777}; // westeros is deduced as a double +auto wedding_location{"The Twins"}; // wedding_location is deduced as a const char*, not std::string +``` + +In each case, the type of the variable is inferred from the value it is initialized with. + +## Type Inference + +The `auto` keyword helps by writing more concise and readable code by reducing the verbosity of explicit types. + +```cpp +const std::vector pigeon_pie{"flour", "butter", "pigeon", "salt"}; +auto purple_wedding_pie{pigeon_pie}; +purple_wedding_pie.emplace_back("the strangler"); +``` + +In this loop, `auto` deduces the type of `purple_wedding_pie` as `std::vector`, avoiding the need to explicitly specify the type again. + +## Compatibility + +The `auto` keyword is compatible with various C++ constructs making it a versatile tool in modern C++ programming. + +```cpp +auto& element{array[0]}; // reference to an element +const auto object{otherObject}; // const type version of otherObject's type +auto* ptr{&x}; // pointer to x with the same type as x, but as a pointer. +``` + +In later concept we will often see the `auto` keyword with lambda expressions, range-based for-loops, and iterators. + +## Use Cases + +- **Generic Programming**: `auto` is particularly useful in generic programming scenarios where types may be complex or template-related. +- **Complex Data Types**: When dealing with complex data types or long type names, `auto` can reduce clutter and improve code maintainability. +- **Iterator-Based Algorithms**: It simplifies the use of iterators in algorithms, enhancing code readability. diff --git a/concepts/auto/introduction.md b/concepts/auto/introduction.md new file mode 100644 index 000000000..64ea85ec9 --- /dev/null +++ b/concepts/auto/introduction.md @@ -0,0 +1,42 @@ +# Introduction + +## The `auto` Keyword in C++ + +In C++, the `auto` keyword is a powerful feature introduced in C++11, used to declare variables with an inferred data type. +The compiler deduces the type of the variable based on its initializer, which can make code more readable and easier to maintain. + +## Example Usage + +Consider the following example where `auto` is used to declare variables: + +```cpp +auto dragon_population{3}; // dragon_population is deduced as an integer +auto westeros{7.7777}; // westeros is deduced as a double +auto wedding_location{"The Twins"}; // wedding_location is deduced as a const char*, not std::string +``` + +In each case, the type of the variable is inferred from the value it is initialized with. + +## Type Inference + +The `auto` keyword helps by writing more concise and readable code by reducing the verbosity of explicit types. + +```cpp +const std::vector pigeon_pie{"flour", "butter", "pigeon", "salt"}; +auto purple_wedding_pie{pigeon_pie}; +purple_wedding_pie.emplace_back("the strangler"); +``` + +In this loop, `auto` deduces the type of `purple_wedding_pie` as `std::vector`, avoiding the need to explicitly specify the type again. + +## Compatibility + +The `auto` keyword is compatible with various C++ constructs making it a versatile tool in modern C++ programming. + +```cpp +auto& element{array[0]}; // reference to an element +const auto object{otherObject}; // const type version of otherObject's type +auto* ptr{&x}; // pointer to x with the same type as x, but as a pointer. +``` + +In later concept we will often see the `auto` keyword with lambda expressions, range-based for-loops, and iterators. diff --git a/concepts/auto/links.json b/concepts/auto/links.json new file mode 100644 index 000000000..87f0bf23d --- /dev/null +++ b/concepts/auto/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/auto", + "description": "C++ reference documentation for the auto keyword" + }, + { + "url": "https://www.learncpp.com/cpp-tutorial/type-deduction-for-objects-using-the-auto-keyword/", + "description": "Learn C++ overview on the auto keyword and its usage" + } +] diff --git a/concepts/basics/.meta/config.json b/concepts/basics/.meta/config.json new file mode 100644 index 000000000..f7afdf354 --- /dev/null +++ b/concepts/basics/.meta/config.json @@ -0,0 +1,11 @@ +{ + "blurb": "C++ is a statically-typed language, which means that everything has a type at compile-time. C++ allows one to use Object-Oriented, Functional and Procedural coding styles.", + "authors": [ + "vaeng" + ], + "contributors": [ + "MatthijsBlom", + "siebenschlaefer", + "b8horpet" + ] +} diff --git a/concepts/basics/about.md b/concepts/basics/about.md new file mode 100644 index 000000000..764b6b3e8 --- /dev/null +++ b/concepts/basics/about.md @@ -0,0 +1,113 @@ +# About + +To set off the C++ journey we are starting with variables, function calls, and comments. + +## Comments + +Comments come in two flavors: single- and multi-line. +Everything that comes after `//` on the same line is ignored by the compiler. +Multi-line comments are also known as C-style comments. +They are surrounded by `/*` and `*/`. +Anything that comes between these will be ignored as well. + +## Variables + +C++ is a typed language. +All types need to be known at compile time, and you generally need to state them explicitly. +A variable's type cannot change. +An integer variable with the name `years` can be declared like this: + +```cpp +int years; +``` + +It is good practice to initialize variables upon declaration. +C++ offers different mechanisms to do so. +The version with the curly braces is more in line with modern C++, but the equal-sign version is also very common. + +```cpp +int tomatoes{80}; +int potatoes = 40; +``` + +~~~~exercism/caution +C++ does allow using uninitialized variables. +Until the variable is deliberately set, it is undefined and might contain anything. +To avoid used-before-set errors and undefined behavior it is adviseable to **always initialize**. +Undefined behavior can crash your program at the worst possible moment, while it was running fine previously. +It cannot be stressed enough: avoid undefined behavior at all cost. +~~~~ + +## Arithmetic Operations + +Arithmetic operators like `*`, `+`, or `-` can be part of an expression like `3 * 2` or `tomatoes + potatoes`. + +## Updating Variables + +You can reassign variables, as long as they keep their type: + +```cpp +tomatoes = tomatoes - 5; // tomatoes is now 75 +potatoes = (32 * 2) + 11; // potatoes is now 75 as well +``` + +## Functions + +Functions have a name, a return type and a (possibly empty) parameter list. +An example of a function named `always_fortyseven` that would always return 47 would look like this: + +```cpp +int always_fortyseven() { + return 47; +} +``` + +Here is `vip_fee`, which has one parameter: +```cpp +int vip_fee(int standard_fee) { + /* + vip_fee calculates the vip fee based on the standard_fee. + */ + int vip_multi{3}; + return standard_fee * vip_multi; +} +``` + +Or `total_fee`, a function with three parameters and a call to another function. + +```cpp +int total_fee(int vips, int adults, int kids) { + /* + total_fee calculates the total price for a group of VIP and adult guests with kids. + Kids get a flat discount on the standard fee. + VIP guest fees are calculated by calling vip_fee. + */ + int standard_fee{30}; + int kids_discount{15}; + + int kids_total_fee = kids * (standard_fee - kids_discount); + int vips_total_fee = vips * vip_fee(standard_fee); + int adult_total_fee = adults * standard_fee; + + return vips_total_fee + adult_total_fee + kids_total_fee; +} +``` + +Functions in C++ do not return the value of the last statement like in some other languages. +The `return` keyword is required for the code to compile. + +### Whitespace + +Whitespace is used for formatting source code and includes spaces, tabs, or newlines. +As the compiler ignores unnecessary whitespace, you can use it to structure your code. +Smart use of whitespace can improve the readability of your code. +There are different formatting standards, but these are all conventional and not enforced by the compiler. + +```cpp +// Formatting makes it easier to find bugs + +int eggs_yolks = 3; +int yeast = 15; + +int flour=500;int sugar=200;// compact, but difficult to read +``` diff --git a/concepts/basics/introduction.md b/concepts/basics/introduction.md new file mode 100644 index 000000000..c2512452f --- /dev/null +++ b/concepts/basics/introduction.md @@ -0,0 +1,113 @@ +# Introduction + +To set off the C++ journey we are starting with variables, function calls, and comments. + +## Comments + +Comments come in two flavors: single- and multi-line. +Everything that comes after `//` on the same line is ignored by the compiler. +Multi-line comments are also known as C-style comments. +They are surrounded by `/*` and `*/`. +Anything that comes between these will be ignored as well. + +## Variables + +C++ is a typed language. +All types need to be known at compile time, and you generally need to state them explicitly. +A variable's type cannot change. +An integer variable with the name `years` can be declared like this: + +```cpp +int years; +``` + +It is good practice to initialize variables upon declaration. +C++ offers different mechanisms to do so. +The version with the curly braces is more in line with modern C++, but the equal-sign version is also very common. + +```cpp +int tomatoes{80}; +int potatoes = 40; +``` + +~~~~exercism/caution +C++ does allow using uninitialized variables. +Until the variable is deliberately set, it is undefined and might contain anything. +To avoid used-before-set errors and undefined behavior it is adviseable to **always initialize**. +Undefined behavior can crash your program at the worst possible moment, while it was running fine previously. +It cannot be stressed enough: avoid undefined behavior at all cost. +~~~~ + +## Arithmetic Operations + +Arithmetic operators like `*`, `+`, or `-` can be part of an expression like `3 * 2` or `tomatoes + potatoes`. + +## Updating Variables + +You can reassign variables, as long as they keep their type: + +```cpp +tomatoes = tomatoes - 5; // tomatoes is now 75 +potatoes = (32 * 2) + 11; // potatoes is now 75 as well +``` + +## Functions + +Functions have a name, a return type and a (possibly empty) parameter list. +An example of a function named `always_fortyseven` that would always return 47 would look like this: + +```cpp +int always_fortyseven() { + return 47; +} +``` + +Here is `vip_fee`, which has one parameter: +```cpp +int vip_fee(int standard_fee) { + /* + vip_fee calculates the vip fee based on the standard_fee. + */ + int vip_multi{3}; + return standard_fee * vip_multi; +} +``` + +Or `total_fee`, a function with three parameters and a call to another function. + +```cpp +int total_fee(int vips, int adults, int kids) { + /* + total_fee calculates the total price for a group of VIP and adult guests with kids. + Kids get a flat discount on the standard fee. + VIP guest fees are calculated by calling vip_fee. + */ + int standard_fee{30}; + int kids_discount{15}; + + int kids_total_fee = kids * (standard_fee - kids_discount); + int vips_total_fee = vips * vip_fee(standard_fee); + int adult_total_fee = adults * standard_fee; + + return vips_total_fee + adult_total_fee + kids_total_fee; +} +``` + +Functions in C++ do not return the value of the last statement like in some other languages. +The `return` keyword is required for the code to compile. + +### Whitespace + +Whitespace is used for formatting source code and includes spaces, tabs, or newlines. +As the compiler ignores unnecessary whitespace, you can use it to structure your code. +Smart use of whitespace can improve the readability of your code. +There are different formatting standards, but these are all conventional and not enforced by the compiler. + +```cpp +// Formatting makes it easier to find bugs + +int eggs_yolks = 3; +int yeast = 15; + +int flour=500;int sugar=200;// compact, but difficult to read +``` diff --git a/concepts/basics/links.json b/concepts/basics/links.json new file mode 100644 index 000000000..690b9bc3d --- /dev/null +++ b/concepts/basics/links.json @@ -0,0 +1,14 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp", + "description": "C++ reference and overview of the standard library" + }, + { + "url": "https://isocpp.org/wiki/faq/how-to-learn-cpp#best-book", + "description": "What is the best book to learn C++ from?" + }, + { + "url": "https://isocpp.org/faq", + "description": "C++ Super-FAQ" + } +] diff --git a/concepts/booleans/.meta/config.json b/concepts/booleans/.meta/config.json new file mode 100644 index 000000000..b50b7332e --- /dev/null +++ b/concepts/booleans/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "In C++, the boolean type enables working with logical values, allowing for boolean operations such as AND, OR, and NOT to evaluate and manipulate conditions.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/booleans/about.md b/concepts/booleans/about.md new file mode 100644 index 000000000..cc85316f1 --- /dev/null +++ b/concepts/booleans/about.md @@ -0,0 +1,38 @@ +# About + +Booleans in C++ are represented by the `bool` type. +A `bool` is either `true` or `false`. + +## Logical Operators + +C++ supports three boolean operators: `!` (NOT), `&&` (AND), and `||` (OR). +You can also use the alternative versions `not`, `and`, and `or`. + +```cpp +true || false // => true +true && false // => false +!true // => false +not false // => true +``` + +## Precedence + +The three boolean operators each have different _operator precedence_. +As a consequence, they are evaluated in this order: `!` first, `&&` second, and finally `||`. +If you want to force a different ordering, you can enclose a boolean expression in parentheses (ie. `()`), as the parentheses have even higher operator precedence. + +```cpp +!true && false // => false +!(true and false) // => true +``` +~~~~exercism/advanced +## Conversion + +If you use `true` or `false` in a place where a number is expected, they will be converted to `1` and `0` respectively. +If you use a number in a Boolean operation, everything except `0` is treated as `true` - even negative values. + +```cpp +!true && 0.0 // => false +true + true + false // => 2 +``` +~~~~ diff --git a/concepts/booleans/introduction.md b/concepts/booleans/introduction.md new file mode 100644 index 000000000..ad0ee9e12 --- /dev/null +++ b/concepts/booleans/introduction.md @@ -0,0 +1,38 @@ +# Introduction + +Booleans in C++ are represented by the `bool` type. +A `bool` is either `true` or `false`. + +## Logical Operators + +C++ supports three boolean operators: `!` (NOT), `&&` (AND), and `||` (OR). +You can also use the alternative versions `not`, `and`, and `or`. + +```cpp +true || false // => true +true && false // => false +!true // => false +not false // => true +``` + +## Precedence + +The three boolean operators each have different _operator precedence_. +As a consequence, they are evaluated in this order: `!` first, `&&` second, and finally `||`. +If you want to force a different ordering, you can enclose a boolean expression in parentheses (ie. `()`), as the parentheses have even higher operator precedence. + +```cpp +!true && false // => false +!(true and false) // => true +``` +~~~~exercism/advanced +## Conversion + +If you use `true` or `false` in a place where a number is expected, they will be converted to `1` and `0` respectively. +If you use a number in a Boolean operation, everything except `0` is treated as `true` - even negative values. + +```cpp +!true && 0.0 // => false +true + true + false // => 2 +``` +~~~~ diff --git a/concepts/booleans/links.json b/concepts/booleans/links.json new file mode 100644 index 000000000..3368e317b --- /dev/null +++ b/concepts/booleans/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/operator_logical", + "description": "C++ reference on Logical operators" + } +] diff --git a/concepts/classes/.meta/config.json b/concepts/classes/.meta/config.json new file mode 100644 index 000000000..0d971637f --- /dev/null +++ b/concepts/classes/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "In C++, classes are types with their own set of characteristics and abilities. They make it easier to organize and reuse code by grouping related data and functions together in a structured and organized way.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/classes/about.md b/concepts/classes/about.md new file mode 100644 index 000000000..bb94b0334 --- /dev/null +++ b/concepts/classes/about.md @@ -0,0 +1,106 @@ +# About + + +It is time to get to one of the core paradigms of C++: object-oriented programming (OOP). +OOP is centered around `classes` - user-defined types of data with their own set of related functions. +We will start with the basics and will cover more advanced topics further down the syllabus tree. + +## Members + +Classes can have **member variables** and **member functions**. +They are accessed by the **member selection** operator `.`. +Just as variables outside of `classes`, it is advisable to initialize member variables with a value upon declaration. +This value will then become the default for newly created objects from this class. + +## Encapsulation and Information Hiding + +Classes offer the option to restrict access to their members. +The two basic cases are `private` and `public`. +`private` members are not accessible from outside the class. +`public` members can be called freely. +All members are `private` by default and need to be explicitly marked to be usable outside of the class. + +## Basic example + +The definition of a new `class` can be seen in the following example. +Notice the `;` after the definition. + +```cpp +class Wizzard { + public: // from here on all members are accessible publicly + int cast_spell() { // defines the public member function cast_spell + return damage; + } + std::string name{}; // defines the public member variable `name` + private: // from here on all members are private + int damage{5}; // defines the private member variable `damage` +}; + +``` + +You can access all variables from within the class. +Take a look at `damage` inside the `cast_spell` function. +You cannot read or change `private` members outside of the class: + +```cpp +Wizzard silverhand{}; +// calling the `cast_spell` function is okay, it is public: +silverhand.cast_spell(); +// => 5 + +// name is public and can be changed: +silverhand.name = "Laeral"; + +// damage is private: +silverhand.damage = 500; + // => Compilation error +``` + +## Constructors + +Constructors offer the possibility to assign values to member variables at object creation. +They have the same name as the `class` and do not have a return type. +A class can have several constructors. +This is useful if you do not always have a need to set all variables. + +```cpp +class Wizzard { + public: + Wizzard(std::string new_name) { + name = new_name; + } + Wizzard(std::string new_name, int new_damage) { + name = new_name; + damage = new_damage; + } + int cast_spell() { + return damage; + } + std::string name{}; + private: + int damage{5}; +}; + +Wizzard el{"Eleven"}; // deals 5 damage +Wizzard vecna{"Vecna", 50}; // deals 50 damage +``` + +Constructors are a big topic and have many nuances. +If you are not explicitly defining a `constructor` for your `class`, then - and only then - the compiler will do the job for you. +This has happened in the first example above. +The _silverhand_ object is created by calling the default constructor, no arguments were passed. +All variables are set to the value that was stated in the definition of the class. +If you had not given any values in that definition, the variables might be uninitialized, which might have unintended consequences. + +~~~~exercism/note +## Structs + +Structs came from the language's original C roots and are as old as C++ itself. +They are effectively the same thing as `classes` with one important exception. +By default, everything in a `class` is `private`. +Structs, on the other hand, are `public` until defined otherwise. +Conventionally, the `struct` keyword is often used for **data-only structures**. +The `class` keyword is preferred for objects that need to ensure certain properties. +Such an invariant could be that the `damage` of your `Wizzard` `class` cannot turn negative. +The `damage` variable is private and any function that changes the damage would ensure the invariant is preserved. +~~~~ diff --git a/concepts/classes/introduction.md b/concepts/classes/introduction.md new file mode 100644 index 000000000..e9a957bc5 --- /dev/null +++ b/concepts/classes/introduction.md @@ -0,0 +1,105 @@ +# Introduction + +It is time to get to one of the core paradigms of C++: object-oriented programming (OOP). +OOP is centered around `classes` - user-defined types of data with their own set of related functions. +We will start with the basics and will cover more advanced topics further down the syllabus tree. + +## Members + +Classes can have **member variables** and **member functions**. +They are accessed by the **member selection** operator `.`. +Just as variables outside of `classes`, it is advisable to initialize member variables with a value upon declaration. +This value will then become the default for newly created objects from this class. + +## Encapsulation and Information Hiding + +Classes offer the option to restrict access to their members. +The two basic cases are `private` and `public`. +`private` members are not accessible from outside the class. +`public` members can be called freely. +All members are `private` by default and need to be explicitly marked to be usable outside of the class. + +## Basic example + +The definition of a new `class` can be seen in the following example. +Notice the `;` after the definition. + +```cpp +class Wizzard { + public: // from here on all members are accessible publicly + int cast_spell() { // defines the public member function cast_spell + return damage; + } + std::string name{}; // defines the public member variable `name` + private: // from here on all members are private + int damage{5}; // defines the private member variable `damage` +}; + +``` + +You can access all variables from within the class. +Take a look at `damage` inside the `cast_spell` function. +You cannot read or change `private` members outside of the class: + +```cpp +Wizzard silverhand{}; +// calling the `cast_spell` function is okay, it is public: +silverhand.cast_spell(); +// => 5 + +// name is public and can be changed: +silverhand.name = "Laeral"; + +// damage is private: +silverhand.damage = 500; + // => Compilation error +``` + +## Constructors + +Constructors offer the possibility to assign values to member variables at object creation. +They have the same name as the `class` and do not have a return type. +A class can have several constructors. +This is useful if you do not always have a need to set all variables. + +```cpp +class Wizzard { + public: + Wizzard(std::string new_name) { + name = new_name; + } + Wizzard(std::string new_name, int new_damage) { + name = new_name; + damage = new_damage; + } + int cast_spell() { + return damage; + } + std::string name{}; + private: + int damage{5}; +}; + +Wizzard el{"Eleven"}; // deals 5 damage +Wizzard vecna{"Vecna", 50}; // deals 50 damage +``` + +Constructors are a big topic and have many nuances. +If you are not explicitly defining a `constructor` for your `class`, then - and only then - the compiler will do the job for you. +This has happened in the first example above. +The _silverhand_ object is created by calling the default constructor, no arguments were passed. +All variables are set to the value that was stated in the definition of the class. +If you had not given any values in that definition, the variables might be uninitialized, which might have unintended consequences. + +~~~~exercism/note +## Structs + +Structs came from the language's original C roots and are as old as C++ itself. +They are effectively the same thing as `classes` with one important exception. +By default, everything in a `class` is `private`. +Structs, on the other hand, are `public` until defined otherwise. +Conventionally, the `struct` keyword is often used for **data-only structures**. +The `class` keyword is preferred for objects that need to ensure certain properties. +Such an invariant could be that the `damage` of your `Wizzard` `class` cannot turn negative. +The `damage` variable is private and any function that changes the damage would ensure the invariant is preserved. +~~~~ diff --git a/concepts/classes/links.json b/concepts/classes/links.json new file mode 100644 index 000000000..cd994028c --- /dev/null +++ b/concepts/classes/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/classes", + "description": "C++ reference on for classes" + } +] diff --git a/concepts/comparisons/.meta/config.json b/concepts/comparisons/.meta/config.json new file mode 100644 index 000000000..c608d01bf --- /dev/null +++ b/concepts/comparisons/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "Compare values using the comparison operators.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/comparisons/about.md b/concepts/comparisons/about.md new file mode 100644 index 000000000..ec34ac14f --- /dev/null +++ b/concepts/comparisons/about.md @@ -0,0 +1,45 @@ +# About + +In C++, you can compare values using the comparison operators. + +| Comparison | Operator | +| ------------------| --------- | +| equal | `==` | +| not equal | `!=` | +| less | `<` | +| less or equal | `<=` | +| greater | `>` | +| greater or equal | `>=` | + +The result of these comparisons is always a boolean value: + +```cpp +int a{3}; + +bool eq1 = a != 4; // true + +bool eq2 = a > 5; // false +``` + +The operators `==` and `!=` check whether a value is equal to another or not, respectively. +Here are some common examples: + +```cpp +bool eq3 = 2 == 3; // false, integer comparison + +bool eq4 = 2.1 != 2.2; // true, float comparison + +bool eq5 = "hello" == "hello"; // true, string comparison +``` + +The other operators check if one value is greater than (`>`), greater or equal to (`>=`), less than (`<`), and less or equal to (`<=`) to another value. +This kind of comparison is available for numbers and strings. +When comparing strings, the dictionary order (also known as lexicographic order) is followed. + +```cpp +bool eq6 = 2 > 3; // false, integer comparison + +bool eq7 = 1.2 < 1.3; // true, float comparison + +bool eq8 = "Hello" < "World"; // true, string comparison +``` diff --git a/concepts/comparisons/introduction.md b/concepts/comparisons/introduction.md new file mode 100644 index 000000000..f16a5b0d0 --- /dev/null +++ b/concepts/comparisons/introduction.md @@ -0,0 +1,45 @@ +# Introduction + +In C++, you can compare values using the comparison operators. + +| Comparison | Operator | +| ------------------| --------- | +| equal | `==` | +| not equal | `!=` | +| less | `<` | +| less or equal | `<=` | +| greater | `>` | +| greater or equal | `>=` | + +The result of these comparisons is always a boolean value: + +```cpp +int a{3}; + +bool eq1 = a != 4; // true + +bool eq2 = a > 5; // false +``` + +The operators `==` and `!=` check whether a value is equal to another or not, respectively. +Here are some common examples: + +```cpp +bool eq3 = 2 == 3; // false, integer comparison + +bool eq4 = 2.1 != 2.2; // true, float comparison + +bool eq5 = "hello" == "hello"; // true, string comparison +``` + +The other operators check if one value is greater than (`>`), greater or equal to (`>=`), less than (`<`), and less or equal to (`<=`) to another value. +This kind of comparison is available for numbers and strings. +When comparing strings, the dictionary order (also known as lexicographic order) is followed. + +```cpp +bool eq6 = 2 > 3; // false, integer comparison + +bool eq7 = 1.2 < 1.3; // true, float comparison + +bool eq8 = "Hello" < "World"; // true, string comparison +``` diff --git a/concepts/comparisons/links.json b/concepts/comparisons/links.json new file mode 100644 index 000000000..be11e8132 --- /dev/null +++ b/concepts/comparisons/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/operator_comparison", + "description": "C++ reference on comparison operators" + } +] diff --git a/concepts/enums/.meta/config.json b/concepts/enums/.meta/config.json new file mode 100644 index 000000000..016740e0d --- /dev/null +++ b/concepts/enums/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "In C++, enumerations (enums) define custom data types with named values, ensuring variables hold specific and valid options, improving code readability and reliability.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/enums/about.md b/concepts/enums/about.md new file mode 100644 index 000000000..e0f1c4627 --- /dev/null +++ b/concepts/enums/about.md @@ -0,0 +1,87 @@ +# About + +## Enumerations + +So far we have mostly handled numbers and strings. +To model the real world, we might want a limited number of values that a variable can take on. +You might want a dedicated type with a few distinct values with distinct names. +For example, in a skateboard factory, having the deck material be a choice of only maple, bamboo, or plastic. + +You could use integers to encode those values, but you would have to use extra code to check if there is an invalid value coming from the system for the material. +The meaning of those [_magic numbers_][magic numbers] is difficult to trace over the source code and they are prone to mix-ups. +`enumerations` can be used to encourage expressive code and to restrict unintentional comparison mistakes. + +## Example usage + +The specific term for this kind of enumeration is `scoped enumeration`. +The snippet below shows how to write a `DeckMaterial` `enumeration`. +Note the `enum class` keyword and the `;` at the end of the definition: + +```cpp +enum class DeckMaterial { + maple, + bamboo, + plastic +}; +``` +Now, look at a pricing function in the skate shop and take note of the scope resolution operator (`::`) specifying an `enumerator` from the `enumeration`: + +```cpp +double deck_price(double base_price, DeckMaterial material) { + if(material == DeckMaterial::plastic) { + return base_price * 0.9; + } + return base_price * 1.3; +} +``` + +## Scoping + +Imagine you have a second `enumeration` for wheel material: + +```cpp +enum class WheelMaterial { + steel, + clay, + plastic +}; +``` +Although the wheels and the deck can both be made of _plastic_, the two cannot be confused. +They are different _types_: `DeckMaterial` plastic and `WheelMaterial` plastic. +Each `enumeration` will have its `enumerators` in its own scope - its own `namespace`. +This is the reason they are called `scoped enumerations`. + +~~~~exercism/advanced +## Unscoped Enumerations +You might be thinking that with a name like `scoped`, there would also be _`unscoped`_ enumerations -- and you would be correct. +`Unscoped enumerations` are becoming less popular because they all share the same global namespace. +Because of the sharing, you could not have two `unscoped enumerations` with the same `enumerators` like _plastic_ in the example above. + +Also, `unscoped enumerations` implicitly convert to integers. +Look at the example below for a surprising result: + +```cpp +enum CitrusFruits { + lemons, // 0 + oranges, //1 +}; + +enum IceCream { + walnut, // 0 + apples, // 1 +}; + +bool comparison{apples == oranges}; +// => true + +// Example from above: +bool comparison{DeckMaterial::plastic == WheelMaterial::plastic}; +// => Does not compile! +``` + +If you want to convert `scoped enumerations` to integers you can use `static_cast`. +~~~~ + + + +[magic numbers]: https://en.wikipedia.org/wiki/Magic_number_(programming) diff --git a/concepts/enums/introduction.md b/concepts/enums/introduction.md new file mode 100644 index 000000000..ad4e4e4c9 --- /dev/null +++ b/concepts/enums/introduction.md @@ -0,0 +1,87 @@ +# Introduction + +## Enumerations + +So far we have mostly handled numbers and strings. +To model the real world, we might want a limited number of values that a variable can take on. +You might want a dedicated type with a few distinct values with distinct names. +For example, in a skateboard factory, having the deck material be a choice of only maple, bamboo, or plastic. + +You could use integers to encode those values, but you would have to use extra code to check if there is an invalid value coming from the system for the material. +The meaning of those [_magic numbers_][magic numbers] is difficult to trace over the source code and they are prone to mix-ups. +`enumerations` can be used to encourage expressive code and to restrict unintentional comparison mistakes. + +## Example usage + +The specific term for this kind of enumeration is `scoped enumeration`. +The snippet below shows how to write a `DeckMaterial` `enumeration`. +Note the `enum class` keyword and the `;` at the end of the definition: + +```cpp +enum class DeckMaterial { + maple, + bamboo, + plastic +}; +``` +Now, look at a pricing function in the skate shop and take note of the scope resolution operator (`::`) specifying an `enumerator` from the `enumeration`: + +```cpp +double deck_price(double base_price, DeckMaterial material) { + if(material == DeckMaterial::plastic) { + return base_price * 0.9; + } + return base_price * 1.3; +} +``` + +## Scoping + +Imagine you have a second `enumeration` for wheel material: + +```cpp +enum class WheelMaterial { + steel, + clay, + plastic +}; +``` +Although the wheels and the deck can both be made of _plastic_, the two cannot be confused. +They are different _types_: `DeckMaterial` plastic and `WheelMaterial` plastic. +Each `enumeration` will have its `enumerators` in its own scope - its own `namespace`. +This is the reason they are called `scoped enumerations`. + +~~~~exercism/advanced +## Unscoped Enumerations +You might be thinking that with the name `scoped`, there would also be be `unscoped` enumerations -- and you would be correct. +`Unscoped enumerations` are becoming less popular because they all share the same global namespace. +Because of the sharing, you could not have two `unscoped enumerations` with the same `enumerators` like _plastic_ in the example above. + +Also, `unscoped enumerations` implicitly convert to integers. +Look at the example below for a surprising result: + +```cpp +enum CitrusFruits { + lemons, // 0 + oranges, //1 +}; + +enum IceCream { + walnut, // 0 + apples, // 1 +}; + +bool comparison{apples == oranges}; +// => true + +// Example from above: +bool comparison{DeckMaterial::plastic == WheelMaterial::plastic}; +// => Does not compile! +``` + +If you want to convert `scoped enumerations` to integers you can use `static_cast`. +~~~~ + + + +[magic numbers]: https://en.wikipedia.org/wiki/Magic_number_(programming) diff --git a/concepts/enums/links.json b/concepts/enums/links.json new file mode 100644 index 000000000..30bc05dcd --- /dev/null +++ b/concepts/enums/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/enum", + "description": "C++ reference on enumerations" + } +] diff --git a/concepts/functions/.meta/config.json b/concepts/functions/.meta/config.json new file mode 100644 index 000000000..8e9c22977 --- /dev/null +++ b/concepts/functions/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "In C++, functions support overloading, constant arguments, and default values.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/functions/about.md b/concepts/functions/about.md new file mode 100644 index 000000000..89748088a --- /dev/null +++ b/concepts/functions/about.md @@ -0,0 +1,124 @@ +# About + +## Terminology + +You have already used and written C++ functions in a couple of concepts. +It is time to get technical. +The code snippet below shows the most common terms for easy reference. +As C++ ignores whitespace, the formatting has been changed to put each element on a single line. + +```cpp +// Function declaration: +bool // Return type +admin_detected(string user, string password) // Type signature +; // Don't forget the ';' for the declaration + +// Function definition: +bool // Return type +admin_detected // Function name +(string user, string password) // Parameter list +{ return user == "admin" && password == "1234"; } // Function body +``` +~~~~exercism/advanced +The declaration works like a note to the compiler, that there is a function of that name, return type and parameter list. +The code will not work, if the definition is missing. +Declarations are optional, they are needed if you use the function before its definiton. +Declarations can solve problems like cyclic references and they can be used to separate the interface from the implementation. +~~~~ + +## The const qualifier + +Sometimes you want to make sure that values cannot be changed after they have been initialized. +C++ uses the `const` keyword as a qualifier for constants. + +```cpp +const int number_of_dragon_balls{7}; +number_of_dragon_balls--; // compilation error +``` + +~~~~exercism/note +You will often see constants written in _UPPER_SNAKE_CASE_. +It is recommened to reserve this casing for macros, if there is no other convention. +~~~~ + +If you try to change a constant variable after it has been set, your code will not compile. +This helps to avoid unintended changes but also opens up optimization possibilities for the compiler. +As a human, it is also easier to reason about the code, if you know that certain parts will not be affected. + +You can also have `const` as a qualifier for function parameters. + +```cpp +string guess_number(const int& secret, const int& guess) { + if (secret < guess) return "lower."; + if (secret > guess) return "higher."; + return "exact!"; +} +``` + +When you pass a `const` reference to the function, you can be sure that it will be left unchanged. +You will often see `const` references for objects that might be costly to copy, like longer strings. +A third use-case for the `const` qualifier are member functions that do not change the instance of a class. + +```cpp +class Stubborn { + public: + Stubborn(string reply) { + response = reply; + } + string answer(const string& question) const { + if (question.length() == 0) { return "";} + return response; + } + private: + string response{}; +}; +``` + +The `Stubborn` member function `answer` uses a `const string&` reference as a parameter. +This avoids a copy operation from the original object that was passed to the function. + +## Function overloading + +Multiple functions can have the same name if the parameter list is different. +That is called function overloading and it is usually done if these functions perform very similar tasks. + +The function header without the return type is the function's __type signature__. +A change in the type signature results in a new function. + +The `play_sound` example has six different overloads to accommodate different scenarios: + +```cpp +// different argument types: +void play_sound(char note); // C, D, E, ..., B +void play_sound(string solfege); // do, re, mi, ..., ti +void play_sound(int jianpu); // 1, 2, 3, ..., 7 + +// different number of arguments: +void play_sound(string solfege, double duration); + +// different qualifiers: +void play_sound(vector& solfege); +void play_sound(const vector& solfege); +``` + +~~~~exercism/advanced +The type signature is defined by the function's name, the number of parameters, their types, and their qualifiers (but not their names). +The return type is explicitly not part of the type signature and you will get compilation errors if you have two functions that only differ in their return type. +The compiler will complain because it is not clear which of the two should be used. +~~~~ + +## Default arguments + +Some functions can get very lengthy and many of its calls might use the same values for most of the parameters. +The repetition in those calls can be avoided with default arguments. + +```cpp +void record_new_horse_birth(string name, int weight, string color="brown-ish", string dam="Alruccaba", string sire="Poseidon"); + +record_new_horse_birth("Urban Sea", 130); // color will be brown, dam "Alruccabam", sire "Poseidon" +record_new_horse_birth("Highclere", 175, "off-white", "Fall Aspen"); // sire will be "Poseidon" +``` + +As the function declaration is often read before the definition, it is the better place to set the default arguments. +If one parameter has a default declaration, all the parameters to its right need a default declaration as well. +Sometimes complicated function overloads can be refactored to fewer functions with default arguments to improve maintainability. diff --git a/concepts/functions/introduction.md b/concepts/functions/introduction.md new file mode 100644 index 000000000..3a173b9bd --- /dev/null +++ b/concepts/functions/introduction.md @@ -0,0 +1,124 @@ +# Introduction + +## Terminology + +You have already used and written C++ functions in a couple of concepts. +It is time to get technical. +The code snippet below shows the most common terms for easy reference. +As C++ ignores whitespace, the formatting has been changed to put each element on a single line. + +```cpp +// Function declaration: +bool // Return type +admin_detected(string user, string password) // Type signature +; // Don't forget the ';' for the declaration + +// Function definition: +bool // Return type +admin_detected // Function name +(string user, string password) // Parameter list +{ return user == "admin" && password == "1234"; } // Function body +``` +~~~~exercism/advanced +The declaration works like a note to the compiler, that there is a function of that name, return type and parameter list. +The code will not work, if the definition is missing. +Declarations are optional, they are needed if you use the function before its definiton. +Declarations can solve problems like cyclic references and they can be used to separate the interface from the implementation. +~~~~ + +## The const qualifier + +Sometimes you want to make sure that values cannot be changed after they have been initialized. +C++ uses the `const` keyword as a qualifier for constants. + +```cpp +const int number_of_dragon_balls{7}; +number_of_dragon_balls--; // compilation error +``` + +~~~~exercism/note +You will often see constants written in _UPPER_SNAKE_CASE_. +It is recommened to reserve this casing for macros, if there is no other convention. +~~~~ + +If you try to change a constant variable after it has been set, your code will not compile. +This helps to avoid unintended changes but also opens up optimization possibilities for the compiler. +As a human, it is also easier to reason about the code, if you know that certain parts will not be affected. + +You can also have `const` as a qualifier for function parameters. + +```cpp +string guess_number(const int& secret, const int& guess) { + if (secret < guess) return "lower."; + if (secret > guess) return "higher."; + return "exact!"; +} +``` + +When you pass a `const` reference to the function, you can be sure that it will be left unchanged. +You will often see `const` references for objects that might be costly to copy, like longer strings. +A third use-case for the `const` qualifier are member functions that do not change the instance of a class. + +```cpp +class Stubborn { + public: + Stubborn(string reply) { + response = reply; + } + string answer(const string& question) const { + if (question.length() == 0) { return ""; } + return response; + } + private: + string response{}; +}; +``` + +The `Stubborn` member function `answer` uses a `const string&` reference as a parameter. +This avoids a copy operation from the original object that was passed to the function. + +## Function overloading + +Multiple functions can have the same name if the parameter list is different. +That is called function overloading and it is usually done if these functions perform very similar tasks. + +The function header without the return type is the function's __type signature__. +A change in the type signature results in a new function. + +The `play_sound` example has six different overloads to accommodate different scenarios: + +```cpp +// different argument types: +void play_sound(char note); // C, D, E, ..., B +void play_sound(string solfege); // do, re, mi, ..., ti +void play_sound(int jianpu); // 1, 2, 3, ..., 7 + +// different number of arguments: +void play_sound(string solfege, double duration); + +// different qualifiers: +void play_sound(vector& solfege); +void play_sound(const vector& solfege); +``` + +~~~~exercism/advanced +The type signature is defined by the function's name, the number of parameters, their types, and their qualifiers (but not their names). +The return type is explicitly not part of the type signature and you will get compilation errors if you have two functions that only differ in their return type. +The compiler will complain because it is not clear which of the two should be used. +~~~~ + +## Default arguments + +Some functions can get very lengthy and many of its calls might use the same values for most of the parameters. +The repetition in those calls can be avoided with default arguments. + +```cpp +void record_new_horse_birth(string name, int weight, string color="brown-ish", string dam="Alruccaba", string sire="Poseidon"); + +record_new_horse_birth("Urban Sea", 130); // color will be brown, dam "Alruccabam", sire "Poseidon" +record_new_horse_birth("Highclere", 175, "off-white", "Fall Aspen"); // sire will be "Poseidon" +``` + +As the function declaration is often read before the definition, it is the better place to set the default arguments. +If one parameter has a default declaration, all the parameters to its right need a default declaration as well. +Sometimes complicated function overloads can be refactored to fewer functions with default arguments to improve maintainability. diff --git a/concepts/functions/links.json b/concepts/functions/links.json new file mode 100644 index 000000000..6d5b65a78 --- /dev/null +++ b/concepts/functions/links.json @@ -0,0 +1,18 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/cv", + "description": "C++ reference on const qualifiers" + }, + { + "url": "https://en.cppreference.com/book/intro/function_overloading", + "description": "C++ reference on function overloading" + }, + { + "url": "https://en.cppreference.com/w/cpp/language/overload_resolution", + "description": "C++ reference on overload resolution" + }, + { + "url": "https://en.cppreference.com/w/cpp/language/default_arguments", + "description": "C++ reference on default arguments" + } +] diff --git a/concepts/headers/.meta/config.json b/concepts/headers/.meta/config.json new file mode 100644 index 000000000..7a1034c85 --- /dev/null +++ b/concepts/headers/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "In C++, header files provide definitions that can be included in other files. They can be seen as an API that decouples the interface from the implementation in the source files.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/headers/about.md b/concepts/headers/about.md new file mode 100644 index 000000000..5983cd3ce --- /dev/null +++ b/concepts/headers/about.md @@ -0,0 +1,216 @@ +# About + +In C++, declarations are often separated from definitions. +Declarations are grouped into so-called header files, with the respective implementations placed in source files. +You can think of the header files as an API. +The header file will tell you _what_ a codebase has to offer without going into the details of _how_. + +## Header and Source + +The most common file extension for header files is `.h`. +Some projects use `.hpp` or skip the extension completely. + +The definitions are located in a separate `.cpp` file. +To reunite the parts, the source file starts by _including_ the respective header file. + +If you want to write a library called "quick_math" that offers a function "super_root" that you want to use often, the files would look like this: + +```cpp +// A file named quick_math.h +#pragma once + +namespace quick_math { + +double super_root(double x, int n); + +} +``` + +```cpp +// A file named quick_math.cpp +#include + +#include "quick_math.h" + +double quick_math::super_root(double x, int n) { + while (n) { + x = std::sqrt(x), --n; + } + return x; +} +``` + +If you need to include a header that is only required by the implementation, the respective `#include` line is only needed in the source file. +Everything that is included in the header is also available in the `.cpp` file, like the `string` library in the example below. +**Attention**: the `;` is needed after the declaration in the header file, but not after the definition in the source file. + +~~~~exercism/note +Many C++ exercises on Exercism start with two almost empty files: header and source. +You have to check the `*_test.cpp` file to see the names and namespaces of the expected functions in order to solve the exercise. +~~~~ + +## Classes and Headers + +Classes can become very complex and their relation to the header / source partition might be confusing. +One possible layout is to keep all the implementation details in the source file and all the declarations and member variables in the header: + +```cpp +// A file named robot_flower.h +#if !defined(ROBOT_FLOWER_H) +#define ROBOT_FLOWER_H + +#include + +namespace robots { + +class Flower { + private: + bool needs_water{}; + int size{}; + std::string name{}; + + public: + Flower(std::string name, int size = 0); + void give_water(); + std::string get_name(); + int get_size(); + void start_next_day(); +}; +} // namespace robots +#endif +``` + +```cpp +// A file named robot_flower.cpp +#include "robot_flower.h" + +robots::Flower::Flower(std::string name, int size) { + this->name = "Robotica " + name; + this->size = size; +} + +void robots::Flower::start_next_day() { + if (!needs_water) ++size; + needs_water = true; +} + +std::string robots::Flower::get_name() { return name; } + +int robots::Flower::get_size() { return size; } +``` + +When the header is used as an API overview, that is where a person would look for information like default values. +The `size` parameter's default of the constructor is therefore handled in the header and not in the implementation. +The definitions in the source file are prefixed with the namespace `robots` and the class type `Flower`. + +Another layout option is a _header only_ library, that does not have a `.cpp` file at all: + +```cpp +// A file named robot_flower.h +#pragma once + +#include + +namespace robots { + +class Flower { + private: + bool needs_water{}; + int size{}; + std::string name{}; + + public: + Flower(std::string name, int size = 0) { + this->name = "Robotica " + name; + this->size = size; + } + void give_water() { needs_water = false; } + std::string get_name() { return name; } + int get_size() { return size; } + void start_next_day() { + if (!needs_water) ++size; + needs_water = true; + } +}; +} // namespace robots +``` + +Projects might use combinations of these layouts and there is a lot of discussion as to what might be the best fit for each use case. + +## Include Guards + +You may have noticed the `#pragma once` line in the example header file above. +This is called an include guard - and it ensures that the content of the file is included only once during the compilation to avoid errors. +There is another, more complex variation of an include guard that starts with `#ifndef` and ends with `#endif`, that is detailed below. + + +~~~~exercism/advanced +## Forward Declarations +C++ code is evaluated procedurally. +If you want to use a function, it has to be known to the compiler at the moment of usage. +Sometimes it is not possible to find a linear fashion to lay out the definitions in the source code. +Take a look at the example below: + +```cpp +int myFunction(int n) { + if (n < 10) { + return n; + } else { + return myOtherFunction(n / 10); + } +} + +int myOtherFunction(int m) { return myFunction(m / 2); } +``` + +When `myFunction` is defined, the compiler does not know about `myOtherFunction` yet. +Unfortunately, the circle-reference problem cannot be solved by switching the order. + +C++ offers **forward declarations** to let the compiler know of `myFunction` and `myOtherFunction` before they are defined. +The compiler assumes that the definition will follow at some later point after the declaration. +The next example shows how a forward declaration is used for functions. + +```cpp +int myFunction(int n); // Forward declaration of myFunction +int myOtherFunction(int m); // Forward declaration of myOtherFunction + +// Definition of myFunction +int myFunction(int n) { + if (n < 0) { + return 0; + } else { + return myOtherFunction(n - 2); + } +} + +// Definition of myOtherFunction +int myOtherFunction(int m) { return myFunction(m / 2); } +``` + +## Include Guards via ifndef + +It does not matter if the same file is included multiple times within a project. +Header files should not contain definitions. +The complete project cannot have the same definition more than once. +This is called the "One definition rule". +It will be enforced by the compiler. + +It is easy to avoid multiple unintended inclusions of the same definition with include guards. +They are formed by a special procedure during the compilation stages. +The aim is to only include a file only if a certain variable has not been set and then set it once the file is included. +Often the sequence of this variable is chosen as a variation of the name of the file. +Another method is the generation of a UUID to reduce the risk of accidentally using the sequence twice. +The syntax can be seen below with `MY_HEADER_FILE_H` as a variable. + +```cpp +#ifndef MY_HEADER_FILE_H /* any name uniquely mapped to file name */ +#define MY_HEADER_FILE_H + +// file content + +#endif +``` + +The problem with `#pragma once` is, that pragmas are not an official part of the C++ language and the implementation differs from compiler to compiler. +Many big projects have switched to the simpler pragma method, but a few are still cautious. +~~~~ diff --git a/concepts/headers/introduction.md b/concepts/headers/introduction.md new file mode 100644 index 000000000..978b94c63 --- /dev/null +++ b/concepts/headers/introduction.md @@ -0,0 +1,152 @@ +# Introduction + +In C++, declarations are often separated from definitions. +Declarations are grouped into so-called header files, with the respective implementations placed in source files. +You can think of the header files as an API. +The header file will tell you _what_ a codebase has to offer without going into the details of _how_. + +## Header and Source + +The most common file extension for header files is `.h`. +Some projects use `.hpp` or skip the extension completely. + +The definitions are located in a separate `.cpp` file. +To reunite the parts, the source file starts by _including_ the respective header file. + +If you want to write a library called "quick_math" that offers a function "super_root" that you want to use often, the files would look like this: + +```cpp +// A file named quick_math.h +#pragma once + +namespace quick_math { + +double super_root(double x, int n); + +} // namespace quick_math +``` + +```cpp +// A file named quick_math.cpp +#include + +#include "quick_math.h" + +double quick_math::super_root(double x, int n) { + while (n) { + x = std::sqrt(x), --n; + } + return x; +} +``` + +If you need to include a header that is only required by the implementation, the respective `#include` line is only needed in the source file. +Everything that is included in the header is also available in the `.cpp` file, like the `string` library in the example below. +**Attention**: the `;` is needed after the declaration in the header file, but not after the definition in the source file. + +~~~~exercism/note +Many C++ exercises on Exercism start with two almost empty files: header and source. +You have to check the `*_test.cpp` file to see the names and namespaces of the expected functions in order to solve the exercise. +~~~~ + +## Classes and Headers + +Classes can become very complex and their relation to the header / source partition might be confusing. +One possible layout is to keep all the implementation details in the source file and all the declarations and member variables in the header: + +```cpp +// A file named robot_flower.h +#if !defined(ROBOT_FLOWER_H) +#define ROBOT_FLOWER_H + +#include + +namespace robots { + +class Flower { + private: + bool needs_water{}; + int size{}; + std::string name{}; + + public: + Flower(std::string name, int size = 0); + void give_water(); + std::string get_name(); + int get_size(); + void start_next_day(); +}; + +} // namespace robots + +#endif +``` + +```cpp +// A file named robot_flower.cpp +#include "robot_flower.h" + +robots::Flower::Flower(std::string name, int size) { + this->name = "Robotica " + name; + this->size = size; +} + +void robots::Flower::start_next_day() { + if (!needs_water) { + ++size; + } + needs_water = true; +} + +std::string robots::Flower::get_name() { return name; } + +int robots::Flower::get_size() { return size; } +``` + +When the header is used as an API overview, that is where a person would look for information like default values. +The `size` parameter's default of the constructor is therefore handled in the header and not in the implementation. +The definitions in the source file are prefixed with the namespace `robots` and the class type `Flower`. + +Another layout option is a _header only_ library, that does not have a `.cpp` file at all: + +```cpp +// A file named robot_flower.h +#pragma once + +#include + +namespace robots { + +class Flower { + private: + bool needs_water{}; + int size{}; + std::string name{}; + + public: + Flower(std::string name, int size = 0) { + this->name = "Robotica " + name; + this->size = size; + } + void give_water() { needs_water = false; } + std::string get_name() { return name; } + int get_size() { return size; } + void start_next_day() { + if (!needs_water) { + ++size; + } + needs_water = true; + } +}; + +} // namespace robots +``` + +Projects might use combinations of these layouts and there is a lot of discussion as to what might be the best fit for each use case. + +## Include Guards + +You may have noticed the `#pragma once` line in the example header file above. +This is called an include guard - and it ensures that the content of the file is included only once during the compilation to avoid errors. +There is another, more complex variation of an include guard that starts with `#ifndef` and ends with `#endif`. +It serves the same purpose and its usage is shown in the `Flower` class example above. diff --git a/concepts/headers/links.json b/concepts/headers/links.json new file mode 100644 index 000000000..fa81042f8 --- /dev/null +++ b/concepts/headers/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://learn.microsoft.com/en-us/cpp/cpp/header-files-cpp?view=msvc-170", + "description": "Examples and information on header files and include guards" + }, + { + "url": "https://www.learncpp.com/cpp-tutorial/classes-and-header-files/", + "description": "More information about classes in header files" + } +] diff --git a/concepts/if-statements/.meta/config.json b/concepts/if-statements/.meta/config.json new file mode 100644 index 000000000..97ba88e17 --- /dev/null +++ b/concepts/if-statements/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "If statements control the flow of execution of a program based on a boolean condition.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/if-statements/about.md b/concepts/if-statements/about.md new file mode 100644 index 000000000..f2921bd27 --- /dev/null +++ b/concepts/if-statements/about.md @@ -0,0 +1,44 @@ +# About + +Conditionals in C++ are similar to conditionals in other languages. +The underlying type of any conditional operation is the `bool` type, which can have the value of `true` or `false`. +Conditionals are often used as flow control mechanisms to check for various conditions. + +An `if` statement can be used, which executes its code if the underlying condition is `true` like this: + +```cpp +std::string value{"val"}; + +if (value == "val") { + return "was val"; +} +``` + +In scenarios involving more than one case many `if` statements can be chained together using the `else if` and `else` statements. + +```cpp +int number{33}; + +if (number > 0) { + return "positive"; +} else if (number < 0) { + return "negative"; +} else { + return "zero"; +} +``` + +Since C++17 if-statements can also include a short initialization statement that can be used to initialize one or more variables for the if statement. +For example: + +```cpp +int num{7}; +if (int v{2 * num}; v > 10) { + return v; +} else { + return num; +} +// => 14 +``` + +> Note: any variables created in the initialization cannot be accesed after the end of the if statement. diff --git a/concepts/if-statements/introduction.md b/concepts/if-statements/introduction.md new file mode 100644 index 000000000..ae4bb9344 --- /dev/null +++ b/concepts/if-statements/introduction.md @@ -0,0 +1,44 @@ +# Introduction + +Conditionals in C++ are similar to conditionals in other languages. +The underlying type of any conditional operation is the `bool` type, which can have the value of `true` or `false`. +Conditionals are often used as flow control mechanisms to check for various conditions. + +An `if` statement can be used, which executes its code if the underlying condition is `true` like this: + +```cpp +std::string value{"val"}; + +if (value == "val") { + return "was val"; +} +``` + +In scenarios involving more than one case many `if` statements can be chained together using the `else if` and `else` statements. + +```cpp +int number{33}; + +if (number > 0) { + return "positive"; +} else if (number < 0) { + return "negative"; +} else { + return "zero"; +} +``` + +Since C++17 if-statements can also include a short initialization statement that can be used to initialize one or more variables for the if statement. +For example: + +```cpp +int num{7}; +if (int v{2 * num}; v > 10) { + return v; +} else { + return num; +} +// => 14 +``` + +> Note: any variables created in the initialization cannot be accesed after the end of the if statement. diff --git a/concepts/if-statements/links.json b/concepts/if-statements/links.json new file mode 100644 index 000000000..7f0c14f80 --- /dev/null +++ b/concepts/if-statements/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/if", + "description": "C++ reference on if statements" + } +] diff --git a/concepts/includes/.meta/config.json b/concepts/includes/.meta/config.json new file mode 100644 index 000000000..6ea7eeb27 --- /dev/null +++ b/concepts/includes/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "To work with multiple files and external libraries, C++ uses the include keyword.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/includes/about.md b/concepts/includes/about.md new file mode 100644 index 000000000..d9399b1e5 --- /dev/null +++ b/concepts/includes/about.md @@ -0,0 +1,32 @@ +# About + +## Includes + +To include the content of other files C++ uses the `include` directive. +These includes should be stated at the top of the file. +During compilation, the line with the directive is replaced by the content of the respective file. +Namespaces are kept as they are in the original file after includes. + +## The Standard Library + +The standard library offers many common functions, algorithms, and data structures. +The standard library uses the `std` namespace. + +One example is the C numerics library `cmath`. +It provides many common mathematical operations. + +```cpp +#include + +int cube_me(int a) { + // raise `a` to the third power + return std::pow(a, 3); +} +``` + +~~~~exercism/note +Standard libraries are included with angled braces `<>` instead of double quotes `"`. +The difference is the location, where the compiler searches for the respective files. +The search in the current project is skipped for the angled braces version, and it directly starts in the system's include directories. +If you want to include local files you would use double quotes: `#include "myfile"` +~~~~ diff --git a/concepts/includes/introduction.md b/concepts/includes/introduction.md new file mode 100644 index 000000000..581ef7cd1 --- /dev/null +++ b/concepts/includes/introduction.md @@ -0,0 +1,35 @@ +# Introduction + +In general, you don't want to reinvent the wheel when it comes to programming. +For many standard algorithms, there will be someone who might have already done your work for you. + +## Includes + +To include the content of other files C++ uses the `include` directive. +These includes should be stated at the top of the file. +During compilation, the line with the directive is replaced by the content of the respective file. +Namespaces are kept as they are in the original file after includes. + +## The Standard Library + +The standard library offers many common functions, algorithms, and data structures. +The standard library uses the `std` namespace. + +One example is the C numerics library `cmath`. +It provides many common mathematical operations. + +```cpp +#include + +int cube_me(int a) { + // raise `a` to the third power + return std::pow(a, 3); +} +``` + +~~~~exercism/note +Standard libraries are included with angled braces `<>` instead of double quotes `"`. +The difference is the location, where the compiler searches for the respective files. +The search in the current project is skipped for the angled braces version, and it directly starts in the system's include directories. +If you want to include local files you would use double quotes: `#include "myfile"` +~~~~ diff --git a/concepts/includes/links.json b/concepts/includes/links.json new file mode 100644 index 000000000..41b93f13f --- /dev/null +++ b/concepts/includes/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/preprocessor/include", + "description": "C++ reference on Source File Inclusion" + } +] diff --git a/concepts/literals/.meta/config.json b/concepts/literals/.meta/config.json new file mode 100644 index 000000000..2be09684a --- /dev/null +++ b/concepts/literals/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "C++ literals represent fixed values, including strings and different numeric formats.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/literals/about.md b/concepts/literals/about.md new file mode 100644 index 000000000..420b44433 --- /dev/null +++ b/concepts/literals/about.md @@ -0,0 +1,76 @@ +# About + +Literals are fixed values that are used directly in code. +They do not need any computation during run-time. +They come in various forms to represent different types of data efficiently. +The most frequent literals are of numeric nature, but there are also literal operators for date, time and string types. +A later concept will show how user-defined literal operators can be used. + +## Numeric Literals + +Numeric literals include decimal, octal, hexadecimal, and binary representations. +These basics were already covered in the [numbers concept][numbers-concept]. + +All representations like `23`, `0x50f7ba11` and `0xba5eba11` are understood as `int` by the compiler. +The general `int` type is signed and ([depending on the compiler and the system][in-depth-integers]), often has a range of `-2'147'483'648` to `2'147'483'647`. +This is sufficient most of the time and you don't need to worry about the specific type. + +Some use-cases demand larger ranges or very small memory footprints of programs. +To be explicit one can directly state specific integer types with the numeric literal. + +When negative numbers aren't needed, but the added range is desired the `u` or `U` suffix are used for _unsigned_ integers. +For greater ranges `L` or even `LL` can be used for long and long long types. + +~~~~exercism/caution +The use of lower-case `l` is permitted, but it is easily confused with the number `1` and thus discouraged. +~~~~ + +```cpp +auto net_worth_christian_grey{2'500'000'000U}; // out of range for 32-bit integers +auto net_worth_carlisle_cullen{46'000'000'000LL}; // int and uint are not enough +``` + +Floating-point numbers usually resolve to `double` during compilation. +This is a good default case and use-cases with the narrower `float` type are less frequent than the unsigned example above. + +```cpp +auto light_year_in_m{9.46073e+15f}; // well in the range of float +auto earth_to_edge_comoving_distance_in_nm{4.32355e+32}; // needs double type for magnitude +auto eulers_number{2.718281828459045}; // needs double type for precision +``` + +~~~~exercism/advanced +## Character and String Literals + +Other concepts already used character literals with single quotes like `'}'` or `'@'`. + +In C++ `char` is limited to the first 128 [ascii character codes][ascii-code]. +To use several ascii chars or extended ascii characters like `‰` or even unicode characters like `麗` and `ẞ` other types are needed. +In previous concept string literals were introduced with _double quotes_: `"一体どういう意味ですか。C++です"`. +The actual type of this Japanese phrase is `const char (&)[46]`, a C-style string. + +The use of string literals is not activated by default. +To use the _string_ literal `""s` or the _string-view_ literal `""sv`, the user has to specify their use by using the related namespace: + +```cpp +#include +#include + +using namespace std::literals; + +auto green_light{"무궁화 꽃 이 피었 습니다"}; +// green_light type is const char (&)[36] +auto umbrella{"달고나"s}; +// umbrella type is std::basic_string, the same as std::string +auto il_nam{"보는 것이 하는 것보다 더 재미있을 수가 없지"sv}; +// il_nam type is std::basic_string_view +``` + +A _string_view_ can be seen as a reference to a const string. + +[ascii-code]: https://www.ascii-code.com/ + +~~~~ + +[numbers-concept]: https://exercism.org/tracks/cpp/concepts/numbers +[in-depth-integers]: https://www.learncpp.com/cpp-tutorial/fixed-width-integers-and-size-t/ diff --git a/concepts/literals/introduction.md b/concepts/literals/introduction.md new file mode 100644 index 000000000..99f433b19 --- /dev/null +++ b/concepts/literals/introduction.md @@ -0,0 +1,85 @@ +# Introduction + +Literals are fixed values that are used directly in code. +They do not need any computation during run-time. +They come in various forms to represent different types of data efficiently. +The most frequent literals are of numeric nature, but there are also literal operators for date, time and string types. +A later concept will show how user-defined literal operators can be used. + +## Numeric Literals + +Numeric literals include decimal, octal, hexadecimal, and binary representations. +These basics were already covered in the [numbers concept][numbers-concept]. + +All representations like `23`, `0x50f7ba11` and `0xba5eba11` are understood as `int` by the compiler. +The general `int` type is signed and ([depending on the compiler and the system][in-depth-integers]), often has a range of `-2'147'483'648` to `2'147'483'647`. +This is sufficient most of the time and you don't need to worry about the specific type. + +Some use-cases demand larger ranges or very small memory footprints of programs. +To be explicit one can directly state specific integer types with the numeric literal. + +When negative numbers aren't needed, but the added range is desired the `u` or `U` suffix are used for _unsigned_ integers. +For greater ranges `L` or even `LL` can be used for long and long long types. + +~~~~exercism/caution +The use of lower-case `l` is permitted, but it is easily confused with the number `1` and thus discouraged. +~~~~ + +```cpp +auto net_worth_christian_grey{2'500'000'000U}; // out of range for 32-bit integers +auto net_worth_carlisle_cullen{46'000'000'000LL}; // int and uint are not enough +``` + +Floating-point numbers usually resolve to `double` during compilation. +This is a good default case and use-cases with the narrower `float` type are less frequent than the unsigned example above. + +~~~~exercism/advanced +The [C++23 standard][cpp23-literals] introduces more fine grained control with literals like `f128` or `F16`. +Some compilers allow their own literals. +GCC permits the usage of `d` as a literal, but this is not foreseen in the standard and can hinder cross-platform compatibility. + +[cpp23-literals]: https://en.cppreference.com/w/cpp/language/floating_literal + +~~~~ + +```cpp +auto light_year_in_m{9.46073e+15f}; // well in the range of float +auto earth_to_edge_comoving_distance_in_nm{4.32355e+32}; // needs double type for magnitude +auto eulers_number{2.718281828459045}; // needs double type for precision +``` + +~~~~exercism/advanced +## Character and String Literals + +Other concepts already used character literals with single quotes like `'}'` or `'@'`. + +In C++ `char` is limited to the first 128 [ascii character codes][ascii-code]. +To use several ascii chars or extended ascii characters like `‰` or even unicode characters like `麗` and `ẞ` other types are needed. +In previous concept string literals were introduced with _double quotes_: `"一体どういう意味ですか。C++です"`. +The actual type of this Japanese phrase is `const char (&)[46]`, a C-style string. + +The use of string literals is not activated by default. +To use the _string_ literal `""s` or the _string-view_ literal `""sv`, the user has to specify their use by using the related namespace: + +```cpp +#include +#include + +using namespace std::literals; + +auto green_light{"무궁화 꽃 이 피었 습니다"}; +// green_light type is const char (&)[36] +auto umbrella{"달고나"s}; +// umbrella type is std::basic_string, the same as std::string +auto il_nam{"보는 것이 하는 것보다 더 재미있을 수가 없지"sv}; +// il_nam type is std::basic_string_view +``` + +A _string_view_ can be seen as a reference to a const string. + +[ascii-code]: https://www.ascii-code.com/ + +~~~~ + +[numbers-concept]: https://exercism.org/tracks/cpp/concepts/numbers +[in-depth-integers]: https://www.learncpp.com/cpp-tutorial/fixed-width-integers-and-size-t/ diff --git a/concepts/literals/links.json b/concepts/literals/links.json new file mode 100644 index 000000000..9f98210d0 --- /dev/null +++ b/concepts/literals/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://www.learncpp.com/cpp-tutorial/literals/", + "description": "Learn C++ overview on literals" + } +] diff --git a/concepts/loops/.meta/config.json b/concepts/loops/.meta/config.json new file mode 100644 index 000000000..099a582d3 --- /dev/null +++ b/concepts/loops/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "Execute the same piece of code several times with loops", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/loops/about.md b/concepts/loops/about.md new file mode 100644 index 000000000..b1fcd0f73 --- /dev/null +++ b/concepts/loops/about.md @@ -0,0 +1,94 @@ +# About + +Loops are used to repeatedly execute some logic. +The two most common types are the `while loop` (indefinite looping) and the `for loop` (definite, or counted looping). +There is also the `for each` loop, that will come up in a later concept. + + +## General Syntax + +The `for loop` consists of a header and a code block that contains the body of the loop wrapped in curly brackets. +The header consists of 3 components separated by semicolons `;`: init-statement, condition, and another expression. +Each of these may be empty. + +```cpp +for (init-statement; condition; expression) { + some_statement; +} +``` + +- The **init** component is some code that runs only once before the loop starts. +- The **condition** component must be some expression that evaluates to a boolean and controls when the loop should stop. +The code inside the loop will run as long as this condition evaluates to true. +As soon as this expression evaluates to false, no more iterations of the loop will run. +- The **expression** component is some code that will run at the end of each iteration. + + +The `while loop` executes its body as long as its condition check is true. +The code snippet below shows how to transform a for into a while loop. + +```cpp +init-statement; +while(condition) { + some_statement; + expression; +} +``` + +## Interlude: Increments and Decrements + +When working with loops is often required to add 1 or subtract 1 from a counter variable. +This is so common, that the operation has special operators: `++` and `--`. + +They come in a prefix and a postfix form. +The prefix changes the variable before use in the statement and the postfix version afterward. +You probably want the prefix version most of the time. + +```cpp +int a{3}; +int b{--a}; +// b is 2, a is now 2 +int c{a++}; +// c is 2, a is now 3 +``` + +## For Loops - An example + +The init component usually sets up a counter variable, the condition checks whether the loop should be continued or stopped and the post component usually increments the counter at the end of each repetition. + +```cpp +int sum{0}; +for (int i{1}; i < 10; ++i) { + sum += i; +} +``` + +This loop will sum the numbers from `1` to `9` (including `9`). + +## Break and Continue + +Inside a loop body, you can use the `break` keyword to stop the execution of the loop entirely: + +```cpp +int sum{2}; +while(true) { + sum *= 2; + if (sum > 1000) + break; +} +// sum is now 1024 +``` + +In contrast, the keyword `continue` only stops the execution of the current iteration and continues with the next one: + +```cpp +int equal_sum{0}; +for (int i{1}; i < 7; ++i) { + if (n%2 == 1) { + continue; + } + equal_sum += i; +} +// equal_sum is now 12 +``` +> Note: it is usually easier to understand the logic of the loop, when the use of `break` and `continue` is minimized or entirely avoided. diff --git a/concepts/loops/introduction.md b/concepts/loops/introduction.md new file mode 100644 index 000000000..521154ee2 --- /dev/null +++ b/concepts/loops/introduction.md @@ -0,0 +1,94 @@ +# Introduction + +Loops are used to repeatedly execute some logic. +The two most common types are the `while loop` (indefinite looping) and the `for loop` (definite, or counted looping). +There is also the `for each` loop, that will come up in a later concept. + + +## General Syntax + +The `for loop` consists of a header and a code block that contains the body of the loop wrapped in curly brackets. +The header consists of 3 components separated by semicolons `;`: init-statement, condition, and another expression. +Each of these may be empty. + +```cpp +for (init-statement; condition; expression) { + some_statement; +} +``` + +- The **init** component is some code that runs only once before the loop starts. +- The **condition** component must be some expression that evaluates to a boolean and controls when the loop should stop. +The code inside the loop will run as long as this condition evaluates to true. +As soon as this expression evaluates to false, no more iterations of the loop will run. +- The **expression** component is some code that will run at the end of each iteration. + + +The `while loop` executes its body as long as its condition check is true. +The code snippet below shows how to transform a for into a while loop. + +```cpp +init-statement; +while(condition) { + some_statement; + expression; +} +``` + +## Interlude: Increments and Decrements + +When working with loops is often required to add 1 or subtract 1 from a counter variable. +This is so common, that the operation has special operators: `++` and `--`. + +They come in a prefix and a postfix form. +The prefix changes the variable before use in the statement and the postfix version afterward. +You probably want the prefix version most of the time. + +```cpp +int a{3}; +int b{--a}; +// b is 2, a is now 2 +int c{a++}; +// c is 2, a is now 3 +``` + +## For Loops - An example + +The init component usually sets up a counter variable, the condition checks whether the loop should be continued or stopped and the post component usually increments the counter at the end of each repetition. + +```cpp +int sum{0}; +for (int i{1}; i < 10; ++i) { + sum += i; +} +``` + +This loop will sum the numbers from `1` to `9` (including `9`). + +## Break and Continue + +Inside a loop body, you can use the `break` keyword to stop the execution of the loop entirely: + +```cpp +int sum{2}; +while(true) { + sum *= 2; + if (sum > 1000) + break; +} +// sum is now 1024 +``` + +In contrast, the keyword `continue` only stops the execution of the current iteration and continues with the next one: + +```cpp +int equal_sum{0}; +for (int i{1}; i < 7; ++i) { + if (i%2 == 1) { + continue; + } + equal_sum += i; +} +// equal_sum is now 12 +``` +> Note: it is usually easier to understand the logic of the loop, when the use of `break` and `continue` is minimized or entirely avoided. diff --git a/concepts/loops/links.json b/concepts/loops/links.json new file mode 100644 index 000000000..458f3a2d1 --- /dev/null +++ b/concepts/loops/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/for", + "description": "C++ reference on for loops" + }, + { + "url": "https://en.cppreference.com/w/cpp/language/while", + "description": "C++ reference on while loops" + } +] diff --git a/concepts/namespaces/.meta/config.json b/concepts/namespaces/.meta/config.json new file mode 100644 index 000000000..129317b1b --- /dev/null +++ b/concepts/namespaces/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "Code can be organized in namespaces for a clearer structure.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/namespaces/about.md b/concepts/namespaces/about.md new file mode 100644 index 000000000..8c281986e --- /dev/null +++ b/concepts/namespaces/about.md @@ -0,0 +1,47 @@ +# About + +An important method for code organization are namespaces. +Two functions might have a naming collision, which can be resolved by putting them in different namespaces. +Namespaces can be nested, which might help to structure big code bases. +Access to the namespaces is done via the scope-resolution operator `::`. + +The example below shows the use of two different `foo` functions. +They are used together by prefixing their respective namespaces. + +```cpp +namespace my_ns { + int foo() { + return 44; + } + namespace my_inner_ns { + int baz() { + return 90; + } + } +} +namespace my_other_ns { + int foo() { + return -2; + } +} +int myresult{my_ns::foo() + my_other_ns::foo() * my_ns::my_inner_ns::baz()}; +``` + +~~~~exercism/advanced +Deeply nested namespaces might be too verbose. +It is possible to remove the verbosity with a namespace import via `using`. +This moves all names into the global namespace. +To keep some differentiation aliases might be useful. + +```cpp +int my_other_result{my_ns::my_inner_ns::baz()}; + +using namespace my_ns::my_inner_ns; // importing the complete namespace +int also_other_result{baz()}; + +namespace m = my_ns; // setting an alias +namespace o = my_other_ns; + +int also_my_result{m::foo() + o::foo() * m::my_inner_ns::baz()}; +``` +~~~~ diff --git a/concepts/namespaces/introduction.md b/concepts/namespaces/introduction.md new file mode 100644 index 000000000..e1d5b7f34 --- /dev/null +++ b/concepts/namespaces/introduction.md @@ -0,0 +1,29 @@ +# Introduction + +An important method for code organization are namespaces. +Two functions might have a naming collision, which can be resolved by putting them in different namespaces. +Namespaces can be nested, which might help to structure big code bases. +Access to the namespaces is done via the scope-resolution operator `::`. + +The example below shows the use of two different `foo` functions. +They are used together by prefixing their respective namespaces. + +```cpp +namespace my_ns { + int foo() { + return 44; + } + namespace my_inner_ns { + int baz() { + return 90; + } + } +} +namespace my_other_ns { + int foo() { + return -2; + } +} + +int myresult{my_ns::foo() + my_other_ns::foo() * my_ns::my_inner_ns::baz()}; +``` diff --git a/concepts/namespaces/links.json b/concepts/namespaces/links.json new file mode 100644 index 000000000..9cb677f95 --- /dev/null +++ b/concepts/namespaces/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/namespace", + "description": "C++ reference on namespaces" + } +] diff --git a/concepts/numbers/.meta/config.json b/concepts/numbers/.meta/config.json new file mode 100644 index 000000000..afae10271 --- /dev/null +++ b/concepts/numbers/.meta/config.json @@ -0,0 +1,9 @@ +{ + "blurb": "C++ has different types for numbers. Common are int for integers and double for decimals.", + "authors": [ + "vaeng" + ], + "contributors": [ + "tesarect" + ] +} diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md new file mode 100644 index 000000000..223f35eb9 --- /dev/null +++ b/concepts/numbers/about.md @@ -0,0 +1,113 @@ +# About +The built-in number types in C++ can be divided into integers and floating points. +Integers are whole numbers like `0`, `691`, or `-2`. +Floating point numbers are numbers with a decimal point like `6.02214076`, `0.1`, or `-1.616`. + +## Integers +The following example shows the declaration and initialization of four different variables + +```cpp +int m_morales{9241}; // base 10: 0-9 +int a_apaec{0x24CD}; // base 16: 0-9 and A-F +int m_gargan{0b10010000011001}; // base 2: 0-1 +int b_reilly{022031}; // base 8: 0-7 + // Leading with a 0 not the letter o. +``` + +When you assign a value to an `int` variable, you can do so directly with a literal. +A literal is a hard-coded number like `9241`. +There are different integer literals for several bases of the representation. +Decimal integer literals are the most common and use the digits `0` to `9`. +By adding a special prefix, like `0x`, it is possible to use other bases. +The example above shows the number `9421` in its four representations and prefixes. +All variables are initialized to the same value. + +For more details on the different representation systems, take a look at [a small tutorial][cpp_numerical_bases]. + +You can use an apostrophe to separate digits for easier readability. +`9'241` is the same as `0b0100'100'0001'1001` or `92'4'1`. + +## Floating-Point Numbers + +The floating-point literals come in two flavors. +In addition to the intuitive `0.0024` it is possible to use its scientific notation `2.4e-3`. +The most common floating-point type is `double`. + +## Arithmetic + +C++ supports `+`, `-`, `*`, `/`, `(` and `)` and `%` to form expressions. +The result from the operation between two integers is also an integer. +`5 / 2` will return `2`. +When one of the involved types is a floating-point type, the result will also be of a floating-point. +`5.0 / 2` and `5 / 2.0` will return `2.5`. +`%` is the remainder operator and will return the remainder of an integer division: `5%3` is `2`. + +## Assignment operator + +The assignment operator assigns a variable with a literal. +Always takes place from right to left, and never the other way around. + +```cpp +int length = 5; +int width = 2; +length = width; +``` + +Here integer value `5` is assigned to the variable `length`. +Then integer value `2` is assigned to variable `width`. +Finally value of `width` is copied to the variable `length` and the earlier value `5` will be lost. +Consider also that we are only assigning the value of `width` to `length` at the moment of the assignment operation. +Therefore, if the value of `width` changes at a later moment, it will not affect the value taken by `length`. + +Assignment operator can be combined with the other operators(arithmetic & bitwise) known as `compound assignment` operators `+=`, `-=`, `*=`, `/=`, `%=`. +These operators modify the current value of a variable by performing an operation on it. + +```cpp +// we start with 0 people +int people{}; +// we need 0 eggs +int eggs{}; +// two people joined: +people += 2; +// people is now 2 +// let's add 3 eggs per person +eggs += 3 * people; +// eggs is now 6 +``` + +Variables `people` & `eggs` are initialized to `0`. +Then, we add integer value `2` over the existing value `0` of the variable `people` and assign it back to `people`. +`people` becomes `2` now. +Later, we add `3` eggs for each person, which turns out to be `6` eggs in total. +Now add this `6` to existing value `0` of the variable `eggs` and assign it back to `eggs`. +`eggs` will be `6` now. +The equivalent expression would be `people = people + 2` and `eggs = eggs + (3 * people)`. + +~~~~exercism/advanced +## Casting + +Casting is the conversion from one type to another. +If a `double` is needed, an `int` can be used in its place. +The `5.0 / 2` example from above is handled by an implicit cast of `2` to `2.0`. +A `double`, that is cast into an `int` might lose information. +It is good practice to make these casts to another type T explicit via `static_cast`. +The old C-style cast via `(T)` is discouraged in modern C++, but should be known nonetheless. + +```cpp +double pi{3.14}; +int about_2_times_pi_cpp{static_cast(pi) * 2}; +// Old C-style cast: +int about_2_times_pi_c{(int)pi * 2}; +``` + +## Precision & Representation + +Numbers in C++ cannot be arbitrarily big. +`int` for example is represented by 32 bits. +That is enough for a set of `2^32` numbers - roughly 4 billion. +Including `0`, this sets the biggest and smallest `int` numbers as `-2^31` and `2^31 - 1`. + +Floating point numbers are usually implemented using 15 decimal places of precision, but will vary in representation based on the host system. +~~~~ + +[cpp_numerical_bases]: https://cplusplus.com/doc/hex/ diff --git a/concepts/numbers/introduction.md b/concepts/numbers/introduction.md new file mode 100644 index 000000000..30c7fe2e5 --- /dev/null +++ b/concepts/numbers/introduction.md @@ -0,0 +1,87 @@ +# Introduction +The built-in number types in C++ can be divided into integers and floating points. +Integers are whole numbers like `0`, `691`, or `-2`. +Floating point numbers are numbers with a decimal point like `6.02214076`, `0.1`, or `-1.616`. + +## Integers +The following example shows the declaration and initialization of four different variables + +```cpp +int m_morales{9241}; // base 10: 0-9 +int a_apaec{0x24CD}; // base 16: 0-9 and A-F +int m_gargan{0b10010000011001}; // base 2: 0-1 +int b_reilly{022031}; // base 8: 0-7 + // Leading with a 0 not the letter o. +``` + +When you assign a value to an `int` variable, you can do so directly with a literal. +A literal is a hard-coded number like `9241`. +There are different integer literals for several bases of the representation. +Decimal integer literals are the most common and use the digits `0` to `9`. +By adding a special prefix, like `0x`, it is possible to use other bases. +The example above shows the number `9421` in its four representations and prefixes. +All variables are initialized to the same value. + +For more details on the different representation systems, take a look at [a small tutorial][cpp_numerical_bases]. + +You can use an apostrophe to separate digits for easier readability. +`9'241` is the same as `0b0100'100'0001'1001` or `92'4'1`. + +## Floating-Point Numbers + +The floating-point literals come in two flavors. +In addition to the intuitive `0.0024` it is possible to use its scientific notation `2.4e-3`. +The most common floating-point type is `double`. + +## Arithmetic + +C++ supports `+`, `-`, `*`, `/`, `(` and `)` and `%` to form expressions. +The result from the operation between two integers is also an integer. +`5 / 2` will return `2`. +When one of the involved types is a floating-point type, the result will also be of a floating-point. +`5.0 / 2` and `5 / 2.0` will return `2.5`. +`%` is the remainder operator and will return the remainder of an integer division: `5%3` is `2`. + +## Assignment operator + +The assignment operator assigns a variable with a literal. +Always takes place from right to left, and never the other way around. + +```cpp +int length = 5; +int width = 2; +length = width; +``` + +Here integer value `5` is assigned to the variable `length`. +Then integer value `2` is assigned to variable `width`. +Finally value of `width` is copied to the variable `length` and the earlier value `5` will be lost. +Consider also that we are only assigning the value of `width` to `length` at the moment of the assignment operation. +Therefore, if the value of `width` changes at a later moment, it will not affect the value taken by `length`. + +Assignment operator can be combined with the other operators(arithmetic & bitwise) known as `compound assignment` operators `+=`, `-=`, `*=`, `/=`, `%=`. +These operators modify the current value of a variable by performing an operation on it. + +```cpp +// we start with 0 people +int people{}; +// we need 0 eggs +int eggs{}; +// two people joined: +people += 2; +// people is now 2 +// let's add 3 eggs per person +eggs += 3 * people; +// eggs is now 6 +``` + +Variables `people` & `eggs` are initialized to `0`. +Then, we add integer value `2` over the existing value `0` of the variable `people` and assign it back to `people`. +`people` becomes `2` now. +Later, we add `3` eggs for each person, which turns out to be `6` eggs in total. +Now add this `6` to existing value `0` of the variable `eggs` and assign it back to `eggs`. +`eggs` will be `6` now. +The equivalent expression would be `people = people + 2` and `eggs = eggs + (3 * people)`. + + +[cpp_numerical_bases]: https://cplusplus.com/doc/hex/ diff --git a/concepts/numbers/links.json b/concepts/numbers/links.json new file mode 100644 index 000000000..894269e31 --- /dev/null +++ b/concepts/numbers/links.json @@ -0,0 +1,14 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/operator_arithmetic", + "description": "Reference for Arithmetic operators" + }, + { + "url": "https://en.cppreference.com/w/cpp/language/auto", + "description": "Reference for the auto placeholder type specifier" + }, + { + "url": "https://en.wikipedia.org/wiki/Double-precision_floating-point_format", + "description": "Explanation on the floating point format" + } + ] diff --git a/concepts/pointers/.meta/config.json b/concepts/pointers/.meta/config.json new file mode 100644 index 000000000..c0d7cc03c --- /dev/null +++ b/concepts/pointers/.meta/config.json @@ -0,0 +1,6 @@ +{ + "blurb": "In C++, pointers are variables that store object addresses. They allow you to directly manipulate memory and access to data structures like arrays and objects.", + "authors": [ + "vaeng" + ] +} diff --git a/concepts/pointers/about.md b/concepts/pointers/about.md new file mode 100644 index 000000000..c5348a211 --- /dev/null +++ b/concepts/pointers/about.md @@ -0,0 +1,136 @@ +# Introduction + +Like many other languages, C++ has _pointers_. +You already know _references_ and _pointers_ are similar, but think of them as a level closer to the inner workings of your computer. +_Pointers_ are variables that hold object addresses. +They are used to directly interact with objects, enabling dynamic memory allocation and efficient data manipulation in C++. + +If you're new to _pointers_, they can feel a little mysterious but once you get used to them, they're quite straight-forward. + +They're a crucial part of C++, so take some time to really understand them. +The bare-bone version in this concept is also called _dumb pointer_ or _raw pointer_. +With modern C++ there are also _smart pointers_, the basic type is not smart at all and you have to handle all the work manually. + +Before digging into the details, it's worth understanding the use of _pointers_. +_Pointers_ are a way to share an object's address with other parts of our program, which is useful for two major reasons: + +1. Like _references_, pointers avoid copies and help to reduce the resource-footprint of your program. +1. Unlike _references_, pointers can be reassigned to different objects. +1. Pointers can also point to a null value, to indicate, that they currently do not point to any object. + +## General Syntax + +A pointer declaration in C++ involves specifying the data type to which the the pointer is pointing, followed by an asterisk (`*`) and the pointer's name. +When pointers are declared, they are not automatically initialized. +Without explicit assignment, a pointer typically holds an indeterminate value, often referred to as a "garbage address." +While certain compilers might initialize pointers to `nullptr`, this behavior is not guaranteed across all compilers, so it's essential not to rely on it. +It's best practice to explicitly initialize raw pointers and verify their non-null status before utilization to avoid potential issues. + +```cpp +int* ptr{nullptr}; // Declares a pointer and makes sure it is not invalid +``` + +To assign the address of a variable to a pointer, you use the address-of operator (`&`). +Dereferencing a pointer is done using the _indirection operator_ (`*`) operator. + +```cpp +std::string opponent{"Solomon Lane"}; +// 'ethan' points to the address of the string opponent +std::string* ethan{&opponent}; +// Instead of ethan's, the opponent's name address is given to the passPort +std::string passportName{*ethan}; +``` + +Attention: dereferencing has to be done explicitly, while _references_ just worked like an alias. + +## Pointer Arithmetic + +_Pointer arithmetic_ allows you to perform arithmetic operations on pointers, which is particularly useful when working with arrays. +Adding an integer to a pointer makes it point to a different element. + +```cpp +// Stargate Coordinate Code +int gateCode[] = {462, 753, 218, 611, 977}; +// 'ptr' points to the first element of 'gateCode' +int* ptr{&gateCode[0]}; +// Accesses the third Stargate address through pointer arithmetic +int dialedAddress{*(ptr + 2)}; +// Chevron encoded! Dialing Stargate address: +openStarGate(dialedAddress); +``` + +~~~~exercism/caution +Pointer arithmetic in C++ can easily lead to __undefined behavior__ if not handled carefully. +Undefined behavior can manifest in unexpected program outcomes, crashes, or even security vulnerabilities. +One infamous example of the consequences of undefined behavior occurred in the [explosion of the Ariane 5 rocket][ariane-flight-v88] in 1996, where a software exception caused by the conversion of a 64-bit floating-point number to a 16-bit signed integer led to a catastrophic failure. + +[ariane-flight-v88]: https://en.wikipedia.org/wiki/Ariane_flight_V88 +~~~~ + +## Accessing member variables + +In C++, the `->` operator is used to access members of an object through a pointer to that object. +It is a shorthand which simplifies accessing members of objects pointed to by pointers. +For instance, if `ptr` is a pointer to an object with a member variable `x`, instead of using `(*ptr).x`, you can directly use `ptr->x`. +This operator enhances code readability and reduces verbosity when working with pointers to objects. + +Here's a brief example, with a _struct_ `Superhero` that has a member variable `superpower`. +The main function creates a pointer `dianaPrince` to a `Superhero` object (representing Wonder Woman). +The `->` operator is used to access the member variable `superpower`, showcasing Wonder Woman's iconic "Lasso of Truth." + +```cpp +struct Superhero { + std::string superpower; +}; + +Superhero wonder_woman{}; +Superhero* dianaPrince = &wonder_woman; +dianaPrince->superpower = "Lasso of Truth"; + +// Using the -> operator to access member variable superpower: +std::cout << "Wonder Woman, possesses the mighty " << dianaPrince->superpower; + +// Memory cleanup: +delete dianaPrince; +``` + +## Pointers vs. references + +Pointers and references both enable indirect access to objects, but they differ in their capabilities and safety considerations. +Pointers offer the flexibility of changing their target object and can be assigned null. +However, this flexibility introduces risks, such as dereferencing null pointers or creating dangling pointers. +References, on the other hand, cannot be null and are bound to valid objects upon creation, avoiding these risks. +Given their safer nature, references should be preferred over pointers unless the additional functionalities provided by pointers are necessary. + +~~~~exercism/advanced +## Dynamic Memory Allocation and Null Pointers + +In C++, `new` is used to dynamically allocate memory for objects on the heap during runtime. +It returns a pointer to the allocated memory address, allowing objects to have lifetimes not tied to a specific scope. +However, it's important to always pair `new` with `delete` to properly deallocate memory. +Dynamic memory allocation should be used cautiously to prevent common issues like memory leaks and accessing deallocated memory. + +Pointers can also be set to a special value called a _null pointer_, which indicates that they do not point to a valid address. +Null pointers are commonly used to initialize pointers before assigning them to valid values. + +```cpp +// There is a dark mark bearer, but who is it? +std::string* darkMarkBearer{nullptr}; +// It is Bellatrix! +darkMarkBearer = new std::string{"Bellatrix Lestrange"}; +// She has been defeated +delete darkMarkBearer; +// Reset pointer to null, symbolizing there is no more darkMarkBearer at the moment +darkMarkBearer = nullptr; +``` + +A pointer should either hold the address of a valid object, or be set to `nullptr`. +That way you only need to test pointers for null, and can assume any non-null pointer is valid. +When an object is destroyed, any pointers to the destroyed object will be left _dangling_ (they will not be automatically set to `nullptr`). +It is your responsibility to detect these cases and ensure those pointers are subsequently set to `nullptr`. + + +In older code, you might encounter two alternatives to `nullptr`. +Firstly, the literal `0` is specifically interpreted as a null value for pointers, though it's the only scenario where an integral literal can be assigned to a pointer. +Secondly, the `preprocessor macro` `NULL`, inherited from C and defined in the `` header, is another representation of a null pointer, though its usage is less common in modern C++ code. +~~~~ diff --git a/concepts/pointers/introduction.md b/concepts/pointers/introduction.md new file mode 100644 index 000000000..6db1296fc --- /dev/null +++ b/concepts/pointers/introduction.md @@ -0,0 +1,100 @@ +# Introduction + +Like many other languages, C++ has _pointers_. +You already know _references_ and _pointers_ are similar, but think of them as a level closer to the inner workings of your computer. +_Pointers_ are variables that hold object addresses. +They are used to directly interact with objects, enabling dynamic memory allocation and efficient data manipulation in C++. + +If you're new to _pointers_, they can feel a little mysterious but once you get used to them, they're quite straight-forward. + +They're a crucial part of C++, so take some time to really understand them. +The bare-bone version in this concept is also called _dumb pointer_ or _raw pointer_. +With modern C++ there are also _smart pointers_, the basic type is not smart at all and you have to handle all the work manually. + +Before digging into the details, it's worth understanding the use of _pointers_. +_Pointers_ are a way to share an object's address with other parts of our program, which is useful for two major reasons: + +1. Like _references_, pointers avoid copies and help to reduce the resource-footprint of your program. +1. Unlike _references_, pointers can be reassigned to different objects. +1. Pointers can also point to a null value, to indicate, that they currently do not point to any object. + +## General Syntax + +A pointer declaration in C++ involves specifying the data type to which the the pointer is pointing, followed by an asterisk (`*`) and the pointer's name. +When pointers are declared, they are not automatically initialized. +Without explicit assignment, a pointer typically holds an indeterminate value, often referred to as a "garbage address." +While certain compilers might initialize pointers to `nullptr`, this behavior is not guaranteed across all compilers, so it's essential not to rely on it. +It's best practice to explicitly initialize raw pointers and verify their non-null status before utilization to avoid potential issues. + +```cpp +int* ptr{nullptr}; // Declares a pointer and makes sure it is not invalid +``` + +To assign the address of a variable to a pointer, you use the address-of operator (`&`). +Dereferencing a pointer is done using the _indirection operator_ (`*`) operator. + +```cpp +std::string opponent{"Solomon Lane"}; +// 'ethan' points to the address of the string opponent +std::string* ethan{&opponent}; +// Instead of ethan's, the opponent's name address is given to the passPort +std::string passportName{*ethan}; +``` + +Attention: dereferencing has to be done explicitly, while _references_ just worked like an alias. + +## Pointer Arithmetic + +_Pointer arithmetic_ allows you to perform arithmetic operations on pointers, which is particularly useful when working with arrays. +Adding an integer to a pointer makes it point to a different element. + +```cpp +// Stargate Coordinate Code +int gateCode[] = {462, 753, 218, 611, 977}; +// 'ptr' points to the first element of 'gateCode' +int* ptr{&gateCode[0]}; +// Accesses the third Stargate address through pointer arithmetic +int dialedAddress{*(ptr + 2)}; +// Chevron encoded! Dialing Stargate address: +openStarGate(dialedAddress); +``` + +~~~~exercism/caution +Pointer arithmetic in C++ can easily lead to __undefined behavior__ if not handled carefully. +Undefined behavior can manifest in unexpected program outcomes, crashes, or even security vulnerabilities. +One infamous example of the consequences of undefined behavior occurred in the [explosion of the Ariane 5 rocket][ariane-flight-v88] in 1996, where a software exception caused by the conversion of a 64-bit floating-point number to a 16-bit signed integer led to a catastrophic failure. + +[ariane-flight-v88]: https://en.wikipedia.org/wiki/Ariane_flight_V88 +~~~~ + +## Accessing member variables + +In C++, the `->` operator is used to access members of an object through a pointer to that object. +It is a shorthand which simplifies accessing members of objects pointed to by pointers. +For instance, if `ptr` is a pointer to an object with a member variable `x`, instead of using `(*ptr).x`, you can directly use `ptr->x`. +This operator enhances code readability and reduces verbosity when working with pointers to objects. + +Here's a brief example, with a _struct_ `Superhero` that has a member variable `superpower`. +The main function creates a pointer `dianaPrince` to a `Superhero` object (representing Wonder Woman). +The `->` operator is used to access the member variable `superpower`, showcasing Wonder Woman's iconic "Lasso of Truth." + +```cpp +struct Superhero { + std::string superpower; +}; +Superhero wonder_woman{}; +Superhero* dianaPrince = &wonder_woman; +dianaPrince->superpower = "Lasso of Truth"; +// Using the -> operator to access member variable superpower: +std::cout << "Wonder Woman, possesses the mighty " << dianaPrince->superpower; +// Memory cleanup: +delete dianaPrince; +``` + +## Pointers vs. references + +Pointers and references both enable indirect access to objects, but they differ in their capabilities and safety considerations. +Pointers offer the flexibility of changing their target object and can be assigned null. +However, this flexibility introduces risks, such as dereferencing null pointers or creating dangling pointers. +References, on the other hand, cannot be null and are bound to valid objects upon creation, avoiding these risks. +Given their safer nature, references should be preferred over pointers unless the additional functionalities provided by pointers are necessary. diff --git a/concepts/pointers/links.json b/concepts/pointers/links.json new file mode 100644 index 000000000..b6c74d4b0 --- /dev/null +++ b/concepts/pointers/links.json @@ -0,0 +1,14 @@ +[ + { + "url": "https://en.cppreference.com/w/c/language/pointer", + "description": "C++ reference on pointers" + }, + { + "url": "https://en.cppreference.com/w/cpp/memory/new", + "description": "C++ reference on dynamic memory allocation with new" + }, + { + "url": "https://en.cppreference.com/w/cpp/language/delete", + "description": "C++ reference on dynamic memory deallocation with delete" + } +] diff --git a/concepts/references/.meta/config.json b/concepts/references/.meta/config.json new file mode 100644 index 000000000..9ad0cfe80 --- /dev/null +++ b/concepts/references/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "In C++, references are used when you want to add an alias for an existing variable. That can avoid expensive copy operations and allow you to modify the variable through the reference.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/references/about.md b/concepts/references/about.md new file mode 100644 index 000000000..86bafd3d8 --- /dev/null +++ b/concepts/references/about.md @@ -0,0 +1,68 @@ +# About + +## Copies and References + +With our current knowledge, it is a bit cumbersome to update a variable with the help of a function. +Every function argument is handled like a copy and does not change the original value. +Depending on the size of your type, this can have serious consequences for the performance of your code. + +```cpp +int adjust_salary(int base, int kids) { + return base + kids * 500; +} +int now_a_mother_of_twins{2500}; +adjust_salary(now_a_mother_of_twins, 2); + +// now_a_mother_of_twins is still 2500 +now_a_mother_of_twins = adjust_salary(now_a_mother_of_twins, 2); +// now_a_mother_of_twins is now 3500 +``` + +`References` can be seen as aliases - changes to them have an effect on the original variable. +`References` use an **ampersand** (`&`) in the type declaration. + +```cpp +int balance{1000}; +int& budget{balance}; +int pro_computer_wheels{699}; +budget -= pro_computer_wheels; +// budget is now 301; +// balance is also 301; +``` +[Reseating][Reseating] (changing the binding of a `reference`) is not possible. +You cannot have an unitialized `reference`. +`References` need to be initialized with an existing variable. + +```cpp +int main_acc{1'000'000}; +int side_acc{-20}; +int& savings{main_acc}; +// => main_acc and savings are 1'000'000 + +// try to reseat savings to use main account +savings = side_acc; +// savings and main_acc are now -20 +// as this uses the **value** of side_acc +savings += 20; +// savings and main_acc are now 0, side_acc is stil -20 + +int& future_budget; +// => compiler error, reference must be bound! +``` + +## `void` + +With the power of `references` you might not need to return a value from a function at all. +`void` is used as a return type in this scenario. + +```cpp +void increase_power(int& level) { + level += 500; +} +int goku{8700}; +increase_power(goku); +// goku's power level? +// It's over 9000! +``` + +[Reseating]: https://stackoverflow.com/questions/728233/why-are-references-not-reseatable-in-c diff --git a/concepts/references/introduction.md b/concepts/references/introduction.md new file mode 100644 index 000000000..b769dc5f4 --- /dev/null +++ b/concepts/references/introduction.md @@ -0,0 +1,66 @@ +# Introduction + +## Copies and References + +With our current knowledge, it is a bit cumbersome to update a variable with the help of a function. +Every function argument is handled like a copy and does not change the original value. +Depending on the size of your type, this can have serious consequences for the performance of your code. + +```cpp +int adjust_salary(int base, int kids) { + return base + kids * 500; +} +int now_a_mother_of_twins{2500}; +adjust_salary(now_a_mother_of_twins, 2); + +// now_a_mother_of_twins is still 2500 +now_a_mother_of_twins = adjust_salary(now_a_mother_of_twins, 2); +// now_a_mother_of_twins is now 3500 +``` + +`References` can be seen as aliases - changes to them have an effect on the original variable. +`References` use an **ampersand** (`&`) in the type declaration. + +```cpp +int balance{1000}; +int& budget{balance}; +int pro_computer_wheels{699}; +budget -= pro_computer_wheels; +// budget is now 301; +// balance is also 301; +``` +Reseating (changing the binding of a `reference`) is not possible. +You cannot have an unitialized `reference`. +`References` need to be initialized with an existing variable. + +```cpp +int main_acc{1'000'000}; +int side_acc{-20}; +int& savings{main_acc}; +// => main_acc and savings are 1'000'000 + +// try to reseat savings to use main account +savings = side_acc; +// savings and main_acc are now -20 +// as this uses the **value** of side_acc +savings += 20; +// savings and main_acc are now 0, side_acc is stil -20 + +int& future_budget; +// => compiler error, reference must be bound! +``` + +## `void` + +With the power of `references` you might not need to return a value from a function at all. +`void` is used as a return type in this scenario. + +```cpp +void increase_power(int& level) { + level += 500; +} +int goku{8700}; +increase_power(goku); +// goku's power level? +// It's over 9000! +``` diff --git a/concepts/references/links.json b/concepts/references/links.json new file mode 100644 index 000000000..2d90ebcd3 --- /dev/null +++ b/concepts/references/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/reference", + "description": "C++ reference on references" + } +] diff --git a/concepts/smart-pointers/.meta/config.json b/concepts/smart-pointers/.meta/config.json new file mode 100644 index 000000000..d6c8cec1c --- /dev/null +++ b/concepts/smart-pointers/.meta/config.json @@ -0,0 +1,6 @@ +{ + "blurb": "Smart pointers in C++ provide automatic memory management, reducing memory leaks and dangling pointers by ensuring proper allocation and deallocation.", + "authors": [ + "vaeng" + ] +} diff --git a/concepts/smart-pointers/about.md b/concepts/smart-pointers/about.md new file mode 100644 index 000000000..c74c0538a --- /dev/null +++ b/concepts/smart-pointers/about.md @@ -0,0 +1,180 @@ +# Introduction + +Smart pointers are a modern C++ feature designed to provide automatic memory management, helping to prevent memory leaks and dangling pointers commonly associated with raw pointers. +They act as wrappers around raw pointers, adding additional functionality such as automatic memory deallocation when the pointer is no longer needed. + +## General Syntax + +Smart pointers are typically implemented as class templates in the C++ standard library. +The two most commonly used smart pointers are `std::unique_ptr` and `std::shared_ptr`. + +## Unique Pointers + +`std::unique_ptr` is a smart pointer that owns the object exclusively. +It ensures that at any given time, only one `std::unique_ptr` object owns the resource. +When the owning `std::unique_ptr` is destroyed or reset, it automatically destructs the objects and releases its memory. + +```cpp +#include +// Declaring and defining a unique pointer +auto rightful_king_of_england = std::make_unique("Excalibur"); + +// Unique pointers cannot be copied or assigned +auto mordred = rightful_king_of_england; // Error: Cannot copy a unique_ptr +``` + +## Advantages of `std::make_unique()` + +When creating a `std::unique_ptr`, it's preferable to use `std::make_unique()` instead of directly using `new` to allocate memory. +`std::make_unique()` provides several advantages: +1. **Exception Safety**: `std::make_unique()` guarantees exception safety. + If an exception is thrown during the construction of the object, memory will be automatically deallocated, preventing memory leaks. +2. **Clarity**: Using `std::make_unique()` makes code clearer and more concise. + It eliminates the need to explicitly specify the type being allocated, as the template arguments are deduced automatically. +3. **Optimization Opportunities**: Compilers have the opportunity to optimize `std::make_unique()` more effectively than manually allocating memory with `new`, potentially resulting in improved performance. +4. **Avoiding Misuse**: Deleting the underlying resource is possible, when the `std::unique_ptr` is constructed manually. + That would lead to undefined behavior, when the `std::unique_ptr` tries to delete it at its end of scope. + +## Shared Pointers + +`std::shared_ptr` is a smart pointer that allows multiple `std::shared_ptr` objects to share ownership of the same resource. +It keeps track of how many shared pointers are referencing the resource, and deallocates the memory only when the last shared pointer owning the resource goes out of scope or is reset. + +```cpp +// Declaring and defining a shared pointer to a dynamically allocated string +auto martian_congressional_republic = std::make_shared("protomolecule"); + +// Creating more shared pointer that shares ownership +auto outer_planets_alliance = martian_congressional_republic; +auto united_nations = martian_congressional_republic; +``` + +~~~~exercism/caution +In C++17 and below, using `std::shared_ptr` with arrays via `std::make_shared` is not directly supported. +While it's possible to allocate arrays with `std::make_shared`, creating shared pointers directly from them may lead to undefined behavior due to differences in memory management between single objects and arrays. +Instead, consider using `std::vector` or custom deletion functions to manage arrays with shared pointers effectively. +Always ensure compatibility with your compiler and standard library implementation when dealing with array allocations and shared pointers in C++17. +~~~~ + +## Advantages of `std::make_shared()` + +Similar to `std::make_unique()`, `std::make_shared()` offers benefits such as improved memory efficiency, exception safety, and readability. +It combines memory allocation for the control block and the managed object into a single operation, enhancing efficiency and reducing the risk of memory leaks. +Additionally, automatic deduction of template arguments simplifies code and enhances readability. +Using `std::make_shared()` promotes cleaner, safer, and more efficient code when working with `std::shared_ptr` objects in C++. + +## Weak Pointers + +`std::weak_ptr` is a companion class to `std::shared_ptr` that provides a non-owning "weak" reference to an object managed by a shared pointer. +It allows access to the resource without affecting its lifetime. +Weak pointers are useful in scenarios where cyclic references need to be broken to prevent memory leaks. + +```cpp +// Creating a shared pointer +auto your_account = std::make_shared("secret_subscription_password"); +// Creating a shared pointer that shares ownership +auto your_flatmates_account = your_account; + +// Creating a weak pointer from the shared pointer +auto your_flatmates_boyfriends_account = your_flatmates_account; +// if your_account and your_flatmates_account are deleted, there is no more reference to the shared pointer. +// your_flatmates_boyfriends_account will be a null pointer and cannot use the associated object any longer. +``` + +## `std::weak_ptr` and Cyclic Ownership + +`std::weak_ptr` was designed to address the issue of cyclic ownership, also known as circular references, that can occur when using `std::shared_ptr`. + +In a cyclic ownership scenario, two or more `std::shared_ptr` objects are referencing each other, creating a cycle where none of the objects can be deleted because they have strong references to each other, leading to memory leaks. + +`std::weak_ptr` provides a solution to this problem by allowing weak references to shared objects without contributing to their reference count. +This means that it can observe and access the shared object but doesn't prevent it from being deleted. +If all strong references to the shared object are released, the object is destroyed, and weak pointers observing it are automatically reset to `nullptr`. + +Here's a short example demonstrating the use of `std::weak_ptr` to break cyclic ownership: + +```cpp +struct Node { + Node() { std::cout << "Node constructed\n"; } + ~Node() { std::cout << "Node destructed\n"; } + + std::weak_ptr next; +}; + +// Creating two nodes +auto node1 = std::make_shared(); +auto node2 = std::make_shared(); + +// Creating a cycle by making each node point to the other +node1->next = node2; +node2->next = node1; + +// Release strong references to nodes +node1.reset(); +node2.reset(); + +// Nodes are automatically destructed due to weak references +``` + +"Node constructed" is printed twice, followed by two lines of "Node destructed". + +When `next` is not a weak pointer but a `std::shared_ptr`, it creates a cyclic ownership situation, leading to memory leaks because both nodes will hold strong references to each other, preventing their destruction. + +```cpp +struct Node { + Node() { std::cout << "Node constructed\n"; } + ~Node() { std::cout << "Node destructed\n"; } + + std::shared_ptr next; // Change to shared_ptr +}; +``` + +In this scenario, both `node1` and `node2` hold strong references to each other through `std::shared_ptr`, forming a cycle. +When the program exits and attempts to release the strong references with `reset()`, the reference counts of both nodes remain non-zero because each node holds a strong reference to the other. +Therefore, the nodes are not destructed, leading to memory leaks. +"Node constructed" is printed twice, but it is not followed by any line of "Node destructed". + +## Dangling pointers and `std::weak_ptr` + +Dangling pointers occur when a pointer references an object that has been deleted, leading to undefined behavior when the pointer is dereferenced. +`std::weak_ptr` helps avoid dangling pointers by providing a non-owning, weak reference to an object managed by `std::shared_ptr`. +Here's how to avoid dangling pointers with `std::weak_ptr`: + +1. **Create a `std::weak_ptr`**: Instead of directly holding a `std::shared_ptr`, create a `std::weak_ptr` to the shared object. + +2. **Check for Validity**: Before using the `std::weak_ptr`, check its validity using the `expired()` function. + This function returns `true` if the associated shared object has been deleted. + +3. **Lock the `std::weak_ptr`**: To access the shared object safely, use the `lock()` function, which returns a `std::shared_ptr` pointing to the same object if it is still valid. + If the object has been deleted, `lock()` returns an empty `std::shared_ptr`. + +Here's a code example demonstrating the use of `std::weak_ptr` to avoid dangling pointers: + +```cpp +auto node1 = std::make_shared(); +auto node2 = std::make_shared(); + +// Create weak pointers +node1->next = node2; +node2->next = node1; + +// Check validity and lock +if (auto lockedNode2 = node1->next.lock()) { + // Use lockedNode2 safely +} else { + // Handle case where node2 has been deleted +} +``` + +In this example, `node1->next` and `node2->next` are `std::weak_ptr` objects. +Before accessing the shared object, validity is checked using the `expired()` function. +Then, `lock()` is used to safely access the shared object. +This approach ensures that dangling pointers are avoided when working with `std::weak_ptr`. + +## Usage advice + +Use smart pointers by default: `std::unique_ptr` for exclusive ownership and `std::shared_ptr` for shared ownership. +Reserve raw pointers for non-owning references or when interfacing with legacy code. +In most cases, `std::unique_ptr` is sufficient for exclusive ownership, as it offers lightweight memory management without the overhead of reference counting. +`std::shared_ptr` should be used sparingly, as it introduces overhead and complexity unless true shared ownership is needed. +`std::weak_ptr` is specialized for breaking cyclic dependencies or observing shared objects, but it's not commonly used. diff --git a/concepts/smart-pointers/introduction.md b/concepts/smart-pointers/introduction.md new file mode 100644 index 000000000..dd84b7882 --- /dev/null +++ b/concepts/smart-pointers/introduction.md @@ -0,0 +1,97 @@ +# Introduction + +Smart pointers are a modern C++ feature designed to provide automatic memory management, helping to prevent memory leaks and dangling pointers commonly associated with raw pointers. +They act as wrappers around raw pointers, adding additional functionality such as automatic memory deallocation when the pointer is no longer needed. + +## General Syntax + +Smart pointers are typically implemented as class templates in the C++ standard library. +The two most commonly used smart pointers are `std::unique_ptr` and `std::shared_ptr`. + +## Unique Pointers + +`std::unique_ptr` is a smart pointer that owns the object exclusively. +It ensures that at any given time, only one `std::unique_ptr` object owns the resource. +When the owning `std::unique_ptr` is destroyed or reset, it automatically destructs the objects and releases its memory. + +```cpp +#include +// Declaring and defining a unique pointer +auto rightful_king_of_england = std::make_unique("Excalibur"); + +// Unique pointers cannot be copied or assigned +auto mordred = rightful_king_of_england; // Error: Cannot copy a unique_ptr +``` + +## Advantages of `std::make_unique()` + +When creating a `std::unique_ptr`, it's preferable to use `std::make_unique()` instead of directly using `new` to allocate memory. +`std::make_unique()` provides several advantages: +1. **Exception Safety**: `std::make_unique()` guarantees exception safety. + If an exception is thrown during the construction of the object, memory will be automatically deallocated, preventing memory leaks. +2. **Clarity**: Using `std::make_unique()` makes code clearer and more concise. + It eliminates the need to explicitly specify the type being allocated, as the template arguments are deduced automatically. +3. **Optimization Opportunities**: Compilers have the opportunity to optimize `std::make_unique()` more effectively than manually allocating memory with `new`, potentially resulting in improved performance. +4. **Avoiding Misuse**: Deleting the underlying resource is possible, when the `std::unique_ptr` is constructed manually. + That would lead to undefined behavior, when the `std::unique_ptr` tries to delete it at its end of scope. + +## Shared Pointers + +`std::shared_ptr` is a smart pointer that allows multiple `std::shared_ptr` objects to share ownership of the same resource. +It keeps track of how many shared pointers are referencing the resource, and deallocates the memory only when the last shared pointer owning the resource goes out of scope or is reset. + +```cpp +// Declaring and defining a shared pointer to a dynamically allocated string +auto martian_congressional_republic = std::make_shared("protomolecule"); + +// Creating more shared pointer that shares ownership +auto outer_planets_alliance = martian_congressional_republic; +auto united_nations = martian_congressional_republic; +``` + +~~~~exercism/caution +In C++17 and below, using `std::shared_ptr` with arrays via `std::make_shared` is not directly supported. +While it's possible to allocate arrays with `std::make_shared`, creating shared pointers directly from them may lead to undefined behavior due to differences in memory management between single objects and arrays. +Instead, consider using `std::vector` or custom deletion functions to manage arrays with shared pointers effectively. +Always ensure compatibility with your compiler and standard library implementation when dealing with array allocations and shared pointers in C++17. +~~~~ + +## Advantages of `std::make_shared()` + +Similar to `std::make_unique()`, `std::make_shared()` offers benefits such as improved memory efficiency, exception safety, and readability. +It combines memory allocation for the control block and the managed object into a single operation, enhancing efficiency and reducing the risk of memory leaks. +Additionally, automatic deduction of template arguments simplifies code and enhances readability. +Using `std::make_shared()` promotes cleaner, safer, and more efficient code when working with `std::shared_ptr` objects in C++. + + +~~~~exercism/advanced +## Weak Pointers + +`std::weak_ptr` is a companion class to `std::shared_ptr` that provides a non-owning "weak" reference to an object managed by a shared pointer. + +```cpp +// Creating a shared pointer +auto your_account = std::make_shared("secret_subscription_password"); +// Creating a shared pointer that shares ownership +auto your_flatmates_account = your_account; + +// Creating a weak pointer from the shared pointer +auto your_flatmates_boyfriends_account = your_flatmates_account; +// if your_account and your_flatmates_account are deleted, there is no more reference to the shared pointer. +// your_flatmates_boyfriends_account will be a null pointer and cannot use the associated object any longer. +``` + +Weak pointers are useful in scenarios where cyclic references need to be broken to prevent memory leaks. +`std::weak_ptr` was designed to address the issue of cyclic ownership, also known as circular references, that can occur when using `std::shared_ptr`. +In a cyclic ownership scenario, two or more `std::shared_ptr` objects are referencing each other, creating a cycle where none of the objects can be deleted because they have strong references to each other, leading to memory leaks. +`std::weak_ptr` provides a solution to this problem by allowing weak references to shared objects without contributing to their reference count. +This means that it can observe and access the shared object but doesn't prevent it from being deleted. +~~~~ + +## Usage advice + +Use smart pointers by default: `std::unique_ptr` for exclusive ownership and `std::shared_ptr` for shared ownership. +Reserve raw pointers for non-owning references or when interfacing with legacy code. +In most cases, `std::unique_ptr` is sufficient for exclusive ownership, as it offers lightweight memory management without the overhead of reference counting. +`std::shared_ptr` should be used sparingly, as it introduces overhead and complexity unless true shared ownership is needed. +`std::weak_ptr` is specialized for breaking cyclic dependencies or observing shared objects, but it's not commonly used. diff --git a/concepts/smart-pointers/links.json b/concepts/smart-pointers/links.json new file mode 100644 index 000000000..da71e152a --- /dev/null +++ b/concepts/smart-pointers/links.json @@ -0,0 +1,14 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/memory/unique_ptr", + "description": "C++ reference on std::unique_ptr" + }, + { + "url": "https://en.cppreference.com/w/cpp/memory/shared_ptr", + "description": "C++ reference on std::shared_ptr" + }, + { + "url": "https://en.cppreference.com/w/cpp/memory/weak_ptr", + "description": "C++ reference on std::weak_ptr" + } +] diff --git a/concepts/strings/.meta/config.json b/concepts/strings/.meta/config.json index 38ee587ab..a9cddbc99 100644 --- a/concepts/strings/.meta/config.json +++ b/concepts/strings/.meta/config.json @@ -1,6 +1,7 @@ { - "blurb": "TODO: add blurb for strings concept", + "blurb": "Strings in C++ are used to store and manipulate sequences of characters. Common operations are concatenation, comparison, and extraction.", "authors": [ - "silvanocerza" + "silvanocerza", + "vaeng" ] } diff --git a/concepts/strings/about.md b/concepts/strings/about.md index b16b72ba2..1d5d48422 100644 --- a/concepts/strings/about.md +++ b/concepts/strings/about.md @@ -1,3 +1,65 @@ # About -TODO: add information on strings concept +A `string` in C++ is a mutable object that represents text as a sequence of characters (letters, digits, punctuation, etc.). +Strings are manipulated by calling the string's methods. + +## Strings Library + +In C++ the string type and the associated functions have to be included from the strings library before usage. +You can do so by adding `#include ` to the top of your file. +They will then populate the `std` namespace. +The string literal uses the double quote character: `"`. + +```cpp +#include +std::string w_berry_quote{"A well-made sentence, I think, is a thing of beauty."}; +``` + +## Common String Operations + +You can use the `+` operator to concatenate strings: + +```cpp +std::string original_title{"The School of Rock"}; +std::string sequel_indicator{"Electric Boogaloo"}; +std::string next_movie_title = original_title + " 2: " + sequel_indicator; +``` + +To use the strings library, you need to know that it is possible to call a function that belongs to an object. +These are called member functions. +Later in the syllabus, you will learn more about member functions and the connected class concept. + +```cpp +std::string qualification{"awesome"}; +// 1st argument: from the index to the end of the string: +std::string who_is_awesome = qualification.substr(5); +// => "me" + +// 2nd optional argument for the length: +std::string material{"haunted books"}; +std::string ghost = material.substr(8, 3); +// => "boo" +``` + + +The `find` function is also very useful. +It is called as a member function on the string and takes a string as the argument. +`find` returns the zero-indexed position of the first occurrence in the string. + +```cpp +std::string new_release{"apple released a new app!"}; +new_release.find("app"); +// => 0 +new_release.find("!"); +// => 24 +``` + +There is also the `to_string` function, which can be used to convert integers and float/double values to string. +`to_string` returns a string. + +```cpp +int num{92}; +double exponent{0.92e2}; +std::string msg{ std::to_string(num) + " can also be written as " + std::to_string(exponent)}; +// => 92 can also be written as 92.0000000 +``` diff --git a/concepts/strings/introduction.md b/concepts/strings/introduction.md index 6dd0472a0..171e651a4 100644 --- a/concepts/strings/introduction.md +++ b/concepts/strings/introduction.md @@ -1,5 +1,65 @@ # Introduction -TODO: the content below is copied from the exercise introduction and probably needs rewriting to a proper concept introduction +A `string` in C++ is a mutable object that represents text as a sequence of characters (letters, digits, punctuation, etc.). +Strings are manipulated by calling the string's methods. -A `string` in C++ is a mutable object that represents text as a sequence of Unicode characters (letters, digits, punctuation, etc.). Strings are manipulated by calling the string's methods. +## Strings Library + +In C++ the string type and the associated functions have to be included from the strings library before usage. +You can do so by adding `#include ` to the top of your file. +They will then populate the `std` namespace. +The fully qualified name of the string type is `std::string`. +The string literal uses the double quote character: `"`. + +```cpp +#include +std::string w_berry_quote{"A well-made sentence, I think, is a thing of beauty."}; +``` + +## Common String Operations + +You can use the `+` operator to concatenate strings: + +```cpp +std::string original_title{"The School of Rock"}; +std::string sequel_indicator{"Electric Boogaloo"}; +std::string next_movie_title = original_title + " 2: " + sequel_indicator; +``` + +To use the strings library, you need to know that it is possible to call a function that belongs to an object. +These are called member functions. +Later in the syllabus, you will learn more about member functions and the connected class concept. + +```cpp +std::string qualification{"awesome"}; +// 1st argument: from the index to the end of the string: +std::string who_is_awesome = qualification.substr(5); +// => "me" + +// 2nd optional argument for the length: +std::string material{"haunted books"}; +std::string ghost = material.substr(8, 3); +// => "boo" +``` + + +The `find` function is also very useful. +It is called as a member function on the string and takes a string as the argument. +`find` returns the zero-indexed position of the _first_ occurrence in the string. + +```cpp +std::string new_release{"apple released a new app!"}; +new_release.find("app"); +// => 0 +new_release.find("e"); +// => 4 +``` + +There is also the `std::to_string` function, which can be used to convert integers and float/double values to string. + +```cpp +int num{92}; +double exponent{0.92e2}; +std::string msg{ std::to_string(num) + " can also be written as " + std::to_string(exponent)}; +// => 92 can also be written as 92.0000000 +``` diff --git a/concepts/strings/links.json b/concepts/strings/links.json index fe51488c7..5ba25e76f 100644 --- a/concepts/strings/links.json +++ b/concepts/strings/links.json @@ -1 +1,6 @@ -[] +[ + { + "url": "https://en.cppreference.com/w/cpp/string/basic_string", + "description": "C++ reference on strings" + } +] diff --git a/concepts/switch/.meta/config.json b/concepts/switch/.meta/config.json new file mode 100644 index 000000000..1b846cb6b --- /dev/null +++ b/concepts/switch/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "Switches control the flow of a program based on the value of a variable or expression.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/switch/about.md b/concepts/switch/about.md new file mode 100644 index 000000000..500c10176 --- /dev/null +++ b/concepts/switch/about.md @@ -0,0 +1,70 @@ +# About + +Like other languages, C++ also provides a `switch` statement. +Switch statements are a shorter way to write long `if ... else if` statements. +To make a switch, we start by using the keyword `switch` followed by an integer. +We then declare each one of the conditions with the `case` keyword. +We can also declare a `default` case, that will run when none of the previous `case` conditions match. +Each case should end with a `break` (or a `return`) statement. + +```cpp +int price{0}; +int adults{3}; +int kids{2}; + +switch (int group_size{adults + kids}) { + case 1: + price = 50; + break; + case 2: + price = 70; + break; + default: + price = group_size * 30; +} +``` + +## Fall-through + +One important thing about the switch construct is that the code will continue to execute until it is stopped by a `break` (or a `return`) statement. +This can lead to unexpected behavior. + +```cpp +int adults{1}; +int kids{0}; +switch (int group_size{adults + kids}) { + case 1: + price = 50; + case 2: + price = 70; + default: + price = group_size * 30; +} +// price will be 30! +``` + +The main use case for this continued execution feature is a statement that has several labels. +Multiple switch results can map to the same piece of code to be executed. +This way - in a booking app, for example - the called function for group sizes 2 and 3 can be the same: +```cpp +switch (group_size) { +case 1: + book_room(); + break; +case 2: +case 3: + book_apartment(group_size); + break; +default: + book_house(group_size); +} +// book_apartment happens wheng roup_size is 2 or 3 +``` + +~~~~exercism/note +As you have seen C++ the switch statement is very limited when it is compared to other languages. +Many languages can do switches based on boolean expression or even regular expressions. +You can think of the C++ version is an extended goto/switch construct. +Only the switch statement can jump to a label. +Afterwards all other labels are ignored and you have to use the `break` statement to break out of the code block. +~~~~ diff --git a/concepts/switch/introduction.md b/concepts/switch/introduction.md new file mode 100644 index 000000000..2963c8313 --- /dev/null +++ b/concepts/switch/introduction.md @@ -0,0 +1,62 @@ +# Introduction + +Like other languages, C++ also provides a `switch` statement. +Switch statements are a shorter way to write long `if ... else if` statements. +To make a switch, we start by using the keyword `switch` followed by an integer. +We then declare each one of the conditions with the `case` keyword. +We can also declare a `default` case, that will run when none of the previous `case` conditions match. +Each case should end with a `break` (or a `return`) statement. + +```cpp +int price{0}; +int adults{3}; +int kids{2}; + +switch (int group_size{adults + kids}) { + case 1: + price = 50; + break; + case 2: + price = 70; + break; + default: + price = group_size * 30; +} +``` + +## Fall-through + +One important thing about the switch construct is that the code will continue to execute until it is stopped by a `break` (or a `return`) statement. +This can lead to unexpected behavior. + +```cpp +int adults{1}; +int kids{0}; +switch (int group_size{adults + kids}) { + case 1: + price = 50; + case 2: + price = 70; + default: + price = group_size * 30; +} +// price will be 30! +``` + +The main use case for this continued execution feature is a statement that has several labels. +Multiple switch results can map to the same piece of code to be executed. +This way - in a booking app, for example - the called function for group sizes 2 and 3 can be the same: +```cpp +switch (group_size) { +case 1: + book_room(); + break; +case 2: +case 3: + book_apartment(group_size); + break; +default: + book_house(group_size); +} +// book_apartment happens when group_size is 2 or 3 +``` diff --git a/concepts/switch/links.json b/concepts/switch/links.json new file mode 100644 index 000000000..477189236 --- /dev/null +++ b/concepts/switch/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/language/switch", + "description": "C++ reference on switch statements" + }, + { + "url": "https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-break", + "description": "CppCoreGuidelines' hints on switch statements" + } +] diff --git a/concepts/vector-arrays/.meta/config.json b/concepts/vector-arrays/.meta/config.json new file mode 100644 index 000000000..6e033e310 --- /dev/null +++ b/concepts/vector-arrays/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "In C++, the array and vector types provide flexible ways to store and access multiple elements of the same data type, with arrays offering fixed-size containers while vectors offer dynamic resizing capabilities, making them suitable for various data storage and manipulation tasks.", + "authors": [ + "vaeng" + ], + "contributors": [ + ] +} diff --git a/concepts/vector-arrays/about.md b/concepts/vector-arrays/about.md new file mode 100644 index 000000000..09bab8b60 --- /dev/null +++ b/concepts/vector-arrays/about.md @@ -0,0 +1,72 @@ +# About + +## Arrays and Vectors + +C++ offers different containers to store elements of the same type in an ordered way. +There is `std::array` for containers of a fixed size and `std::vector`, which comes with dynamic resizing capabilities. + +### Construction + +When you declare an array or a vector you need to specify the type of elements, that container will hold. +Arrays also need a size. +Look at these examples to see the two container types' initializations: + +```cpp +#include +#include + +// std::array variable_name {list of elements} +std::array indie_rock {"yeah", "yeah", "yeah"}; +// indie_rock contains the elements "yeah" three times +``` + +Vectors usually need more space, as they allocate memory for further growth. +You do not need to specify a size: + +```cpp +#include + +// std::vector variable_name {list of elements} +std::vector countdown {3, 2, 1}; +// my_vector contains the elements 3, 2 and 1 +``` + +> Note: You do not need to know the exact mechanics behind the template concept yet, it will have its own concept further down the syllabus tree. + +### Element access + +Vectors and arrays share the same functions to access their elements. +You can use the member functions `front` and `back` to get the first and last elements of the container. +There is also `at` and the `[]` operator to access speficic elements. + +```cpp +countdown[0]; +// => 3 +countdown.at(2); +// => 1 +countdown[1] = 4; +// countdown now contains 3, 4 and 1 +indie_rock.back() = "yeahs"; +// indie_rock is now "yeah", "yeah", "yeahs" +``` + +> Note: If you pick a position that is not in the range of your container, `[]` might lead to undefined behavior. +> The `at` function would raise an exception, which might make your life easier in the long term. + +### Vector modifiers + +As a `vector` is not fixed in size, it is possible to add or remove elements. +Two common functions for that purpose are `emplace_back` and `pop_back`. + +```cpp +std::vector witches {"Holly", "Alyssa", "Shannen"}; +witches.pop_back(); +// Shannen is no longer with the witches +witches.emplace_back("Rose"); +// Rose has joined the team +``` + +### Capacity + +You can check the containers for emptiness with the member function `empty`. +If you want to know the number of elements, you can use `size`. diff --git a/concepts/vector-arrays/introduction.md b/concepts/vector-arrays/introduction.md new file mode 100644 index 000000000..7e96cbb39 --- /dev/null +++ b/concepts/vector-arrays/introduction.md @@ -0,0 +1,72 @@ +# Introduction + +## Arrays and Vectors + +C++ offers different containers to store elements of the same type in an ordered way. +There is `std::array` for containers of a fixed size and `std::vector`, which comes with dynamic resizing capabilities. + +### Construction + +When you declare an array or a vector you need to specify the type of elements, that container will hold. +Arrays also need a size. +Look at these examples to see the two container types' initializations: + +```cpp +#include +#include + +// std::array variable_name {list of elements} +std::array indie_rock {"yeah", "yeah", "yeah"}; +// indie_rock contains the elements "yeah" three times +``` + +Vectors usually need more space, as they allocate memory for further growth. +You do not need to specify a size: + +```cpp +#include + +// std::vector variable_name {list of elements} +std::vector countdown {3, 2, 1}; +// countdown contains the elements 3, 2 and 1 +``` + +> Note: You do not need to know the exact mechanics behind the template concept yet, it will have its own concept further down the syllabus tree. + +### Element access + +Vectors and arrays share the same functions to access their elements. +You can use the member functions `front` and `back` to get the first and last elements of the container. +There is also `at` and the `[]` operator to access speficic elements. + +```cpp +countdown[0]; +// => 3 +countdown.at(2); +// => 1 +countdown[1] = 4; +// countdown now contains 3, 4 and 1 +indie_rock.back() = "yeahs"; +// indie_rock is now "yeah", "yeah", "yeahs" +``` + +> Note: If you pick a position that is not in the range of your container, `[]` might lead to undefined behavior. +> The `at` function would raise an exception, which might make your life easier in the long term. + +### Vector modifiers + +As a `vector` is not fixed in size, it is possible to add or remove elements. +Two common functions for that purpose are `emplace_back` and `pop_back`. + +```cpp +std::vector witches {"Holly", "Alyssa", "Shannen"}; +witches.pop_back(); +// Shannen is no longer with the witches +witches.emplace_back("Rose"); +// Rose has joined the team +``` + +### Capacity + +You can check the containers for emptiness with the member function `empty`. +If you want to know the number of elements, you can use `size`. diff --git a/concepts/vector-arrays/links.json b/concepts/vector-arrays/links.json new file mode 100644 index 000000000..2699bfe12 --- /dev/null +++ b/concepts/vector-arrays/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://en.cppreference.com/w/cpp/container/array", + "description": "C++ reference on arrays" + }, + { + "url": "https://en.cppreference.com/w/cpp/container/vector", + "description": "C++ reference on vectors" + } +] diff --git a/config.json b/config.json index 0d230dd2e..5e5b97153 100644 --- a/config.json +++ b/config.json @@ -3,9 +3,9 @@ "slug": "cpp", "active": true, "status": { - "concept_exercises": false, + "concept_exercises": true, "test_runner": true, - "representer": false, + "representer": true, "analyzer": false }, "blurb": "C++ is a general-purpose programming language that supports procedural, object-oriented, generic and functional programming styles. C++ is deployed on billions of devices from the smallest embedded microprocessor to the largest supercomputer.", @@ -16,7 +16,7 @@ "highlightjs_language": "cpp" }, "test_runner": { - "average_run_time": 2.0 + "average_run_time": 2 }, "files": { "solution": [ @@ -27,8 +27,8 @@ "%{snake_slug}_test.cpp" ], "example": [ - "example.cpp", - "example.h" + ".meta/example.cpp", + ".meta/example.h" ], "exemplar": [ ".meta/exemplar.cpp", @@ -38,14 +38,188 @@ "exercises": { "concept": [ { - "slug": "strings", - "name": "Strings", + "slug": "log-levels", + "name": "Log Levels", "uuid": "62f945e7-0791-46b7-8161-855183f736ba", "concepts": [ - "strings" + "strings", + "includes" ], - "prerequisites": [], - "status": "wip" + "prerequisites": [ + "basics", + "namespaces" + ] + }, + { + "slug": "lasagna", + "name": "Lasagna", + "uuid": "00ab71eb-4ebb-4360-8621-a394040c894a", + "concepts": [ + "basics" + ], + "prerequisites": [] + }, + { + "slug": "freelancer-rates", + "name": "Freelancer Rates", + "uuid": "88baa5ab-dacb-4fab-8f16-4f3ed726b27b", + "concepts": [ + "numbers" + ], + "prerequisites": [ + "basics", + "includes" + ] + }, + { + "slug": "last-will", + "name": "Last Will", + "uuid": "d3946a64-a2a3-4dfc-8ab7-3ac1b6721b39", + "concepts": [ + "namespaces" + ], + "prerequisites": [ + "basics" + ] + }, + { + "slug": "pacman-rules", + "name": "Pacman Rules", + "uuid": "8da713a2-6d34-49f7-893b-23a4bb45d3a1", + "concepts": [ + "booleans" + ], + "prerequisites": [ + "basics" + ] + }, + { + "slug": "vehicle-purchase", + "name": "Vehicle Purchase", + "uuid": "e12f3678-202a-4d71-89d8-243ecc90b33a", + "concepts": [ + "comparisons", + "if-statements" + ], + "prerequisites": [ + "strings", + "booleans", + "numbers" + ] + }, + { + "slug": "making-the-grade", + "name": "Making the Grade", + "uuid": "b9d0b273-1c48-492a-8c61-44f432c8beff", + "concepts": [ + "vector-arrays" + ], + "prerequisites": [ + "loops", + "strings", + "if-statements", + "booleans", + "numbers" + ] + }, + { + "slug": "interest-is-interesting", + "name": "Interest is interesting", + "uuid": "6be13d54-4d1e-448e-a65d-c5d3d8abbfe0", + "concepts": [ + "loops" + ], + "prerequisites": [ + "comparisons", + "if-statements" + ] + }, + { + "slug": "ellens-alien-game", + "name": "Ellen's Alien Game", + "uuid": "cb6915ba-1539-4c4c-8313-38bfc69c4531", + "concepts": [ + "classes" + ], + "prerequisites": [ + "comparisons", + "if-statements" + ] + }, + { + "slug": "election-day", + "name": "Election Day", + "uuid": "431b877a-cb73-41aa-b9e4-034d9179faf0", + "concepts": [ + "references" + ], + "prerequisites": [ + "classes", + "vector-arrays" + ] + }, + { + "slug": "troll-the-trolls", + "name": "Troll the Trolls", + "uuid": "61c007d9-d856-4922-9098-e06a505363ed", + "concepts": [ + "switch", + "enums" + ], + "prerequisites": [ + "classes", + "comparisons" + ] + }, + { + "slug": "doctor-data", + "name": "Doctor Data", + "uuid": "2d2efdad-4f5d-4a1d-9626-381b37e72e3f", + "concepts": [ + "headers" + ], + "prerequisites": [ + "classes", + "enums" + ] + }, + { + "slug": "lasagna-master", + "name": "lasagna-master", + "uuid": "fe7fc6b7-9707-4988-a4c6-3fddf6ce3389", + "concepts": [ + "functions" + ], + "prerequisites": [ + "classes", + "vector-arrays", + "references", + "headers" + ] + }, + { + "slug": "power-of-troy", + "name": "Power of Troy", + "uuid": "9f5e64e5-2838-4237-9cc6-fef500aacd7b", + "concepts": [ + "auto", + "smart-pointers" + ], + "prerequisites": [ + "pointers" + ] + }, + { + "slug": "speedywagon", + "name": "Speedywagon Foundation", + "uuid": "ed2148a5-674c-4660-a050-b11ab240b876", + "concepts": [ + "pointers" + ], + "prerequisites": [ + "classes", + "references" + ] } ], "practice": [ @@ -64,8 +238,12 @@ "slug": "leap", "name": "Leap", "uuid": "76a840c7-24f1-455d-b62e-da42b13f8dd5", - "practices": [], - "prerequisites": [], + "practices": [ + "if-statements" + ], + "prerequisites": [ + "booleans" + ], "difficulty": 1, "topics": [ "conditionals", @@ -76,8 +254,12 @@ "slug": "reverse-string", "name": "Reverse String", "uuid": "c6946af0-3c2f-4f76-8b30-a1643cd5be63", - "practices": [], - "prerequisites": [], + "practices": [ + "loops" + ], + "prerequisites": [ + "strings" + ], "difficulty": 2, "topics": [ "strings" @@ -120,7 +302,10 @@ "slug": "raindrops", "name": "Raindrops", "uuid": "9155f4ea-3566-4a57-a2c5-76535d931ccc", - "practices": [], + "practices": [ + "strings", + "if-statements" + ], "prerequisites": [], "difficulty": 2, "topics": [ @@ -141,7 +326,7 @@ }, { "slug": "two-fer", - "name": "Two Fer", + "name": "Two-Fer", "uuid": "95a162a4-d825-4874-9e2f-014787cbdfb5", "practices": [], "prerequisites": [], @@ -190,7 +375,7 @@ }, { "slug": "rna-transcription", - "name": "Rna Transcription", + "name": "RNA Transcription", "uuid": "2fbd008c-5007-4403-ae71-f4d1f5ccaf8f", "practices": [], "prerequisites": [], @@ -213,10 +398,14 @@ }, { "slug": "difference-of-squares", - "name": "Difference Of Squares", + "name": "Difference of Squares", "uuid": "46cb230c-8ce9-431d-9dea-a195a3abd117", - "practices": [], - "prerequisites": [], + "practices": [ + "numbers" + ], + "prerequisites": [ + "loops" + ], "difficulty": 3, "topics": [ "loops", @@ -250,8 +439,12 @@ "slug": "sieve", "name": "Sieve", "uuid": "7ea05dd1-2c3c-499e-8608-dc76d30c5457", - "practices": [], - "prerequisites": [], + "practices": [ + "vector-arrays" + ], + "prerequisites": [ + "loops" + ], "difficulty": 5, "topics": [ "loops", @@ -299,8 +492,12 @@ "slug": "allergies", "name": "Allergies", "uuid": "80ae6a8e-8464-4f3a-afed-14d424757413", - "practices": [], - "prerequisites": [], + "practices": [ + "classes" + ], + "prerequisites": [ + "strings" + ], "difficulty": 3, "topics": [ "bitwise_operations", @@ -311,8 +508,12 @@ "slug": "protein-translation", "name": "Protein Translation", "uuid": "fb73e822-4831-4d33-a049-44a514bfa46a", - "practices": [], - "prerequisites": [], + "practices": [ + "strings" + ], + "prerequisites": [ + "vector-arrays" + ], "difficulty": 3, "topics": [ "filtering", @@ -322,7 +523,7 @@ }, { "slug": "sum-of-multiples", - "name": "Sum Of Multiples", + "name": "Sum of Multiples", "uuid": "a190ad11-db1c-4624-a477-e1d0c91d8b4f", "practices": [], "prerequisites": [], @@ -335,8 +536,12 @@ "slug": "prime-factors", "name": "Prime Factors", "uuid": "ba755932-b301-41cc-b7f2-5e6d2e130325", - "practices": [], - "prerequisites": [], + "practices": [ + "vector-arrays" + ], + "prerequisites": [ + "loops" + ], "difficulty": 5, "topics": [ "loops", @@ -395,8 +600,12 @@ "slug": "atbash-cipher", "name": "Atbash Cipher", "uuid": "15c741a0-944d-4b06-a096-6af63137347a", - "practices": [], - "prerequisites": [], + "practices": [ + "strings" + ], + "prerequisites": [ + "loops" + ], "difficulty": 3, "topics": [ "algorithms", @@ -407,8 +616,12 @@ "slug": "trinary", "name": "Trinary", "uuid": "c8246de0-29b1-4f92-8b00-b766e395ad66", - "practices": [], - "prerequisites": [], + "practices": [ + "numbers" + ], + "prerequisites": [ + "loops" + ], "difficulty": 3, "topics": [ "math", @@ -495,8 +708,12 @@ "slug": "bob", "name": "Bob", "uuid": "d80210b8-45e8-4e5c-8b07-9e87c33c5959", - "practices": [], - "prerequisites": [], + "practices": [ + "loops" + ], + "prerequisites": [ + "strings" + ], "difficulty": 5, "topics": [ "conditionals", @@ -546,7 +763,7 @@ }, { "slug": "etl", - "name": "Etl", + "name": "ETL", "uuid": "0dd45f6a-c6cd-4549-a56b-7babe0a71add", "practices": [], "prerequisites": [], @@ -561,8 +778,13 @@ "slug": "matching-brackets", "name": "Matching Brackets", "uuid": "4a517292-3472-40f2-b4b8-5c8c25219ea5", - "practices": [], - "prerequisites": [], + "practices": [ + "vector-arrays" + ], + "prerequisites": [ + "strings", + "loops" + ], "difficulty": 3, "topics": [ "pattern_matching", @@ -598,8 +820,12 @@ "slug": "luhn", "name": "Luhn", "uuid": "885865bc-a197-436f-94bc-b1998d5cc081", - "practices": [], - "prerequisites": [], + "practices": [ + "numbers" + ], + "prerequisites": [ + "loops" + ], "difficulty": 1, "topics": [ "math", @@ -622,8 +848,12 @@ "slug": "armstrong-numbers", "name": "Armstrong Numbers", "uuid": "1035aa3f-030a-425b-84f1-1967f344d155", - "practices": [], - "prerequisites": [], + "practices": [ + "loops" + ], + "prerequisites": [ + "includes" + ], "difficulty": 3, "topics": [ "algorithms", @@ -647,8 +877,12 @@ "slug": "hexadecimal", "name": "Hexadecimal", "uuid": "c2e8e3fc-a39c-434b-87dd-9524636aa804", - "practices": [], - "prerequisites": [], + "practices": [ + "numbers" + ], + "prerequisites": [ + "loops" + ], "difficulty": 3, "topics": [ "math", @@ -670,7 +904,7 @@ }, { "slug": "pascals-triangle", - "name": "Pascals Triangle", + "name": "Pascal's Triangle", "uuid": "7ed4434a-3564-4194-b91a-d3baeec9c519", "practices": [], "prerequisites": [], @@ -680,6 +914,20 @@ "math" ] }, + { + "slug": "perfect-numbers", + "name": "Perfect Numbers", + "uuid": "b5ab8c58-607e-4850-98b4-ac45f4ba3466", + "practices": [], + "prerequisites": [], + "difficulty": 3, + "topics": [ + "enums", + "conditionals", + "loops", + "math" + ] + }, { "slug": "scrabble-score", "name": "Scrabble Score", @@ -721,6 +969,332 @@ "strings", "variables" ] + }, + { + "slug": "bank-account", + "name": "Bank Account", + "uuid": "56cbac7e-8bd8-4cd5-bfc5-0bc512e95de0", + "practices": [], + "prerequisites": [], + "difficulty": 8, + "topics": [ + "basics", + "classes", + "conditionals", + "numbers", + "threads" + ] + }, + { + "slug": "darts", + "name": "Darts", + "uuid": "e8ad3859-9999-4a36-900d-d82fa6e74b08", + "practices": [ + "if-statements" + ], + "prerequisites": [ + "numbers" + ], + "difficulty": 2, + "topics": [ + "basics", + "bools", + "conditionals", + "numbers" + ] + }, + { + "slug": "largest-series-product", + "name": "Largest Series Product", + "uuid": "bf25f471-20d9-4f41-abf7-5d0dc5ad2d87", + "practices": [], + "prerequisites": [], + "difficulty": 4, + "topics": [ + "bools", + "conditionals", + "lists", + "loops", + "numbers", + "strings" + ] + }, + { + "slug": "isbn-verifier", + "name": "ISBN Verifier", + "uuid": "5fcc6fd8-1dc7-4493-970a-731c7602b229", + "practices": [], + "prerequisites": [], + "difficulty": 1, + "topics": [ + "conditionals", + "loops", + "strings" + ] + }, + { + "slug": "linked-list", + "name": "Linked List", + "uuid": "6f6c55dd-db6b-40ca-96f2-d9b7232dcbb0", + "practices": [], + "prerequisites": [], + "difficulty": 6, + "topics": [ + "classes", + "conditionals", + "loops", + "pointers" + ] + }, + { + "slug": "simple-linked-list", + "name": "Simple Linked List", + "uuid": "ffccabe9-9779-4914-9cff-c6f5696c8afe", + "practices": [ + "pointers" + ], + "prerequisites": [], + "difficulty": 4, + "topics": [ + "classes", + "conditionals", + "loops", + "pointers" + ] + }, + { + "slug": "high-scores", + "name": "High Scores", + "uuid": "10448fb8-60eb-4c67-93e8-488ecbde8676", + "practices": [ + "vector-arrays" + ], + "prerequisites": [ + "classes" + ], + "difficulty": 4 + }, + { + "slug": "eliuds-eggs", + "name": "Eliud's Eggs", + "uuid": "cfe01bad-0972-466c-b52a-9b8cf563b2f4", + "practices": [ + "loops" + ], + "prerequisites": [ + "if-statements", + "comparisons" + ], + "difficulty": 3 + }, + { + "slug": "diamond", + "name": "Diamond", + "uuid": "f8298f68-ef4e-41e1-99af-85a1bef71561", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, + { + "slug": "run-length-encoding", + "name": "Run-Length Encoding", + "uuid": "916fedd1-a9ed-4200-b115-cc1d1bc44aba", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, + { + "slug": "rotational-cipher", + "name": "Rotational Cipher", + "uuid": "21d02535-4ab6-455d-88e1-08ec3b578894", + "practices": [ + "strings" + ], + "prerequisites": [ + "loops" + ], + "difficulty": 3 + }, + { + "slug": "resistor-color", + "name": "Resistor Color", + "uuid": "15a9277e-59e2-484a-9877-64437556825c", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "resistor-color-duo", + "name": "Resistor Color Duo", + "uuid": "83ce95e9-f8af-4dbb-b96b-d94b12421d77", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, + { + "slug": "flower-field", + "name": "Flower Field", + "uuid": "d17d040b-b214-4079-ad3e-b518f776aeeb", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "minesweeper", + "name": "Minesweeper", + "uuid": "3753a72a-78b7-429f-b79a-f68d55a00387", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "status": "deprecated" + }, + { + "slug": "sublist", + "name": "Sublist", + "uuid": "801cb315-91d6-48e6-92f4-36423a89d4e1", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "zebra-puzzle", + "name": "Zebra Puzzle", + "uuid": "2badf89b-efc4-46f1-af8d-b7d2440e61c0", + "practices": [], + "prerequisites": [], + "difficulty": 7 + }, + { + "slug": "list-ops", + "name": "List Ops", + "uuid": "9498d0bf-c411-4b3b-b0c7-a063aa7f0acc", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "knapsack", + "name": "Knapsack", + "uuid": "35705d66-fe72-4dee-8eb0-54823240f2d7", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, + { + "slug": "two-bucket", + "name": "Two Bucket", + "uuid": "83c92b7f-fb0e-43d8-ab7d-d514408b8575", + "practices": [], + "prerequisites": [], + "difficulty": 7 + }, + { + "slug": "yacht", + "name": "Yacht", + "uuid": "3abaa99e-14e5-434e-ae50-e9d549c5ae6e", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, + { + "slug": "kindergarten-garden", + "name": "Kindergarten Garden", + "uuid": "5bc93b17-5209-4517-985e-f6fb3252afb3", + "practices": [ + "enums" + ], + "prerequisites": [ + "vector-arrays" + ], + "difficulty": 3 + }, + { + "slug": "dnd-character", + "name": "D&D Character", + "uuid": "83e20a67-990e-4db7-b16a-48b4f516f893", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, + { + "slug": "spiral-matrix", + "name": "Spiral Matrix", + "uuid": "d5569902-5815-4b05-ab13-13826fef7826", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "parallel-letter-frequency", + "name": "Parallel Letter Frequency", + "uuid": "0a029212-bc55-4559-b3c6-2c26bec0d560", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, + { + "slug": "pig-latin", + "name": "Pig Latin", + "uuid": "e4242a6e-3611-4f49-890b-368f1ebbdc94", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, + { + "slug": "rail-fence-cipher", + "name": "Rail Fence Cipher", + "uuid": "8a7f6f8c-ff36-4afd-9ab3-6dbd355d0263", + "practices": [], + "prerequisites": [ + "basics", + "namespaces", + "loops", + "numbers", + "strings" + ], + "difficulty": 4 + }, + { + "slug": "affine-cipher", + "name": "Affine Cipher", + "uuid": "61374c16-0a99-4616-9244-a38919eafbe1", + "practices": [ + "math", + "exceptions" + ], + "prerequisites": [ + "functions", + "comparisons", + "loops", + "booleans" + ], + "difficulty": 6 + }, + { + "slug": "alphametics", + "name": "Alphametics", + "uuid": "0c69f8bd-c99a-4cb2-b113-82deaf0915ee", + "practices": [ + "auto", + "literals" + ], + "prerequisites": [ + "includes", + "namespaces", + "if-statements", + "booleans", + "headers", + "comparisons" + ], + "difficulty": 6 + }, + { + "slug": "twelve-days", + "name": "Twelve Days", + "uuid": "0daec3e8-a0d1-49e8-9de5-0ad73f9c3243", + "practices": [], + "prerequisites": [], + "difficulty": 3 } ], "foregone": [ @@ -732,51 +1306,146 @@ "uuid": "8d51e52a-95d3-45da-adb0-65bdfe96b875", "slug": "strings", "name": "Strings" + }, + { + "uuid": "de675a2c-6fd2-427d-93c1-4190eeffcd95", + "slug": "numbers", + "name": "Numbers" + }, + { + "uuid": "e52453a5-4997-4eba-b754-5bda7b97c701", + "slug": "basics", + "name": "Basics" + }, + { + "uuid": "257598c7-c09f-47be-90db-514f93758420", + "slug": "includes", + "name": "Includes" + }, + { + "uuid": "7b9bcf07-b447-4c18-b838-a7f4167ff521", + "slug": "namespaces", + "name": "Namespaces" + }, + { + "uuid": "d1edc955-7226-4f7d-9ce7-784e3f28a7c3", + "slug": "if-statements", + "name": "If Statements" + }, + { + "uuid": "1414040c-4f4f-45ab-b46b-03bc560e2511", + "slug": "booleans", + "name": "Booleans" + }, + { + "uuid": "bb5ee09c-db6b-41ef-ae17-f3c894c3b09a", + "slug": "headers", + "name": "Headers" + }, + { + "uuid": "6891847f-4fe2-48a1-9d27-bc079b0d975c", + "slug": "comparisons", + "name": "Comparisons" + }, + { + "uuid": "1f377a50-ebb2-4203-b4f5-ca90ccf5ad25", + "slug": "vector-arrays", + "name": "Arrays and Vectors" + }, + { + "uuid": "e356069a-09d4-417a-8688-8ab8ccb80a55", + "slug": "loops", + "name": "Loops" + }, + { + "uuid": "7a24381f-a7d3-4d3a-93e9-eb7cd19858b4", + "slug": "switch", + "name": "Switch Statements" + }, + { + "uuid": "fc00966e-dc25-46a6-8883-d6106c7d0774", + "slug": "classes", + "name": "Classes" + }, + { + "uuid": "f791086d-7bd1-41fb-9ba9-e7fe67128886", + "slug": "references", + "name": "References" + }, + { + "uuid": "2e4f454e-4a54-4999-9627-86840c88fc46", + "slug": "enums", + "name": "Enumerations" + }, + { + "uuid": "18b2f529-3be6-4a70-be7d-81aa07393585", + "slug": "functions", + "name": "Functions" + }, + { + "uuid": "c071a5e8-796c-4538-945a-3bdd2006fc2b", + "slug": "pointers", + "name": "Pointers" + }, + { + "uuid": "0316eb64-d1e9-44cc-b7d4-ea4b88f85ac3", + "slug": "smart-pointers", + "name": "Smart Pointers" + }, + { + "uuid": "da0a69e5-33ec-4458-8c70-80457715ada6", + "slug": "auto", + "name": "Auto" + }, + { + "uuid": "5de475cc-5321-476f-bd19-a82b60284f5a", + "slug": "literals", + "name": "Literals" } ], "key_features": [ { - "icon": "fast", "title": "Fast execution speed", - "content": "With execution speed in mind, C++ is used in a wide range of computation-intensive domains." + "content": "With execution speed in mind, C++ is used in a wide range of computation-intensive domains.", + "icon": "fast" }, { - "icon": "multi-paradigm", "title": "Multi-paradigm", - "content": "C++ supports a variety of programming paradigms like OOP, functional and procedural programming." + "content": "C++ supports a variety of programming paradigms like OOP, functional and procedural programming.", + "icon": "multi-paradigm" }, { - "icon": "powerful", "title": "High-level programming", - "content": "C++ standardizes a comprehensive library, enabling the developer to concentrate on the problem." + "content": "C++ standardizes a comprehensive library, enabling the developer to concentrate on the problem.", + "icon": "powerful" }, { - "icon": "homoiconic", "title": "Low-level programming", - "content": "C++ masters the strength of its predecessor C and gives the developer access to the bits and bytes." + "content": "C++ masters the strength of its predecessor C and gives the developer access to the bits and bytes.", + "icon": "homoiconic" }, { - "icon": "widely-used", "title": "Popular", - "content": "Thanks to the flexibility and portability, you can find applications written in C++ in every scale." + "content": "Thanks to the flexibility and portability, you can find applications written in C++ in every scale.", + "icon": "widely-used" }, { - "icon": "evolving", "title": "Ever-evolving", - "content": "Although mature, C++ is still evolving and developed by a committee to fit the developers needs." + "content": "Although mature, C++ is still evolving and developed by a committee to fit the developers needs.", + "icon": "evolving" } ], "tags": [ - "paradigm/object_oriented", - "typing/static", - "typing/strong", "execution_mode/compiled", + "paradigm/object_oriented", "platform/android", "platform/ios", "platform/linux", "platform/mac", "platform/windows", "runtime/standalone_executable", + "typing/static", + "typing/strong", "used_for/backends", "used_for/cross_platform_development", "used_for/embedded_systems", diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 8d6331522..aea73633f 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -64,6 +64,20 @@ of the Microsoft Visual C++ compiler. You should be able to use other integrated IDE's such as JetBrains' CLion in a similar manner. +### Installing Boost on Windows + +The `meetup` and `gigasecond` exercises depend on the [Boost][boost-website] libraries. +The following steps describe how to set that up. + +1. Download the [prebuilt windows libraries][sourceforge-boost-binaries] for Boost. + - The exercises require a minimum Boost version of 1.58. + - You can find the version of MSVC by clicking "Modify" in the Visual Studio Installer. +1. Extract the libraries to a local folder. +1. Set the `BOOST_ROOT` environment variable to the folder where you installed the Boost libraries. +1. If you had Visual Studio open while doing this, close it to make sure the value of `BOOST_ROOT` gets picked up. +1. Start Visual Studio and open the folder containing the `gigasecond` or `meetup` exercise. +1. The "Startup Item" should be `gigasecond.exe` or `meetup.exe` and you should get build errors in either `gigasecond_test.cpp` or `meetup_test.cpp` telling you to implement missing items, similar to the other exercises. + ## Linux All recent Linux distribution releases have compatible C++14 compilers, you @@ -86,3 +100,7 @@ to figure out how to install the minimum compiler version. MacOS users can install GCC with [Homebrew](http://brew.sh/) via `brew install gcc`. + + +[boost-website]: https://www.boost.org/ +[sourceforge-boost-binaries]: https://sourceforge.net/projects/boost/files/boost-binaries/ diff --git a/docs/TESTS.md b/docs/TESTS.md index 3c2463cc9..5b659c291 100644 --- a/docs/TESTS.md +++ b/docs/TESTS.md @@ -54,6 +54,7 @@ code using the appropriate command for your environment: * Linux with make: `make` * Windows with Visual Studio: Select Build / Build Solution from the menu. * MacOS with Xcode: Select Build from the toolbar +* MacOS with "Unix Makefile": `make` _If using the exercism CLI app_ Examples of running CMake for different environments are shown below. @@ -73,8 +74,8 @@ $ make This example shows creating empty files for the implementation before running CMake. -Simply type `make` in the build directory to compile the tests. This should -generate compile time errors. Once the errors are fixed, `make` will build and +Simply type `make` in the build directory to compile the tests. This should +generate compile time errors. Once the errors are fixed, `make` will build and run the tests. ### Windows with Visual Studio @@ -97,5 +98,32 @@ $ cmake -G Xcode .. ``` This example generates the XCode files so that you can open the project in XCode. -With the project opened in XCode you may need to add another target. +With the project opened in XCode you may need to add another target. For a new target go to File -> New -> Target from the menu, _OS X \ Application \ Command Line Tool_ -> Next _C++_ as the language and provide a target name. Run the code using the target. + +If your terminal throws an `Xcode x.x not supported` error, it is highly likely that Xcode is not installed on your machine or that your version is out of date. +Please download and install Xcode via the App Store or via [this link][web-xcode-download]. +Errors similar to `The CXX compiler identification is unknown` will likely be resolved by following the [instructions to install GCC][cpp-installation-instructions] or by adding another target in Xcode as per the above paragraph. + +[web-xcode-download]: https://apps.apple.com/us/app/xcode/id497799835?mt=12 +[cpp-installation-instructions]: https://exercism.org/docs/tracks/cpp/installation + +### MacOS with Make _when using the Exercism CLI_ +#### This example specifically assists those who want to use the Exercism CLI app for configuring, testing, and submitting their C++ exercises. + +The generator name for CMake is `Unix Makefiles`. +Assuming the current exercise is `bob` and we're in the exercise folder: + +```sh +$ touch bob.{h,cpp} +$ cmake -G "Unix Makefiles" . +$ exercism test +``` + +In this example, we do create empty files for the implementation before running CMake, but we do **not** create an empty build directory. +This is because the Exercism CLI uses the `.exercism/metadata.json` file in the root directory to parse the test cases. + +Simply type `exercism test` in the root directory to compile the tests. This should generate compile-time errors. +Once the errors are fixed, running `exercism test` again will build and execute the tests using the Exercism CLI. + +[cpp-installation-instructions]: https://exercism.org/docs/tracks/cpp/installation diff --git a/exercises/concept/doctor-data/.docs/hints.md b/exercises/concept/doctor-data/.docs/hints.md new file mode 100644 index 000000000..9fd16c8d3 --- /dev/null +++ b/exercises/concept/doctor-data/.docs/hints.md @@ -0,0 +1,24 @@ +# Hints + +## General + +- Pay attention to any enumerators you come across. +- The tests can only be run when you have implemented every task. + To see the test results you only need a rough skeleton of the functions and classes. +- Every function, class, and enumeration that is used by the tests must be declared in the header file. + +## 1. Try to extract some first clues + +- The class and the enum are in different namespaces. +- Use a `class enum` for `System` +- You can either have two constructors, or one constructor with a default argument. + +## 2. Find more details + +- The first `REQUIRE` line checks for the default `System`. +- `generation` seems to increase by one in a cloned vessel. +- The `System` seems to be untouched by the cloning process. + +## 3. Look into the inner workings + +- The `shoot_buster` member function seems to be dependent on an internal counter. diff --git a/exercises/concept/doctor-data/.docs/instructions.md b/exercises/concept/doctor-data/.docs/instructions.md new file mode 100644 index 000000000..52ae5f14c --- /dev/null +++ b/exercises/concept/doctor-data/.docs/instructions.md @@ -0,0 +1,108 @@ +# Instructions + +You start your first day at an Australian company called "Doctor Data", which specializes in information recovery. +You aced your job interview through your knowledge of C++ and [von Neumann probes][van-neumann-probes]. +As you have seen a lot of test files, your new boss wants you to recreate the respective source and header files from some test code the company has recently recovered. + +In this exercise, you are going to recreate lost files. +Currently the content of the files is unusable garbage data. + +~~~~exercism/note +The workflow of this concept exercise is very similar to the structure of Exercism's practice exercises. +The exercise introduction text is only one part of the specification. +The test file is your definitive guide to solving a given problem. +Due to the way C++ compilation works, the test results might not show up in the web interface until you have implemented a minimal version of every class, function and enumeration that is required by the tests. +If you receive compilation errors, they might disappear once you have addressed all tasks and tests. +~~~~ + +You have four tasks, all related to recovering the lost code in the header and source files. + +## 1. Try to extract some first clues + +You look at two recovered lines from the test files: + +```cpp +heaven::Vessel bob{"Robert Johansson", 1}; +heaven::Vessel will{"Riker", 2, star_map::System::BetaHydri}; +``` + +Your sharp eye instantly recognized a namespace `heaven`. +You also see that there must be a class called `Vessel`. +The constructor can apparently be called with two or three arguments. +The first argument seems to be of type `string`, the second one is a number. +It is possible to initialize the `Vessel` class with a third argument. +The third argument comes from a `star_map` namespace. +It is an enumerator of type `System`. +You even got one of the enumerations: `BetaHydri`. + +Prepare the source and header files with your discovered information. +You need two namespaces: `heaven` and `star_map`. +The `heaven` namespace has a class `Vessel`, which can be called with two or three arguments. +The `System` enum is to be placed in the `star_map` namespace and has an `BetaHydri` enumeration. +Keep an eye out for other enumerations, so that you can constantly update the `System` enum. + +## 2. Find more details + +You uncover more lines from the test: + +```cpp +heaven::Vessel bob{"Robert Johansson", 1}; +REQUIRE(bob.current_system == star_map::System::Sol); +REQUIRE(bob.generation == 1); +heaven::Vessel bob5 = bob.replicate("Mario"); +REQUIRE(bob5.current_system == star_map::System::Sol); +REQUIRE(bob5.generation == 2); +``` + +The newly found test lines uncover another member function of the `Vessel` class: `replicate`. +This function receives a string and returns another `Vessel` instance. +You get an idea about the default value of the third argument of the constructor from the previous task. +You also see two public member variables of the `Vessel` class and the specification of the `replicate` member function. + +Add the `replicate` function and the public member variables `current_system` and `generation` to the header and source files. + +## 3. Look into the inner workings + +You find some more interesting lines in the recovered test files: + +```cpp +heaven::Vessel bob6{"Homer", 3, star_map::System::EpsilonEridani}; +REQUIRE(bob6.busters == 0); +bob6.make_buster(); +REQUIRE(bob6.busters == 1); +bool success = bob6.shoot_buster(); +REQUIRE(success); +REQUIRE(bob6.busters == 0); +success = bob6.shoot_buster(); +REQUIRE_FALSE(success); +``` + +Apparently, the `Vessel` class has a member variable `busters`, that can be changed with the two class member functions `make_buster` and `shoot_buster`. +Until other information surfaces, you take a guess that the `make_buster` function returns `void`. +As there is a test for the return value of `shoot_buster`, you assume that the function returns a `bool`. + +Add the two functions and the member variable to the `Vessel` class. +Keep looking for other `System` enumerators. + +## 4. Complete the picture + +During your scan of the test files you find only two uncovered sections of the code: + +```cpp +heaven::Vessel bob1{"Bob", 1, star_map::System::AlphaCentauri}; +heaven::Vessel marv{"Marvin", 2, star_map::System::DeltaEridani}; +heaven::Vessel milo{"Milo", 3, star_map::System::DeltaEridani}; +heaven::Vessel howie{"Howard", 4, star_map::System::Omicron2Eridani}; + +REQUIRE("Bob" == heaven::get_older_bob(bob1, marv)); +REQUIRE(heaven::in_the_same_system(marv, milo)); +REQUIRE_FALSE(heaven::in_the_same_system(marv, howie)); +``` + +You see two functions, that are not members of the `Vessel` class, as they are not called with an instance. +`get_older_bob` compares two `Vessel`instances and returns a `string`. +`in_the_same_system` compares two `Vessel`instances and returns a `bool`. + +Implement the last missing functions from the recovered lines above. + +[van-neumann-probes]: https://en.wikipedia.org/wiki/Self-replicating_spacecraft diff --git a/exercises/concept/doctor-data/.docs/introduction.md b/exercises/concept/doctor-data/.docs/introduction.md new file mode 100644 index 000000000..e7db48e12 --- /dev/null +++ b/exercises/concept/doctor-data/.docs/introduction.md @@ -0,0 +1,114 @@ +# Introduction + +In C++, declarations are often separated from definitions. +Declarations are grouped into so-called header files, with the respective implementations placed in source files. +You can think of the header files as an API. +The header file will tell you _what_ a codebase has to offer without going into the details of _how_. + +## Header and Source + +The most common file extension for header files is `.h`. +Some projects use `.hpp` or skip the extension completely. + +The definitions are located in a separate `.cpp` file. +To reunite the parts, the source file starts by _including_ the respective header file. + +If you want to write a library called "quick_math" that offers a function "super_root" that you want to use often, the files would look like this: + +```cpp +// A file named quick_math.h +#pragma once +namespace quick_math { + double super_root(double x, int n); +} +``` + +```cpp +// A file named quick_math.cpp +#include "quick_math.h" +#include +double quick_math::super_root(double x, int n) { + while(n) { x = std::sqrt(x), --n;} + return x; +} +``` + +If you need to include a header that is only required by the implementation, the respective `#include` line is only needed in the source file. +Everything that is included in the header is also available in the `.cpp` file, like the `string` library in the example below. +**Attention**: the `;` is needed after the declaration in the header file, but not after the definition in the source file. + +~~~~exercism/note +Many C++ exercises on Exercism start with two almost empty files: header and source. +You have to check the `*_test.cpp` file to see the names and namespaces of the expected functions in order to solve the exercise. +~~~~ + +## Classes and Headers + +Classes can become very complex and their relation to the header / source partition might be confusing. +One possible layout is to keep all the implementation details in the source file and all the declarations and member variables in the header: + +```cpp +// A file named robot_flower.h +#if !defined(ROBOT_FLOWER_H) +#define ROBOT_FLOWER_H +#include +namespace robots { + class Flower { + private: + bool needs_water{}; + int size{}; + std::string name{}; + public: + Flower(std::string name, int size = 0); + void give_water(); + std::string get_name(); + int get_size(); + void start_next_day(); + }; +} +#endif +``` + +```cpp +// A file named robot_flower.cpp +#include "robot_flower.h" +robots::Flower::Flower(std::string name, int size) {this->name = "Robotica " + name; this->size = size;} +void robots::Flower::start_next_day() {if (!needs_water) ++size; needs_water = true;} +std::string robots::Flower::get_name() {return name;} +int robots::Flower::get_size() {return size;} +``` + +When the header is used as an API overview, that is where a person would look for information like default values. +The `size` parameter's default of the constructor is therefore handled in the header and not in the implementation. +The definitions in the source file are prefixed with the namespace `robots` and the class type `Flower`. + +Another layout option is a _header only_ library, that does not have a `.cpp` file at all: + +```cpp +// A file named robot_flower.h +#pragma once +#include +namespace robots { + class Flower { + private: + bool needs_water{}; + int size{}; + std::string name{}; + public: + Flower(std::string name, int size = 0) {this->name = "Robotica " + name; this->size = size;} + void give_water() {needs_water = false;} + std::string get_name() {return name;} + int get_size() {return size;} + void start_next_day() {if (!needs_water) ++size; needs_water = true;} + }; +} +``` + +Projects might use combinations of these layouts and there is a lot of discussion as to what might be the best fit for each use case. + +## Include Guards + +You may have noticed the `#pragma once` line in the example header file above. +This is called an include guard - and it ensures that the content of the file is included only once during the compilation to avoid errors. +There is another, more complex variation of an include guard that starts with `#ifndef` and ends with `#endif`. +It serves the same purpose and its usage is shown in the `Flower` class example above. diff --git a/exercises/concept/doctor-data/.docs/introduction.md.tpl b/exercises/concept/doctor-data/.docs/introduction.md.tpl new file mode 100644 index 000000000..4bc156890 --- /dev/null +++ b/exercises/concept/doctor-data/.docs/introduction.md.tpl @@ -0,0 +1,3 @@ +# Introduction + +%{concept:headers} diff --git a/exercises/concept/doctor-data/.meta/config.json b/exercises/concept/doctor-data/.meta/config.json new file mode 100644 index 000000000..b78e53b43 --- /dev/null +++ b/exercises/concept/doctor-data/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "vaeng" + ], + "files": { + "solution": [ + "doctor_data.cpp", + "doctor_data.h" + ], + "test": [ + "doctor_data_test.cpp" + ], + "exemplar": [ + ".meta/exemplar.h", + ".meta/exemplar.cpp" + ] + }, + "blurb": "Learn about header files by recovering data." +} diff --git a/exercises/concept/doctor-data/.meta/design.md b/exercises/concept/doctor-data/.meta/design.md new file mode 100644 index 000000000..dcab28e2f --- /dev/null +++ b/exercises/concept/doctor-data/.meta/design.md @@ -0,0 +1,24 @@ +# Design + +## Learning objectives + +- Know what headers are. +- Know how to include a header file. +- Know what goes into the source or header file. + +## Out of scope + +- - + +## Concepts + +- `headers`: know what headers are; now how to include a header file; know what goes into the source or header file. + +## Prerequisites + +- `classes` +- `enums` + +## Analyzer + +- - diff --git a/exercises/concept/doctor-data/.meta/exemplar.cpp b/exercises/concept/doctor-data/.meta/exemplar.cpp new file mode 100644 index 000000000..55cd59af3 --- /dev/null +++ b/exercises/concept/doctor-data/.meta/exemplar.cpp @@ -0,0 +1,31 @@ +#include "doctor_data.h" + +namespace heaven { + +Vessel::Vessel(std::string name, int generation, + star_map::System current_system) + : name(name), generation(generation), current_system(current_system) {} + +void Vessel::make_buster() { ++busters; } +bool Vessel::shoot_buster() { + if (busters) { + --busters; + return true; + } + return false; +} + +Vessel Vessel::replicate(std::string new_name) { + Vessel clone{new_name, generation + 1, current_system}; + return clone; +} + +std::string get_older_bob(Vessel& vessel1, Vessel& vessel2) { + return vessel1.generation < vessel2.generation ? vessel1.name + : vessel2.name; +} + +bool in_the_same_system(Vessel& vessel1, Vessel& vessel2) { + return vessel1.current_system == vessel2.current_system; +} +} // namespace heaven diff --git a/exercises/concept/doctor-data/.meta/exemplar.h b/exercises/concept/doctor-data/.meta/exemplar.h new file mode 100644 index 000000000..8fa6bf0ae --- /dev/null +++ b/exercises/concept/doctor-data/.meta/exemplar.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace star_map { +enum class System { + EpsilonEridani, + Sol, + Omicron2Eridani, + DeltaEridani, + AlphaCentauri, + BetaHydri +}; +} +namespace heaven { +class Vessel { + public: + Vessel(std::string name, int generation, + star_map::System current_system = star_map::System::Sol); + void make_buster(); + bool shoot_buster(); + Vessel replicate(std::string name); + + std::string name{}; + int generation{}; + star_map::System current_system{}; + int busters{}; +}; + +std::string get_older_bob(Vessel& vessel1, Vessel& vessel2); +bool in_the_same_system(Vessel& vessel1, Vessel& vessel2); + +} // namespace heaven diff --git a/exercises/concept/doctor-data/CMakeLists.txt b/exercises/concept/doctor-data/CMakeLists.txt new file mode 100644 index 000000000..dba98a931 --- /dev/null +++ b/exercises/concept/doctor-data/CMakeLists.txt @@ -0,0 +1,67 @@ +# Get the exercise name from the current directory +get_filename_component(exercise ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +# Basic CMake project +cmake_minimum_required(VERSION 3.5.1) + +# Name the project after the exercise +project(${exercise} CXX) + +# Get a source filename from the exercise name by replacing -'s with _'s +string(REPLACE "-" "_" file ${exercise}) + +# Implementation could be only a header +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.cpp) + set(exercise_cpp ${file}.cpp) +else() + set(exercise_cpp "") +endif() + +# Use the common Catch library? +if(EXERCISM_COMMON_CATCH) + # For Exercism track development only + add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h $) +elseif(EXERCISM_TEST_SUITE) + # The Exercism test suite is being run, the Docker image already + # includes a pre-built version of Catch. + find_package(Catch2 REQUIRED) + add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h) + target_link_libraries(${exercise} PRIVATE Catch2::Catch2WithMain) + # When Catch is installed system wide we need to include a different + # header, we need this define to use the correct one. + target_compile_definitions(${exercise} PRIVATE EXERCISM_TEST_SUITE) +else() + # Build executable from sources and headers + add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h test/tests-main.cpp) +endif() + +set_target_properties(${exercise} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED OFF + CXX_EXTENSIONS OFF +) + +set(CMAKE_BUILD_TYPE Debug) + +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(GNU|Clang)") + set_target_properties(${exercise} PROPERTIES + COMPILE_FLAGS "-Wall -Wextra -Wpedantic -Werror" + ) +endif() + +# Configure to run all the tests? +if(${EXERCISM_RUN_ALL_TESTS}) + target_compile_definitions(${exercise} PRIVATE EXERCISM_RUN_ALL_TESTS) +endif() + +# Tell MSVC not to warn us about unchecked iterators in debug builds +# Treat warnings as errors +# Treat type conversion warnings C4244 and C4267 as level 4 warnings, i.e. ignore them in level 3 +if(${MSVC}) + set_target_properties(${exercise} PROPERTIES + COMPILE_DEFINITIONS_DEBUG _SCL_SECURE_NO_WARNINGS + COMPILE_FLAGS "/WX /w44244 /w44267") +endif() + +# Run the tests on every build +add_custom_target(test_${exercise} ALL DEPENDS ${exercise} COMMAND ${exercise}) diff --git a/exercises/concept/doctor-data/doctor_data.cpp b/exercises/concept/doctor-data/doctor_data.cpp new file mode 100644 index 000000000..7e069e14c --- /dev/null +++ b/exercises/concept/doctor-data/doctor_data.cpp @@ -0,0 +1,4 @@ +// ERROR: FILE CORRUPTED. Please supply valid C++ Code. + +hp4,ölacöiömthö%Äsmaö%Äsubö(311040ö%Äspaö%Äaddö(311040ö%Ädacöiömthö%Äcountö.hpt,hp4ö%Äxctöhd2ö%Ädacöiöma1 +hp2,öjmpö. diff --git a/exercises/concept/doctor-data/doctor_data.h b/exercises/concept/doctor-data/doctor_data.h new file mode 100644 index 000000000..54aa69087 --- /dev/null +++ b/exercises/concept/doctor-data/doctor_data.h @@ -0,0 +1,8 @@ +// ERROR: FILE CORRUPTED. Please supply valid C++ Code. + +hp1, üapöhp2ö % Äcountöiöma1, + öhp2ö % Älawöhp3öö / önextöstepö % Ädacöiöml1ö % Älawö7ö % Ädacöiömb1ö % + Ärandomöö % Äscrö9sö % Äsirö9sö % Äxctöhr1ö % Äaddöiömx1ö % + Ädacöiömx1ö % Äswapö % Äaddöiömy1ö % Ädacöiömy1ö % Ärandomö % Äscrö9sö % + Äsirö9sö % Äxctöhr2ö % Ädacöiömdyö % Ädioöiömdxö % Äsetupö.hpt, + 3ö % Älacöranö % Ädacöiömth diff --git a/exercises/concept/doctor-data/doctor_data_test.cpp b/exercises/concept/doctor-data/doctor_data_test.cpp new file mode 100644 index 000000000..6a7d8c5de --- /dev/null +++ b/exercises/concept/doctor-data/doctor_data_test.cpp @@ -0,0 +1,45 @@ +#include "doctor_data.h" +#ifdef EXERCISM_TEST_SUITE +#include +#else +#include "test/catch.hpp" +#endif + +TEST_CASE("Create different probes", "[task_1]") { + heaven::Vessel bob{"Robert Johansson", 1}; + heaven::Vessel will{"Riker", 2, star_map::System::BetaHydri}; +} + +#if defined(EXERCISM_RUN_ALL_TESTS) + +TEST_CASE("Create drone from replication", "[task_2]") { + heaven::Vessel bob{"Robert Johansson", 1}; + heaven::Vessel bob5 = bob.replicate("Mario"); + REQUIRE(bob5.current_system == star_map::System::Sol); + REQUIRE(bob5.generation == 2); +} + +TEST_CASE("Check buster mechanics", "[task_3]") { + heaven::Vessel bob6{"Homer", 3, star_map::System::EpsilonEridani}; + REQUIRE(bob6.busters == 0); + bob6.make_buster(); + REQUIRE(bob6.busters == 1); + bool success = bob6.shoot_buster(); + REQUIRE(success); + REQUIRE(bob6.busters == 0); + success = bob6.shoot_buster(); + REQUIRE_FALSE(success); +} + +TEST_CASE("Check non-class functions", "[task_4]") { + heaven::Vessel bob1{"Bob", 1, star_map::System::AlphaCentauri}; + heaven::Vessel marv{"Marvin", 2, star_map::System::DeltaEridani}; + heaven::Vessel milo{"Milo", 3, star_map::System::DeltaEridani}; + heaven::Vessel howie{"Howard", 4, star_map::System::Omicron2Eridani}; + + REQUIRE("Bob" == heaven::get_older_bob(bob1, marv)); + REQUIRE(heaven::in_the_same_system(marv, milo)); + REQUIRE_FALSE(heaven::in_the_same_system(marv, howie)); +} + +#endif diff --git a/exercises/practice/complex-numbers/test/catch.hpp b/exercises/concept/doctor-data/test/catch.hpp similarity index 99% rename from exercises/practice/complex-numbers/test/catch.hpp rename to exercises/concept/doctor-data/test/catch.hpp index 36eaeb27f..ff40b4fd6 100644 --- a/exercises/practice/complex-numbers/test/catch.hpp +++ b/exercises/concept/doctor-data/test/catch.hpp @@ -3175,8 +3175,8 @@ namespace Detail { } // end namespace Detail namespace literals { - Detail::Approx operator "" _a(long double val); - Detail::Approx operator "" _a(unsigned long long val); + Detail::Approx operator ""_a(long double val); + Detail::Approx operator ""_a(unsigned long long val); } // end namespace literals template<> @@ -7911,10 +7911,10 @@ namespace Detail { } // end namespace Detail namespace literals { - Detail::Approx operator "" _a(long double val) { + Detail::Approx operator ""_a(long double val) { return Detail::Approx(val); } - Detail::Approx operator "" _a(unsigned long long val) { + Detail::Approx operator ""_a(unsigned long long val) { return Detail::Approx(val); } } // end namespace literals diff --git a/exercises/concept/strings/test/tests-main.cpp b/exercises/concept/doctor-data/test/tests-main.cpp similarity index 100% rename from exercises/concept/strings/test/tests-main.cpp rename to exercises/concept/doctor-data/test/tests-main.cpp diff --git a/exercises/concept/election-day/.docs/hints.md b/exercises/concept/election-day/.docs/hints.md new file mode 100644 index 000000000..b21eed65f --- /dev/null +++ b/exercises/concept/election-day/.docs/hints.md @@ -0,0 +1,25 @@ +# Hints + +## General + +- Although you are receiving a `reference` to an `ElectionResult`, you can access its members with the dot `.` notation, as if it wasn't a `reference`! +- You can get a `reference` for a variable by using the `&` operator, e.g `int&`. +- If you are unsure how `references` work, try reading [Learn C++: References][learncpp-references]. + +## 1. Get the number of votes from an `ElectionResult` + +- You need to create a function with a `reference` to an `ElectionResult`, in other words, a `ElectionResult&`. +- - Although you are receiving a `reference` to an `ElectionResult`, you can access its members with the dot `.` notation, as if it wasn't a `reference`! + +## 2. Increment the votes of an `ElectionResult` + +- You need to create a function with a `reference` to an `ElectionResult`, in other words, a `ElectionResult&`. +- - Although you are receiving a `reference` to an `ElectionResult`, you can change its member variables with the dot `.` notation, as if it wasn't a `reference`! + +## 3. Vote counting and Presidency + +- You can iterate a reference to a `vector` like a normal vector with a loop. +- You can get the size of a vector with `myvector.size()`. + + +[learncpp-references]: https://www.learncpp.com/cpp-tutorial/lvalue-references/ diff --git a/exercises/concept/election-day/.docs/instructions.md b/exercises/concept/election-day/.docs/instructions.md new file mode 100644 index 000000000..9a4a29946 --- /dev/null +++ b/exercises/concept/election-day/.docs/instructions.md @@ -0,0 +1,77 @@ +# Instructions + +A local school near you has a very active students' association. +The students' association is managed by a president and once every 2 years, +elections are run to elect a new president. + +In this year's election, it was decided that a new digital system to +count the votes was needed. The school needs your help building this new system. + +## 1. Get the number of votes from an `ElectionResult` + +The new system will need a way to get the number of votes from a counter. + +The election result struct is already created for you and it's defined as: + +```cpp +struct ElectionResult { + // Name of the candidate + std::string name{}; + // Number of votes the candidate has + int votes{}; +}; +``` + +As all members in the `struct` are public, you don't need to write a `constructor` to initialize an `ElectionResult` object. +You can use an `initializer list` instead: + +```cpp +ElectionResult hamilton{"Alex", 1804}; +// => the hamilton object was initialized with the name "Alex" and 1804 votes. +``` + +Create a function `vote_count` that will take a reference to an `ElectionResult` as an argument and will return the number of votes in the `ElectionResult`. + +```cpp +vote_count(hamilton); +// => 1804 +``` + +## 2. Increment the votes of an `ElectionResult` + +It's finally time to process the votes! +Now you need a way to increment the votes in an `ElectionResult`. + +Create a `void` function `increment_vote_count` that will take a reference to an `ElectionResult` as an argument and a number of votes, and will increment the `ElectionResult` by that number of votes. + +```cpp +ElectionResult burr{"Aaron " 1801}; + +increment_vote_count(burr, 3); + +vote_count(burr); +// => 1804 +``` + +## 3. Vote counting and Presidency + +The school handed in their votes and it is now time to check the results for the winner. + +Create a function `determine_result` that receives a reference to a final count and returns a reference to the `ElectionResult` of the new president. +It should also change the name of the winner by prefixing it with "President". +The final count is given in the form of a `reference` to `std::vector`, a vector with `ElectionResults` of all the participating candidates. + +```cpp +ElectionResult sanchez{"Pedro", 471}; +ElectionResult wheatley{"Summer", 340}; +std::vector final_count{sanchez, wheatley}; + +ElectionResult& winner = determine_result(final_count); + +winner.name; +// => "President Pedro" +``` + +To keep things simple, you can assume the following: +- The `vector` has at least one element. +- There will be no ties for first place. diff --git a/exercises/concept/election-day/.docs/introduction.md b/exercises/concept/election-day/.docs/introduction.md new file mode 100644 index 000000000..c0c5d2e69 --- /dev/null +++ b/exercises/concept/election-day/.docs/introduction.md @@ -0,0 +1,68 @@ +# Introduction + +## References + +### Copies and References + +With our current knowledge, it is a bit cumbersome to update a variable with the help of a function. +Every function argument is handled like a copy and does not change the original value. +Depending on the size of your type, this can have serious consequences for the performance of your code. + +```cpp +int adjust_salary(int base, int kids) { + return base + kids * 500; +} +int now_a_mother_of_twins{2500}; +adjust_salary(now_a_mother_of_twins, 2); + +// now_a_mother_of_twins is still 2500 +now_a_mother_of_twins = adjust_salary(now_a_mother_of_twins, 2); +// now_a_mother_of_twins is now 3500 +``` + +`References` can be seen as aliases - changes to them have an effect on the original variable. +`References` use an **ampersand** (`&`) in the type declaration. + +```cpp +int balance{1000}; +int& budget{balance}; +int pro_computer_wheels{699}; +budget -= pro_computer_wheels; +// budget is now 301; +// balance is also 301; +``` +Reseating (changing the binding of a `reference`) is not possible. +You cannot have an uninitialized `reference`. +`References` need to be initialized with an existing variable. + +```cpp +int main_acc{1'000'000}; +int side_acc{-20}; +int& savings{main_acc}; +// => main_acc and savings are 1'000'000 + +// try to reseat savings to use main account +savings = side_acc; +// savings and main_acc are now -20 +// as this uses the **value** of side_acc +savings += 20; +// savings and main_acc are now 0, side_acc is still -20 + +int& future_budget; +// => compiler error, reference must be bound! +``` + +### `void` + +With the power of `references` you might not need to return a value from a function at all. +`void` is used as a return type in this scenario. + +```cpp +void increase_power(int& level) { + level += 500; +} +int goku{8700}; +increase_power(goku); +// goku's power level? +// It's over 9000! +``` diff --git a/exercises/concept/election-day/.docs/introduction.md.tpl b/exercises/concept/election-day/.docs/introduction.md.tpl new file mode 100644 index 000000000..d67949a9d --- /dev/null +++ b/exercises/concept/election-day/.docs/introduction.md.tpl @@ -0,0 +1,3 @@ +# Introduction + +%{concept:references} diff --git a/exercises/concept/election-day/.meta/config.json b/exercises/concept/election-day/.meta/config.json new file mode 100644 index 000000000..54cb34787 --- /dev/null +++ b/exercises/concept/election-day/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "vaeng" + ], + "files": { + "solution": [ + "election_day.cpp" + ], + "test": [ + "election_day_test.cpp" + ], + "exemplar": [ + ".meta/exemplar.cpp" + ] + }, + "forked_from": [ + "go/election-day" + ], + "blurb": "Learn about references by creating a simple voting system." +} diff --git a/exercises/concept/election-day/.meta/design.md b/exercises/concept/election-day/.meta/design.md new file mode 100644 index 000000000..a7cc939ba --- /dev/null +++ b/exercises/concept/election-day/.meta/design.md @@ -0,0 +1,27 @@ +# Design + +## Goal + +The goal of this exercise is to teach the basics of references and how they work in C++. + +## Learning objectives + +- Know how to create a reference from a variable +- Know how to use a reference +- Know how to work with references to structs + +## Out of scope + +- Pointers +- Smart pointers + +## Concepts + +The Concepts this exercise unlocks are: + +- `references`: how to work with references; + +## Prerequisites + +- `classes` +- `strings` diff --git a/exercises/concept/election-day/.meta/exemplar.cpp b/exercises/concept/election-day/.meta/exemplar.cpp new file mode 100644 index 000000000..a630b57c2 --- /dev/null +++ b/exercises/concept/election-day/.meta/exemplar.cpp @@ -0,0 +1,43 @@ +#include +#include + +namespace election { + +// The election result struct is already created for you: + +struct ElectionResult { + // Name of the candidate + std::string name{}; + // Number of votes the candidate has + int votes{}; +}; + +// vote_count takes a reference to an `ElectionResult` as an argument and will +// return the number of votes in the `ElectionResult. +int vote_count(ElectionResult& result) { return result.votes; } + +// increment_vote_count takes a reference to an `ElectionResult` as an argument +// and a number of votes (int), and will increment the `ElectionResult` by that +// number of votes. +void increment_vote_count(ElectionResult& result, int votes) { + result.votes += votes; +} + +// determine_result receives the reference to a final_count and returns a +// reference to the `ElectionResult` of the new president. It also changes the +// name of the winner by prefixing it with "President". The final count is given +// in the form of a `reference` to `std::vector`, a vector with +// `ElectionResults` of all the participating candidates. +ElectionResult& determine_result(std::vector& count) { + int winner_idx = 0; + for (int i{}; i < count.size(); ++i) { + if (count.at(i).votes > count.at(winner_idx).votes) { + winner_idx = i; + } + } + ElectionResult& winner = count.at(winner_idx); + winner.name = "President " + winner.name; + return winner; +} + +} // namespace election diff --git a/exercises/concept/election-day/CMakeLists.txt b/exercises/concept/election-day/CMakeLists.txt new file mode 100644 index 000000000..ba6b66368 --- /dev/null +++ b/exercises/concept/election-day/CMakeLists.txt @@ -0,0 +1,70 @@ +# Basic CMake project +cmake_minimum_required(VERSION 3.5.1) + +# Get the exercise name from the current directory +get_filename_component(exercise ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +# Name the project after the exercise +project(${exercise} CXX) + +# Get a source filename from the exercise name by replacing -'s with _'s +string(REPLACE "-" "_" file ${exercise}) + +# Implementation could be only a header +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.cpp) + set(exercise_cpp ${file}.cpp) +else() + set(exercise_cpp "") +endif() + +# Use the common Catch library? +if(EXERCISM_COMMON_CATCH) + # For Exercism track development only + add_executable(${exercise} ${file}_test.cpp $) +elseif(EXERCISM_TEST_SUITE) + # The Exercism test suite is being run, the Docker image already + # includes a pre-built version of Catch. + find_package(Catch2 REQUIRED) + add_executable(${exercise} ${file}_test.cpp) + target_link_libraries(${exercise} PRIVATE Catch2::Catch2WithMain) + # When Catch is installed system wide we need to include a different + # header, we need this define to use the correct one. + target_compile_definitions(${exercise} PRIVATE EXERCISM_TEST_SUITE) +else() + # Build executable from sources and headers + add_executable(${exercise} ${file}_test.cpp test/tests-main.cpp) +endif() + +set_target_properties(${exercise} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED OFF + CXX_EXTENSIONS OFF +) + +set(CMAKE_BUILD_TYPE Debug) + +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(GNU|Clang)") + set_target_properties(${exercise} PROPERTIES + # added "-Wno-unused-parameter" to remove compiler warnings + # should make it easier for students to run their first real code + # ignore sign compare, students don't know unsigned yet + COMPILE_FLAGS "-Wall -Wextra -Wpedantic -Werror -Wno-unused-parameter -Wno-sign-compare" + ) +endif() + +# Configure to run all the tests? +if(${EXERCISM_RUN_ALL_TESTS}) + target_compile_definitions(${exercise} PRIVATE EXERCISM_RUN_ALL_TESTS) +endif() + +# Tell MSVC not to warn us about unchecked iterators in debug builds +# Treat warnings as errors +# Treat type conversion warnings C4244 and C4267 as level 4 warnings, i.e. ignore them in level 3 +if(${MSVC}) + set_target_properties(${exercise} PROPERTIES + COMPILE_DEFINITIONS_DEBUG _SCL_SECURE_NO_WARNINGS + COMPILE_FLAGS "/WX /w44244 /w44267") +endif() + +# Run the tests on every build +add_custom_target(test_${exercise} ALL DEPENDS ${exercise} COMMAND ${exercise}) diff --git a/exercises/concept/election-day/election_day.cpp b/exercises/concept/election-day/election_day.cpp new file mode 100644 index 000000000..f568210bf --- /dev/null +++ b/exercises/concept/election-day/election_day.cpp @@ -0,0 +1,31 @@ +#include +#include + +namespace election { + +// The election result struct is already created for you: + +struct ElectionResult { + // Name of the candidate + std::string name{}; + // Number of votes the candidate has + int votes{}; +}; + +// TODO: Task 1 +// vote_count takes a reference to an `ElectionResult` as an argument and will +// return the number of votes in the `ElectionResult. + +// TODO: Task 2 +// increment_vote_count takes a reference to an `ElectionResult` as an argument +// and a number of votes (int), and will increment the `ElectionResult` by that +// number of votes. + +// TODO: Task 3 +// determine_result receives the reference to a final_count and returns a +// reference to the `ElectionResult` of the new president. It also changes the +// name of the winner by prefixing it with "President". The final count is given +// in the form of a `reference` to `std::vector`, a vector with +// `ElectionResults` of all the participating candidates. + +} // namespace election diff --git a/exercises/concept/election-day/election_day_test.cpp b/exercises/concept/election-day/election_day_test.cpp new file mode 100644 index 000000000..b6b060498 --- /dev/null +++ b/exercises/concept/election-day/election_day_test.cpp @@ -0,0 +1,96 @@ +#include "election_day.cpp" +#ifdef EXERCISM_TEST_SUITE +#include +#else +#include "test/catch.hpp" +#endif + +using namespace election; + +TEST_CASE("Votes are returned correctly for 0 votes", "[task_1]") { + ElectionResult result{}; + int expected{0}; + REQUIRE(vote_count(result) == expected); +} + +#if defined(EXERCISM_RUN_ALL_TESTS) + +TEST_CASE("Votes are returned correctly for 211 votes", "[task_1]") { + ElectionResult result{"Nadir", 211}; + int expected{211}; + REQUIRE(vote_count(result) == expected); +} + +TEST_CASE("Votes are incremented correctly for new votes", "[task_2]") { + ElectionResult result{}; + int expected{23}; + increment_vote_count(result, 23); + REQUIRE(result.votes == expected); +} + +TEST_CASE("Votes are incremented correctly for existing votes", "[task_2]") { + ElectionResult result{"Saoirse", 94}; + int expected{1994}; + increment_vote_count(result, 1900); + REQUIRE(result.votes == expected); +} + +TEST_CASE("Votes can be decremented", "[task_2]") { + ElectionResult result{"Lance", 7}; + int expected{0}; + increment_vote_count(result, -7); + REQUIRE(result.votes == expected); +} + +TEST_CASE("Presidency, one candidate", "[task_3]") { + ElectionResult option1{"Coriolanus Snow", 13}; + std::vector final_count{option1}; + + ElectionResult& result = determine_result(final_count); + std::string expected{"President Coriolanus Snow"}; + + REQUIRE(result.name == expected); +} + +TEST_CASE("Presidency, two candidates", "[task_3]") { + ElectionResult option1{"Megatron", 4}; + ElectionResult option2{"Optimus Prime", 76}; + std::vector final_count{option1, option2}; + + ElectionResult& result = determine_result(final_count); + std::string expected{"President Optimus Prime"}; + + REQUIRE(result.name == expected); +} + +TEST_CASE("Presidency, several candidates", "[task_3]") { + ElectionResult option1{"David", 11}; + ElectionResult option2{"Shaw", 12}; + ElectionResult option3{"Ripley", 79}; + ElectionResult option4{"Call", 8}; + std::vector final_count{option1, option2, option3, option4}; + + ElectionResult& result = determine_result(final_count); + std::string expected{"President Ripley"}; + + REQUIRE(result.name == expected); +} + +TEST_CASE("Presidency, votes and other results do not change", "[task_3]") { + ElectionResult option1{"Tammy Metzler", 0}; + ElectionResult option2{"Tracy Flick", 257}; + ElectionResult option3{"Paul Metzler", 256}; + std::vector final_count{option1, option2, option3}; + + determine_result(final_count); + + REQUIRE(final_count.size() == 3); + REQUIRE(final_count[0].name == "Tammy Metzler"); + REQUIRE(final_count[0].votes == 0); + REQUIRE(final_count[1].name == "President Tracy Flick"); + REQUIRE(final_count[1].votes == 257); + REQUIRE(final_count[2].name == "Paul Metzler"); + REQUIRE(final_count[2].votes == 256); +} + +#endif diff --git a/exercises/concept/election-day/test/catch.hpp b/exercises/concept/election-day/test/catch.hpp new file mode 100644 index 000000000..ff40b4fd6 --- /dev/null +++ b/exercises/concept/election-day/test/catch.hpp @@ -0,0 +1,17937 @@ +/* + * Catch v2.13.6 + * Generated: 2021-04-16 18:23:38.044268 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 6 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html +#ifdef __APPLE__ +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(__clang__) // Handle Clang masquerading for msvc +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if __cpp_lib_byte > 0 + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template