From 41111dba30b30bf66aad7fb3bf09263bf6045e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Gil=20de=20G=C3=B3mez=20P=C3=A9rez?= Date: Wed, 12 Aug 2026 13:47:50 +0300 Subject: [PATCH] Update: Using C++26 with contracts enabled --- CMakeLists.txt | 19 ++++++++++ CMakePresets.json | 20 +++++++++-- README.md | 68 ++++++++++++++++++++++++++--------- cmake/CourseOptions.cmake | 15 +++++++- cmake/toolchains/gcc-16.cmake | 25 +++++++++++-- support/CMakeLists.txt | 2 +- 6 files changed, 125 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 20055c8..b21a925 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,22 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16) + message( + FATAL_ERROR + "GCC 16+ is required (found ${CMAKE_CXX_COMPILER_VERSION}). " + "Use the 'default' CMake preset or cmake/toolchains/gcc-16.cmake." + ) + endif() +else() + message( + FATAL_ERROR + "This project requires GCC 16+. Found compiler: ${CMAKE_CXX_COMPILER_ID}. " + "Select the GCC 16 toolchain / default CMake preset in CLion." + ) +endif() + option(COURSE_BUILD_TESTS "Build Google Test exercise targets" ON) option(COURSE_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" OFF) option(COURSE_ENABLE_CONTRACTS "Enable C++26 contracts on GCC 16+" ON) @@ -37,3 +53,6 @@ if(COURSE_BUILD_TESTS) endif() add_subdirectory(modules) + +include(Cppcheck) +course_add_cppcheck_targets() diff --git a/CMakePresets.json b/CMakePresets.json index 5165e93..c88a25a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -10,11 +10,21 @@ "name": "default", "displayName": "GCC 16 + C++26", "generator": "Unix Makefiles", - "binaryDir": "${sourceDir}/build", + "binaryDir": "${sourceDir}/cmake-build-debug-gcc-16-cpp26", + "toolchainFile": "${sourceDir}/cmake/toolchains/gcc-16.cmake", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_C_COMPILER": "/opt/homebrew/bin/gcc-16", - "CMAKE_CXX_COMPILER": "/opt/homebrew/bin/g++-16", + "COURSE_ENABLE_CONTRACTS": "ON" + } + }, + { + "name": "release", + "displayName": "GCC 16 + C++26 (Release)", + "generator": "Unix Makefiles", + "binaryDir": "${sourceDir}/cmake-build-release-gcc-16-cpp26", + "toolchainFile": "${sourceDir}/cmake/toolchains/gcc-16.cmake", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", "COURSE_ENABLE_CONTRACTS": "ON" } } @@ -23,6 +33,10 @@ { "name": "default", "configurePreset": "default" + }, + { + "name": "release", + "configurePreset": "release" } ], "testPresets": [ diff --git a/README.md b/README.md index 7b0aa72..c22004a 100644 --- a/README.md +++ b/README.md @@ -17,37 +17,40 @@ brew install gcc@16 ## Quick Start (CLion) -1. Open this folder in CLion (`File → Open…`). -2. Use the **`default`** CMake profile (`GCC 16 + C++26` from `CMakePresets.json`). -3. Set the toolchain to **GCC 16** (`/opt/homebrew/bin/g++-16`). -4. **Disable Clang-based analysis** (this project targets GCC only): - - **Settings → Languages & Frameworks → C/C++ → Clangd** → disable, **or** - - **Settings → Editor → Inspections → C/C++ → General → Clang-Tidy** → disable -5. Reload CMake, pick an exercise target (e.g. `02_control_flow_loops`), implement TODOs, run tests. +1. Open this folder in CLion. +2. **Settings → Build, Execution, Deployment → CMake** +3. Enable profile **`default`** (display name **GCC 16 + C++26** from `CMakePresets.json`). +4. Disable old profiles that use Apple Clang (e.g. custom profiles without the preset toolchain). +5. Click **Reload CMake Project**. + +If configure fails with *"This project requires GCC 16+"*, CLion is still using Apple Clang. Enable the **`default`** preset or set the toolchain C++ compiler to `g++-16`. + +If configure fails with *"generator does not match"*, delete the build folder (`cmake-build-debug-gcc-16-cpp26`) and reload CMake. + +6. Pick an exercise target (e.g. `02_control_flow_loops`) and run tests. ## Quick Start (Terminal) ```bash cmake --preset default -cmake --build build -ctest --test-dir build --output-on-failure +cmake --build cmake-build-debug-gcc-16-cpp26 +ctest --test-dir cmake-build-debug-gcc-16-cpp26 --output-on-failure ``` Run a single exercise: ```bash -cmake --build build --target 02_control_flow_loops -./build/modules/02_control_flow/02_control_flow_loops +cmake --build cmake-build-debug-gcc-16-cpp26 --target 02_control_flow_loops +./cmake-build-debug-gcc-16-cpp26/modules/02_control_flow/02_control_flow_loops ``` Manual configure (without presets): ```bash -cmake -S . -B build \ - -DCMAKE_CXX_COMPILER=/opt/homebrew/bin/g++-16 \ - -DCMAKE_C_COMPILER=/opt/homebrew/bin/gcc-16 \ +cmake -S . -B cmake-build-debug-gcc-16-cpp26 \ + -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/gcc-16.cmake \ -DCOURSE_ENABLE_CONTRACTS=ON -cmake --build build +cmake --build cmake-build-debug-gcc-16-cpp26 ``` ## Project Structure @@ -143,10 +146,43 @@ auto factorial(const int n) -> long long - Use the **GCC 16** toolchain, not Apple Clang. - Disable **Clangd** and **Clang-Tidy** inspections — they use the Clang frontend and do not support GCC C++26 contracts. -- Use CLion’s built-in **GCC** parser/inspections, or rely on compiler errors from the build. +- **Cppcheck plugin:** disable inline analysis for this project (see [Cppcheck](#cppcheck) below). - Use **Run | Run…** and filter by module prefix (e.g. `02_control_flow`). - Enable **Google Test** integration to see individual tests in the tree view. +## Cppcheck + +Cppcheck **does not understand GCC C++26 contracts** (`pre(...)`) or GCC 16 libstdc++ when the CLion plugin analyzes headers in isolation. That produces false IDE errors such as: + +- `Include file: not found` +- `failed to evaluate #elif condition` in libstdc++ headers +- parse errors on `pre(...)` + +### Recommended: disable the plugin, use the CMake target + +1. **Settings → Editor → Inspections → C/C++ → Cppcheck** → **disable** +2. Configure and build once: + ```bash + cmake --preset default + cmake --build build + ``` +3. Run Cppcheck from CLion or terminal: + ```bash + cmake --build build --target cppcheck_modules + ``` + +This uses `compile_commands.json` from your **GCC 16** build and suppresses known false positives (`cppcheck/suppressions.txt`). + +### If you keep the plugin anyway + +**Settings → Cppcheck configuration → Options:** + +``` +--language=c++ --std=c++26 --max-configs=1 --suppress=missingIncludeSystem --suppress=syntaxError +``` + +Expect remaining false positives on contract syntax. The plugin is not a good fit for this codebase. + ## Notes on C++26 All exercises build with **C++26**. Module READMEs describe the language feature each exercise teaches (C++11, C++17, etc.), but the toolchain baseline is C++26 throughout. diff --git a/cmake/CourseOptions.cmake b/cmake/CourseOptions.cmake index b13be6e..b1bff34 100644 --- a/cmake/CourseOptions.cmake +++ b/cmake/CourseOptions.cmake @@ -21,7 +21,20 @@ function(course_apply_warnings target_name) endfunction() function(course_set_cxx_standard target_name standard) - target_compile_features(${target_name} PRIVATE cxx_std_${standard}) + # Use explicit -std flags so older CLion/CMake builds still get C++26. + if(standard GREATER_EQUAL 26) + target_compile_options(${target_name} PRIVATE -std=c++26) + elseif(standard GREATER_EQUAL 23) + target_compile_options(${target_name} PRIVATE -std=c++23) + elseif(standard GREATER_EQUAL 20) + target_compile_options(${target_name} PRIVATE -std=c++20) + elseif(standard GREATER_EQUAL 17) + target_compile_options(${target_name} PRIVATE -std=c++17) + elseif(standard GREATER_EQUAL 14) + target_compile_options(${target_name} PRIVATE -std=c++14) + else() + target_compile_options(${target_name} PRIVATE -std=c++11) + endif() endfunction() function(course_enable_contracts target_name) diff --git a/cmake/toolchains/gcc-16.cmake b/cmake/toolchains/gcc-16.cmake index 2508ee5..2acd5e7 100644 --- a/cmake/toolchains/gcc-16.cmake +++ b/cmake/toolchains/gcc-16.cmake @@ -1,3 +1,22 @@ -# Use GCC 16 for C++26 contracts support on macOS (Homebrew). -set(CMAKE_C_COMPILER "/opt/homebrew/bin/gcc-16" CACHE FILEPATH "C compiler") -set(CMAKE_CXX_COMPILER "/opt/homebrew/bin/g++-16" CACHE FILEPATH "C++ compiler") +# GCC 16 toolchain for C++26 and contracts. +find_program( + COURSE_GCC16_C + NAMES gcc-16 + HINTS /opt/homebrew/bin /usr/local/bin /opt/local/bin +) +find_program( + COURSE_GCC16_CXX + NAMES g++-16 + HINTS /opt/homebrew/bin /usr/local/bin /opt/local/bin +) + +if(NOT COURSE_GCC16_CXX) + message( + FATAL_ERROR + "g++-16 not found. Install GCC 16, e.g. 'brew install gcc@16', " + "then ensure g++-16 is on your PATH." + ) +endif() + +set(CMAKE_C_COMPILER "${COURSE_GCC16_C}" CACHE FILEPATH "C compiler" FORCE) +set(CMAKE_CXX_COMPILER "${COURSE_GCC16_CXX}" CACHE FILEPATH "C++ compiler" FORCE) diff --git a/support/CMakeLists.txt b/support/CMakeLists.txt index 8fa3cfe..16c660e 100644 --- a/support/CMakeLists.txt +++ b/support/CMakeLists.txt @@ -1,5 +1,5 @@ add_library(course_contract_support STATIC contract_handler.cpp) -target_compile_features(course_contract_support PUBLIC cxx_std_26) +target_compile_options(course_contract_support PUBLIC -std=c++26) set_target_properties(course_contract_support PROPERTIES POSITION_INDEPENDENT_CODE ON) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND COURSE_ENABLE_CONTRACTS)