diff --git a/.clang-tidy b/.clang-tidy index 1d51260f..8dbf66c5 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -30,16 +30,19 @@ -readability-uppercase-literal-suffix, -readability-braces-around-statements, -bugprone-easily-swappable-parameters, + -bugprone-unchecked-optional-access, -cppcoreguidelines-special-member-functions, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-non-private-member-variables-in-classes, -cppcoreguidelines-prefer-member-initializer, + -cppcoreguidelines-missing-std-forward, -cppcoreguidelines-narrowing-conversions, -cppcoreguidelines-avoid-non-const-global-variables, -misc-non-private-member-variables-in-classes, -misc-no-recursion, -misc-const-correctness, - -misc-use-anonymous-namespace + -misc-use-anonymous-namespace, + -misc-include-cleaner # Also check the header files diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 00000000..a59c2b9f --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,15 @@ +name: "CodeQL config" + +queries: + - uses: security-and-quality + +query-filters: + - exclude: + id: cpp/include-non-header + - exclude: + id: cpp/undisciplined-multiple-inheritance + - exclude: + id: cpp/unused-static-variable + - exclude: + id: cpp/missing-return + \ No newline at end of file diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index a89c06d4..600d1a16 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -6,8 +6,6 @@ jobs: core-guidelines: name: core-guidelines-check runs-on: windows-2022 - # Disabled because of an internal compiler error in reference_lines.cpp - if: false defaults: run: @@ -37,24 +35,23 @@ jobs: ############################################################################################################# clang-analyzers: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: tool: [ - { name: iwyu, cmake-flag: CMAKE_CXX_INCLUDE_WHAT_YOU_USE="iwyu;-Xiwyu;--mapping_file=../.iwyu-mappings" }, { name: clang-tidy, cmake-flag: CMAKE_CXX_CLANG_TIDY="clang-tidy" }, { name: cppcheck, cmake-flag: CMAKE_CXX_CPPCHECK="cppcheck;--version;--verbose;--report-progress;--enable=all;--error-exitcode=1;--std=c++20;--suppressions-list=../.cppcheck-supressions" } ] include: - - pkgs: clang-15 clang-tools-15 clang-tidy-15 iwyu cppcheck - cxx: clang++-15 + - pkgs: clang-18 clang-tools-18 clang-tidy-18 iwyu cppcheck + cxx: clang++-18 defaults: run: working-directory: ${{ github.workspace }}/build - name: ${{ matrix.tool.name }} + name: ${{ matrix.tool.name }} (${{ matrix.cxx }}) steps: - name: checkout-repo @@ -79,15 +76,15 @@ jobs: ############################################################################################################# codeql: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: include: - - pkgs: clang-15 - cxx: clang++-15 + - pkgs: clang-18 + cxx: clang++-18 - name: codeql + name: codeql (${{ matrix.cxx }}) defaults: run: @@ -104,7 +101,7 @@ jobs: uses: github/codeql-action/init@v3 with: languages: cpp - queries: security-and-quality + config-file: ${{ github.workspace }}/.github/codeql/codeql-config.yml - name: setup-build env: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5f09b76b..f8040fd4 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -6,7 +6,7 @@ on: jobs: update-docs: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 defaults: run: working-directory: ${{ github.workspace }} diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 586036ea..9333bd8b 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -4,17 +4,23 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 + container: + image: ubuntu:24.04 strategy: fail-fast: false matrix: + common-pkgs: [ git cmake ] build-type: [ Release, RelWithDebInfo ] compiler: [ { cxx: g++-11, pkgs: g++-11 }, { cxx: g++-12, pkgs: g++-12 }, { cxx: g++-13, pkgs: g++-13 }, - { cxx: clang++-14, pkgs: clang-14 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 }, + { cxx: g++-14, pkgs: g++-14 }, { cxx: clang++-15, pkgs: clang-15 }, + { cxx: clang++-16, pkgs: clang-16 }, + { cxx: clang++-17, pkgs: clang-17 }, + { cxx: clang++-18, pkgs: clang-18 }, ] defaults: @@ -28,17 +34,17 @@ jobs: uses: actions/checkout@v4 - name: setup-compiler - run: sudo apt update && sudo apt install --allow-downgrades -y ${{ matrix.compiler.pkgs }} + run: apt update && apt install --allow-downgrades -y ${{ matrix.common-pkgs }} ${{ matrix.compiler.pkgs }} - name: setup-catch env: CXX: ${{ matrix.compiler.cxx }} - run: sudo bash ../tools/install_catch.sh + run: bash ../tools/install_catch.sh - name: setup-build env: CXX: ${{ matrix.compiler.cxx }} - run: cmake .. -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -DGAPP_USE_WERROR=ON -DGAPP_USE_LTO=ON + run: cmake .. -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -DGAPP_USE_WERROR=ON -DGAPP_USE_LTO=ON -DGAPP_CXX_FLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS" - name: build run: cmake --build . --parallel 8 @@ -47,4 +53,4 @@ jobs: run: ctest --output-on-failure --schedule-random - name: install - run: sudo cmake --install . + run: cmake --install . diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index d3f21f2b..d7793bbe 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,27 +4,45 @@ on: [push, pull_request] jobs: build: - runs-on: macos-13 + runs-on: macos-14 strategy: fail-fast: false matrix: build-type: [ Release, RelWithDebInfo ] compiler: [ - { cxx: g++-11, pkgs: gcc@11, extra-flags: "-undefined dynamic_lookup" }, - { cxx: g++-12, pkgs: gcc@12, extra-flags: "-undefined dynamic_lookup" }, - { cxx: g++-13, pkgs: gcc@13, extra-flags: "-undefined dynamic_lookup -Wno-array-bounds -Wno-stringop-overflow -Wno-stringop-overread" }, + { cxx: g++-11, pkgs: gcc@11, extra-flags: "-undefined dynamic_lookup -Wno-psabi" }, + { cxx: g++-12, pkgs: gcc@12, extra-flags: "-undefined dynamic_lookup -Wno-psabi" }, + { cxx: g++-13, pkgs: gcc@13, extra-flags: "-undefined dynamic_lookup -Wno-psabi" }, + { cxx: g++-14, pkgs: gcc@14, extra-flags: "-undefined dynamic_lookup -Wno-psabi" }, { cxx: $(brew --prefix llvm@14)/bin/clang++, pkgs: llvm@14 gcc@11, - extra-flags: "-femulated-tls -stdlib=libstdc++ -stdlib++-isystem $(brew --prefix gcc@11)/include/c++/11 -cxx-isystem $(brew --prefix gcc@11)/include/c++/11/x86_64-apple-darwin22", + extra-flags: "-femulated-tls -stdlib=libstdc++ -stdlib++-isystem $(brew --prefix gcc@11)/include/c++/11 -cxx-isystem $(brew --prefix gcc@11)/include/c++/11/aarch64-apple-darwin23 -g -fno-omit-frame-pointer -fsanitize=undefined", linker-flags: "-L$(brew --prefix gcc@11)/lib/gcc/11" }, { cxx: $(brew --prefix llvm@15)/bin/clang++, - pkgs: llvm@15 gcc@12, - extra-flags: "-femulated-tls -stdlib=libstdc++ -stdlib++-isystem $(brew --prefix gcc@12)/include/c++/12 -cxx-isystem $(brew --prefix gcc@12)/include/c++/12/x86_64-apple-darwin22", - linker-flags: "-L$(brew --prefix gcc@12)/lib/gcc/12" - } + pkgs: llvm@15 gcc@13, + extra-flags: "-femulated-tls -stdlib=libstdc++ -stdlib++-isystem $(brew --prefix gcc@13)/include/c++/13 -cxx-isystem $(brew --prefix gcc@13)/include/c++/13/aarch64-apple-darwin23 -g -fno-omit-frame-pointer -fsanitize=address", + linker-flags: "-L$(brew --prefix gcc@13)/lib/gcc/13" + }, + # { + # cxx: $(brew --prefix llvm@16)/bin/clang++, + # pkgs: llvm@16 gcc@13, + # extra-flags: "-femulated-tls -stdlib=libstdc++ -stdlib++-isystem $(brew --prefix gcc@13)/include/c++/13 -cxx-isystem $(brew --prefix gcc@13)/include/c++/13/aarch64-apple-darwin23", + # linker-flags: "-L$(brew --prefix gcc@13)/lib/gcc/13" + # }, + # { + # cxx: $(brew --prefix llvm@17)/bin/clang++, + # pkgs: llvm@17 gcc@14, + # extra-flags: "-femulated-tls -stdlib=libstdc++ -stdlib++-isystem $(brew --prefix gcc@14)/include/c++/14 -cxx-isystem $(brew --prefix gcc@14)/include/c++/14/aarch64-apple-darwin23", + # linker-flags: "-L$(brew --prefix gcc@14)/lib/gcc/14" + # }, + # { + # cxx: $(brew --prefix llvm@18)/bin/clang++, + # pkgs: llvm@18 gcc@14, + # extra-flags: "-femulated-tls" + # } ] defaults: @@ -40,9 +58,6 @@ jobs: - name: setup-compiler run: brew update && brew install ${{ matrix.compiler.pkgs }} - - name: setup-xcode - run: sudo xcode-select -switch /Applications/Xcode_15.1.app - - name: setup-catch run: sudo bash ../tools/install_catch.sh -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} -DCMAKE_CXX_FLAGS="${{ matrix.compiler.extra-flags }}" -DCMAKE_EXE_LINKER_FLAGS="${{ matrix.compiler.linker-flags }}" @@ -52,6 +67,9 @@ jobs: - name: build run: cmake --build . --parallel 8 + - name: run-integration-tests + run: ./integration_tests + - name: run-tests run: ctest --output-on-failure --schedule-random diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 81321b9d..247069a4 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -4,14 +4,14 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: sanitizer: [ address, undefined, thread ] include: - - cxx: clang++-15 - pkgs: clang-15 llvm-15 + - cxx: clang++-18 + pkgs: clang-18 llvm-18 env: ASAN_OPTIONS: check_initialization_order=1:strict_init_order=1:detect_stack_use_after_return=1:detect_leaks=1 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 71bb122d..76231d0b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -11,7 +11,10 @@ jobs: build-type: [ Release, RelWithDebInfo ] platform: [ x64, Win32 ] generator: [ "Visual Studio 17 2022" ] - compiler: [ v143, ClangCL ] + compiler: [ + { name: msvc, toolset: v143 }, + { name: clang, toolset: ClangCL } + ] build-shared: [ "ON", "OFF" ] exclude: - platform: Win32 @@ -23,7 +26,7 @@ jobs: run: working-directory: ${{ github.workspace }}/build - name: ${{ matrix.compiler }}-${{ matrix.platform }}, ${{ matrix.build-type }}, Shared=${{ matrix.build-shared }} + name: ${{ matrix.compiler.name }}-${{ matrix.platform }}, ${{ matrix.build-type }}, Shared=${{ matrix.build-shared }} steps: @@ -36,14 +39,14 @@ jobs: - name: setup-catch env: CMAKE_GENERATOR: ${{ matrix.generator }} - CMAKE_GENERATOR_TOOLSET: ${{ matrix.compiler }} + CMAKE_GENERATOR_TOOLSET: ${{ matrix.compiler.toolset }} CMAKE_GENERATOR_PLATFORM: ${{ matrix.platform }} run: bash ../tools/install_catch.sh - name: setup-build env: CMAKE_GENERATOR: ${{ matrix.generator }} - CMAKE_GENERATOR_TOOLSET: ${{ matrix.compiler }} + CMAKE_GENERATOR_TOOLSET: ${{ matrix.compiler.toolset }} CMAKE_GENERATOR_PLATFORM: ${{ matrix.platform }} run: cmake .. -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -DBUILD_SHARED_LIBS=${{ matrix.build-shared }} -DGAPP_USE_WERROR=ON -DGAPP_USE_LTO=ON diff --git a/.iwyu-mappings b/.iwyu-mappings deleted file mode 100644 index fea62999..00000000 --- a/.iwyu-mappings +++ /dev/null @@ -1,26 +0,0 @@ -# Mapping file for include-what-you-use -# See: https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUMappings.md - -[ - # Map C headers to their C++ counterparts - { include: [ "", public, "", public ] }, - { include: [ "", public, "", public ] }, - { include: [ "", public, "", public ] }, - { include: [ "", public, "", public ] }, - - # iwyu wants to include whenever something allocates memory - { symbol: ["std::bad_alloc", "private", "", "public" ] }, - - # Fix incorrectly suggested private header includes - { include: ["", "private", "", "public" ] }, - { include: ["", "private", "", "public" ] }, - { include: ["", "private", "", "public" ] }, - { symbol: ["std::tuple_element::type", "private", "", "public" ] }, - { symbol: ["__alloc_traits::value_type", "private", "", "public" ] }, - { symbol: ["abs", "private", "", "public" ] }, - { symbol: ["allocator_traits::value_type", "private", "", "public" ] }, - - # Fix unrecognized symbols - { symbol: [ "std::floating_point", "public", "", "public" ] }, - { symbol: [ "std::is_final", "public", "", "public" ] }, -] \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d6e7242f..23f095fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,7 +100,8 @@ else() # GNU style compiler interface # gcc specific options if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set(GAPP_WARN_FLAGS "${GAPP_WARN_FLAGS} -Wlogical-op") + # -Warray-bounds, -Wstringop-overflow, -Wstringop-overread are regular false positives since g++-12 + set(GAPP_WARN_FLAGS "${GAPP_WARN_FLAGS} -Wlogical-op -Wno-array-bounds -Wno-stringop-overflow -Wno-stringop-overread") endif() # clang specific options if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -108,7 +109,7 @@ else() # GNU style compiler interface endif() set(CMAKE_CXX_FLAGS "${GAPP_CXX_FLAGS} ${GAPP_WARN_FLAGS}") - set(CMAKE_CXX_FLAGS_DEBUG "-Og") + set(CMAKE_CXX_FLAGS_DEBUG "-Og -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS") set(CMAKE_CXX_FLAGS_RELEASE "-g -DNDEBUG ${GAPP_OPT_FLAGS}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g ${GAPP_OPT_FLAGS}") endif() diff --git a/CMakeSettings.json b/CMakeSettings.json index 753dee08..e58d6afd 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -103,7 +103,8 @@ "variables": [ { "name": "CMAKE_CXX_CLANG_TIDY", - "value": "clang-tidy" + "value": "clang-tidy", + "type": "STRING" } ] }, diff --git a/README.md b/README.md index 9dc027cc..5d916d23 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ The maximum of sin(x) in [0.0, 3.14] is at x = 1.57079 The following are needed for building and using the library: -- C++20 compiler (gcc 11.0, clang 14.0, msvc 14.30 or later) +- C++20 compiler (gcc 11.0, clang 15.0, msvc 14.30 or later) - CMake 3.21 or later - Catch2 3.3 or later (optional, only needed for the tests) diff --git a/core-guidelines.ruleset b/core-guidelines.ruleset index ebf549bb..14d6c680 100644 --- a/core-guidelines.ruleset +++ b/core-guidelines.ruleset @@ -24,9 +24,11 @@ - + - + + + diff --git a/docs/install-guide.md b/docs/install-guide.md index 16412195..599eabe3 100644 --- a/docs/install-guide.md +++ b/docs/install-guide.md @@ -16,7 +16,7 @@ is needed to build the tests. The full list of requirements are: -- C++20 compiler (gcc 11.0, clang 14.0, msvc 14.30 or later) +- C++20 compiler (gcc 11.0, clang 15.0, msvc 14.30 or later) - CMake 3.21 or later - Catch2 3.3 or later (optional, only needed for the tests) diff --git a/src/algorithm/nd_sort.hpp b/src/algorithm/nd_sort.hpp index a833b5b7..87077d79 100644 --- a/src/algorithm/nd_sort.hpp +++ b/src/algorithm/nd_sort.hpp @@ -56,8 +56,8 @@ namespace gapp::algorithm::dtl constexpr iterator end() const noexcept { return last_; } private: - iterator first_ = {}; - iterator last_ = {}; + iterator first_; + iterator last_; }; diff --git a/src/algorithm/reference_lines.cpp b/src/algorithm/reference_lines.cpp index b5a5194d..fb632407 100644 --- a/src/algorithm/reference_lines.cpp +++ b/src/algorithm/reference_lines.cpp @@ -17,7 +17,7 @@ namespace gapp::algorithm::reflines { - using SimplexMapping = FitnessVector(*)(FitnessVector&&); + using SimplexMapping = void(*)(FitnessVector&); /* * The unit-hypercube -> unit-simplex transformations used for the quasirandom points are based on: @@ -27,35 +27,35 @@ namespace gapp::algorithm::reflines */ /* Transform a point from the n-dimensional unit hypercube to the n-dimensional unit simplex. */ - static inline FitnessVector simplexMappingLog(FitnessVector&& point) + static inline void simplexMappingLog(FitnessVector& point) { GAPP_ASSERT(std::all_of(point.begin(), point.end(), detail::between(0.0, 1.0))); std::transform(point.begin(), point.end(), point.begin(), [](double p) { return -std::log(std::max(p, math::small)); }); const double sum = std::reduce(point.begin(), point.end(), 0.0); std::transform(point.begin(), point.end(), point.begin(), detail::divide_by(sum)); - - return point; } /* Transform a point from the n-dimensional unit hypercube to the (n+1)-dimensional unit simplex. */ - static inline FitnessVector simplexMappingSort(FitnessVector&& point) + static inline void simplexMappingSort(FitnessVector& point) { GAPP_ASSERT(std::all_of(point.begin(), point.end(), detail::between(0.0, 1.0))); point.push_back(1.0); std::sort(point.begin(), point.end()); std::adjacent_difference(point.begin(), point.end(), point.begin()); - - return point; } /* Transform a point from the n-dimensional unit hypercube to the (n+1)-dimensional unit simplex. */ - static inline FitnessVector simplexMappingRoot(FitnessVector&& point) + static inline void simplexMappingRoot(FitnessVector& point) { GAPP_ASSERT(std::all_of(point.begin(), point.end(), detail::between(0.0, 1.0))); - if (point.empty()) return { 1.0 }; + if (point.empty()) + { + point = { 1.0 }; + return; + } point.back() = std::pow(point.back(), 1.0 / point.size()); @@ -66,17 +66,15 @@ namespace gapp::algorithm::reflines point.push_back(1.0); std::adjacent_difference(point.begin(), point.end(), point.begin()); - - return point; } /* Transform a point from the n-dimensional unit hypercube to the (n+1)-dimensional unit simplex. */ - static inline FitnessVector simplexMappingMirror(FitnessVector&& point) + static inline void simplexMappingMirror(FitnessVector& point) { GAPP_ASSERT(std::all_of(point.begin(), point.end(), detail::between(0.0, 1.0))); point.push_back(1.0); - if (point.size() == 1) return point; + if (point.size() == 1) return; for (auto last = std::prev(point.end(), 2); ; ) { @@ -98,8 +96,6 @@ namespace gapp::algorithm::reflines if (!has_lower) --last; } std::adjacent_difference(point.begin(), point.end(), point.begin()); - - return point; } @@ -128,10 +124,9 @@ namespace gapp::algorithm::reflines for (size_t i = 0; i < num_points; i++) { - auto hypercubePoint = qrng(); - FitnessVector simplexPoint = F({ hypercubePoint.begin(), hypercubePoint.end() }); - - points[i] = std::move(simplexPoint); + auto point = qrng(); + std::invoke(F, point); + points[i] = std::move(point); } return points; diff --git a/src/algorithm/soga_selection.cpp b/src/algorithm/soga_selection.cpp index a0dbc3ab..61caa5cc 100644 --- a/src/algorithm/soga_selection.cpp +++ b/src/algorithm/soga_selection.cpp @@ -152,9 +152,9 @@ namespace gapp::selection const double temperature = temperature_(ga.generation_cntr(), ga.max_gen()); // can't capture the iterators by ref or value here - std::transform(fvec.begin(), fvec.end(), fvec.begin(), [&, fmin = *fmin](double f) noexcept + std::transform(fvec.begin(), fvec.end(), fvec.begin(), [&, f_min = *fmin](double f) noexcept { - const double fnorm = f / df - fmin / df; // normalize the fitness values to prevent overflows with std::exp + const double fnorm = f / df - f_min / df; // normalize the fitness values to prevent overflows with std::exp return std::min(math::large, std::exp(fnorm / temperature)); }); diff --git a/src/core/fitness_function.hpp b/src/core/fitness_function.hpp index 0a38ac50..e8ddd00f 100644 --- a/src/core/fitness_function.hpp +++ b/src/core/fitness_function.hpp @@ -34,7 +34,7 @@ namespace gapp * is considered to be dynamic if it may return different fitness vectors for the same * candidate solution over multiple calls to the fitness function. */ - enum class Type { Static = 0, Dynamic = 1 }; + enum class Type : bool { Static = false, Dynamic = true }; /** * Create a fitness function. diff --git a/src/problems/many_objective.cpp b/src/problems/many_objective.cpp index ac031a62..674f3f49 100644 --- a/src/problems/many_objective.cpp +++ b/src/problems/many_objective.cpp @@ -273,7 +273,7 @@ namespace gapp::problems nadir_point_ = FitnessVector(num_obj, 0.0); for (size_t i = 0; i < num_obj; i++) { - nadir_point_[i] = -1.0 / std::pow(std::sqrt(2), num_obj - 1 - i); + nadir_point_[i] = -1.0 / std::pow(std::numbers::sqrt2, num_obj - 1 - i); } nadir_point_[0] = nadir_point_[1]; @@ -298,7 +298,7 @@ namespace gapp::problems nadir_point_ = FitnessVector(num_obj, 0.0); for (size_t i = 0; i < num_obj; i++) { - nadir_point_[i] = -1.0 / std::pow(std::sqrt(2), num_obj - 1 - i); + nadir_point_[i] = -1.0 / std::pow(std::numbers::sqrt2, num_obj - 1 - i); } nadir_point_[0] = nadir_point_[1]; diff --git a/src/utility/cone_tree.cpp b/src/utility/cone_tree.cpp index b55e214b..9ea42473 100644 --- a/src/utility/cone_tree.cpp +++ b/src/utility/cone_tree.cpp @@ -133,7 +133,7 @@ namespace gapp::detail node.radius = findRadius(node_cbegin(node), node_cend(node), node.center); /* Leaf node. */ - if (size_t(node.last - node.first) <= MAX_LEAF_ELEMENTS) + if (node.last - node.first <= MAX_LEAF_ELEMENTS) { node.left = 0; node.right = 0; diff --git a/src/utility/cone_tree.hpp b/src/utility/cone_tree.hpp index e1d7a793..27ed7ddf 100644 --- a/src/utility/cone_tree.hpp +++ b/src/utility/cone_tree.hpp @@ -34,7 +34,7 @@ namespace gapp::detail struct Node { - Point center = {}; + Point center = {}; // NOLINT(*redundant-member-init) double radius = 0.0; size_t first = 0; /* Index of the first point which belongs to the node. */ size_t last = 0; /* Index of the first point which does not belong to the node. */ @@ -62,7 +62,7 @@ namespace gapp::detail Matrix points_; std::vector nodes_; - inline static constexpr size_t MAX_LEAF_ELEMENTS = 8; /* The maximum number of points in a leaf node. */ + static constexpr size_t MAX_LEAF_ELEMENTS = 8; /* The maximum number of points in a leaf node. */ void buildTree(); diff --git a/src/utility/dynamic_bitset.hpp b/src/utility/dynamic_bitset.hpp index 31c22b67..4fbb2c29 100644 --- a/src/utility/dynamic_bitset.hpp +++ b/src/utility/dynamic_bitset.hpp @@ -12,7 +12,7 @@ #include #include -// NOLINTBEGIN(*bool-conversion, *assignment, *assignment-signature, *operator) +// NOLINTBEGIN(*bool-conversion, *assignment, *assignment-signature, *operator, *ref-data-members) namespace gapp::detail { @@ -251,6 +251,6 @@ namespace gapp::detail } // namespace gapp::detail -// NOLINTEND(*bool-conversion, *assignment, *assignment-signature, *operator) +// NOLINTEND(*bool-conversion, *assignment, *assignment-signature, *operator, *ref-data-members) #endif // !GAPP_UTILITY_DYNAMIC_BITSET_HPP diff --git a/src/utility/functional.hpp b/src/utility/functional.hpp index 1631a645..3d65eba4 100644 --- a/src/utility/functional.hpp +++ b/src/utility/functional.hpp @@ -74,36 +74,6 @@ namespace gapp::detail return result; } - template - std::vector flatten(const std::vector>& pairs) - { - std::vector flat; - flat.reserve(2 * pairs.size()); - - for (size_t i = 0; i < pairs.size(); i++) - { - flat.push_back(pairs[i].first); - flat.push_back(pairs[i].second); - } - - return flat; - } - - template - std::vector flatten(std::vector>&& pairs) - { - std::vector flat; - flat.reserve(2 * pairs.size()); - - for (size_t i = 0; i < pairs.size(); i++) - { - flat.push_back(std::move(pairs[i].first)); - flat.push_back(std::move(pairs[i].second)); - } - - return flat; - } - template constexpr auto multiply_by(const T& multiplier) diff --git a/src/utility/latch.hpp b/src/utility/latch.hpp new file mode 100644 index 00000000..785a1023 --- /dev/null +++ b/src/utility/latch.hpp @@ -0,0 +1,43 @@ +/* Copyright (c) 2024 Krisztián Rugási. Subject to the MIT License. */ + +#ifndef GA_UTILITY_LATCH_HPP +#define GA_UTILITY_LATCH_HPP + +#include "utility.hpp" +#include +#include +#include + +namespace gapp::detail +{ + class latch + { + public: + explicit latch(std::uint32_t n) noexcept : + count_(n) + {} + + void count_down(std::uint32_t n = 1) noexcept + { + count_.fetch_sub(n, std::memory_order_release); + } + + void wait() const noexcept + { + while (count_.load(std::memory_order_relaxed)) std::this_thread::yield(); + std::atomic_thread_fence(std::memory_order_acquire); + GAPP_ANNOTATE_TSAN_ACQUIRE(&count_); + } + + bool try_wait() const noexcept + { + return count_.load(std::memory_order_acquire) == 0; + } + + private: + alignas(64) std::atomic count_; + }; + +} // namespace gapp::detail + +#endif // !GA_UTILITY_LATCH_HPP diff --git a/src/utility/qrng.hpp b/src/utility/qrng.hpp index 6eab5b12..1868a081 100644 --- a/src/utility/qrng.hpp +++ b/src/utility/qrng.hpp @@ -106,7 +106,7 @@ namespace gapp::rng } template - constexpr inline RealType QuasiRandom::phi(size_type dim, size_t n) noexcept + constexpr RealType QuasiRandom::phi(size_type dim, size_t n) noexcept { RealType phid = 1.0; while (n--) diff --git a/src/utility/rcu.hpp b/src/utility/rcu.hpp index 7f774315..2851351c 100644 --- a/src/utility/rcu.hpp +++ b/src/utility/rcu.hpp @@ -68,7 +68,7 @@ namespace gapp::detail std::vector list; }; - inline static constexpr uint64_t NOT_READING = std::numeric_limits::max(); + static constexpr uint64_t NOT_READING = std::numeric_limits::max(); GAPP_API inline static detail::Indestructible tls_readers; GAPP_API inline static constinit std::atomic writer_epoch = 0; diff --git a/src/utility/rng.hpp b/src/utility/rng.hpp index b6a1fcec..7fa8a378 100644 --- a/src/utility/rng.hpp +++ b/src/utility/rng.hpp @@ -285,9 +285,9 @@ namespace gapp::rng Xoroshiro128p instance{ 0 }; - detail::uniform_bool_distribution bool_distribution = {}; - std::normal_distribution normal_distribution = {}; - std::poisson_distribution poisson_distribution = {}; + detail::uniform_bool_distribution bool_distribution; + std::normal_distribution normal_distribution; + std::poisson_distribution poisson_distribution; }; struct GeneratorList diff --git a/src/utility/shared_spinlock.hpp b/src/utility/shared_spinlock.hpp index 4142cf91..b9a66485 100644 --- a/src/utility/shared_spinlock.hpp +++ b/src/utility/shared_spinlock.hpp @@ -54,7 +54,7 @@ namespace gapp::detail } private: - constexpr inline static std::uint32_t WRITER = std::numeric_limits::max() >> 1; + static constexpr std::uint32_t WRITER = std::numeric_limits::max() >> 1; std::atomic cntr_; }; diff --git a/src/utility/small_vector.hpp b/src/utility/small_vector.hpp index 999ed993..f2995390 100644 --- a/src/utility/small_vector.hpp +++ b/src/utility/small_vector.hpp @@ -276,14 +276,14 @@ namespace gapp::detail public: template constexpr allocator_managed(Allocator& alloc, Args&&... args) : - alloc_(alloc) + alloc_(std::addressof(alloc)) { - detail::construct(alloc_, std::addressof(**this), std::forward(args)...); + detail::construct(*alloc_, std::addressof(**this), std::forward(args)...); } constexpr ~allocator_managed() noexcept { - detail::destroy(alloc_, std::addressof(**this)); + detail::destroy(*alloc_, std::addressof(**this)); } constexpr T& operator*() noexcept { return data_; } @@ -291,7 +291,7 @@ namespace gapp::detail private: union { T data_; }; - Allocator& alloc_; + Allocator* alloc_; }; //--------------------------------------- SMALL VECTOR BUFFER ------------------------------------------------------- @@ -321,11 +321,11 @@ namespace gapp::detail struct default_small_size { private: - inline constexpr static std::size_t overall_size = cache_line_size; - inline constexpr static std::size_t buffer_size = overall_size - 3 * sizeof(T*); - inline constexpr static std::size_t buffer_min_count = 4; + constexpr static std::size_t overall_size = cache_line_size; + constexpr static std::size_t buffer_size = overall_size - 3 * sizeof(T*); + constexpr static std::size_t buffer_min_count = 4; public: - inline constexpr static std::size_t value = std::max(buffer_min_count, buffer_size / sizeof(T)); + constexpr static std::size_t value = std::max(buffer_min_count, buffer_size / sizeof(T)); }; template diff --git a/src/utility/thread_pool.hpp b/src/utility/thread_pool.hpp index 3d1c79af..7df38b7a 100644 --- a/src/utility/thread_pool.hpp +++ b/src/utility/thread_pool.hpp @@ -7,6 +7,7 @@ #include "algorithm.hpp" #include "functional.hpp" #include "iterators.hpp" +#include "latch.hpp" #include "utility.hpp" #include #include @@ -14,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -61,7 +61,7 @@ namespace gapp::detail const size_t step_size = iterations / task_count; const size_t remainder = iterations % task_count; - std::latch remaining_tasks(task_count - 1); + detail::latch remaining_tasks(task_count - 1); for (size_t i = 0; i < task_count - 1; i++) { diff --git a/src/utility/type_traits.hpp b/src/utility/type_traits.hpp index cfa56c8a..fa737f69 100644 --- a/src/utility/type_traits.hpp +++ b/src/utility/type_traits.hpp @@ -111,7 +111,7 @@ namespace gapp::detail static std::true_type f(BaseTempl*); static std::false_type f(...); public: - inline constexpr static bool value = decltype( f(static_cast(nullptr)) )::value; + constexpr static bool value = decltype( f(static_cast(nullptr)) )::value; }; template class BaseTempl> diff --git a/src/utility/utility.hpp b/src/utility/utility.hpp index 6084a2b9..a1fa17ff 100644 --- a/src/utility/utility.hpp +++ b/src/utility/utility.hpp @@ -10,10 +10,24 @@ #include -#define GAPP_GCC_COMPILER ( __GNUC__ && !__clang__ ) -#define GAPP_CLANG_COMPILER ( __GNUC__ && __clang__ ) -#define GAPP_MSVC_COMPILER ( _MSC_VER && !__clang__ ) -#define GAPP_CLANG_CL_COMPILER ( _MSC_VER && __clang__ ) +#if defined(__GNUC__) && !defined(__clang__) +# define GAPP_GCC_COMPILER +#elif defined(__GNUC__) && defined(__clang__) +# define GAPP_CLANG_COMPILER +#elif defined(_MSC_VER) && !defined(__clang__) +# define GAPP_MSVC_COMPILER +#elif defined(_MSC_VER) && defined(__clang__) +# define GAPP_CLANG_CL_COMPILER +#endif + + +#if defined(_M_IX86) || defined(_M_X64) || defined(__x86_64__) || defined(__i386__) +# define GAPP_X86_ARCH +#endif + +#if defined(_M_ARM) || defined(_M_ARM64) || defined(__arm__) || defined(__aarch64__) +# define GAPP_ARM_ARCH +#endif #if defined(_MSC_VER) @@ -35,7 +49,7 @@ #if __has_cpp_attribute(no_unique_address) -# if GAPP_MSVC_COMPILER +# if defined(GAPP_MSVC_COMPILER) # define GAPP_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] # else # define GAPP_NO_UNIQUE_ADDRESS [[no_unique_address]] @@ -77,10 +91,12 @@ #endif -#if defined(__GNUC__) || defined(__clang__) +#if (defined(__GNUC__) || defined(__clang__)) && defined(GAPP_X86_ARCH) # define GAPP_PAUSE() __builtin_ia32_pause() -#elif defined(_MSC_VER) +#elif defined(GAPP_MSVC_COMPILER) && defined(GAPP_X86_ARCH) # define GAPP_PAUSE() _mm_pause() +#elif defined(GAPP_MSVC_COMPILER) && defined(GAPP_ARM_ARCH) +# define GAPP_PAUSE() __yield() #else # define GAPP_PAUSE() #endif @@ -89,16 +105,16 @@ #if defined(__has_feature) # if __has_feature(thread_sanitizer) && __has_include() # include -# define GAPP_ANNOTATE_TSAN_ACQUIRE(p) __tsan_acquire(p) -# define GAPP_ANNOTATE_TSAN_RELEASE(p) __tsan_release(p) +# define GAPP_ANNOTATE_TSAN_ACQUIRE(p) __tsan_acquire((void*)p) +# define GAPP_ANNOTATE_TSAN_RELEASE(p) __tsan_release((void*)p) # else # define GAPP_ANNOTATE_TSAN_ACQUIRE(p) # define GAPP_ANNOTATE_TSAN_RELEASE(p) # endif #elif defined(__SANITIZE_THREAD__) && __has_include() # include -# define GAPP_ANNOTATE_TSAN_ACQUIRE(p) __tsan_acquire(p) -# define GAPP_ANNOTATE_TSAN_RELEASE(p) __tsan_release(p) +# define GAPP_ANNOTATE_TSAN_ACQUIRE(p) __tsan_acquire((void*)p) +# define GAPP_ANNOTATE_TSAN_RELEASE(p) __tsan_release((void*)p) #else # define GAPP_ANNOTATE_TSAN_ACQUIRE(p) # define GAPP_ANNOTATE_TSAN_RELEASE(p) diff --git a/test/unit/functional.cpp b/test/unit/functional.cpp index 507f245d..ddc1e887 100644 --- a/test/unit/functional.cpp +++ b/test/unit/functional.cpp @@ -26,13 +26,6 @@ TEST_CASE("map", "[functional]") REQUIRE(map(std::vector{}, std::identity{}).empty()); } -TEST_CASE("flatten", "[functional]") -{ - std::vector> num_pairs = { { 0, 1 }, { 1, 3 }, { 5, 2 }}; - - REQUIRE(flatten(num_pairs) == std::vector{ 0, 1, 1, 3, 5, 2 }); -} - TEST_CASE("arithmetic_funcs", "[functional]") { const std::vector nums = { 1, 2, 4, 2, 9 }; diff --git a/test/unit/synchronization.cpp b/test/unit/synchronization.cpp index 9158e43a..01f8ed4d 100644 --- a/test/unit/synchronization.cpp +++ b/test/unit/synchronization.cpp @@ -3,6 +3,7 @@ #include #include "utility/spinlock.hpp" #include "utility/shared_spinlock.hpp" +#include "utility/latch.hpp" #include #include #include @@ -56,3 +57,18 @@ TEST_CASE("shared_spinlock", "[synchronization]") REQUIRE(0 <= read); REQUIRE(read <= 3000); } + +TEST_CASE("latch", "[synchronization]") +{ + int n1 = 1; + int n2 = 1; + latch nthreads(2); + + std::jthread t1{ [&]{ n1--; nthreads.count_down(); } }; + std::jthread t2{ [&]{ n2--; nthreads.count_down(); } }; + + nthreads.wait(); + + REQUIRE(n2 == 0); + REQUIRE(n1 == 0); +} diff --git a/test/unit/thread_pool.cpp b/test/unit/thread_pool.cpp index fa5207e7..e029bd82 100644 --- a/test/unit/thread_pool.cpp +++ b/test/unit/thread_pool.cpp @@ -43,27 +43,26 @@ TEST_CASE("concurrent_queue", "[thread-pool]") TEST_CASE("thread_pool", "[thread-pool]") { - std::atomic n = 0; - auto increment_n = [&]{ n++; }; - thread_pool pool; - std::vector> futures; + latch count(1000); + + int n = 0; + auto increment_n = [&] { std::atomic_ref{ n }.fetch_add(1, std::memory_order_relaxed); count.count_down(); }; for (size_t i = 0; i < 1000; i++) { - auto res = pool.execute_task(increment_n); - futures.push_back(std::move(res)); + std::ignore = pool.execute_task(increment_n); } - for (auto& elem : futures) { elem.wait(); } + count.wait(); REQUIRE(n == 1000); } TEST_CASE("parallel_for", "[thread-pool]") { - std::atomic n = 0; - auto increment_n = [&](int) { n++; }; + int n = 0; + auto increment_n = [&](int) { std::atomic_ref{ n }.fetch_add(1, std::memory_order_relaxed); }; parallel_for(iota_iterator(0), iota_iterator(100), increment_n); REQUIRE(n == 100); @@ -74,7 +73,7 @@ TEST_CASE("parallel_for", "[thread-pool]") TEST_CASE("nested_parallel_for", "[thread-pool]") { - std::atomic n = 0; + int n = 0; parallel_for(iota_iterator(0), iota_iterator(10), [&](int) { @@ -82,7 +81,7 @@ TEST_CASE("nested_parallel_for", "[thread-pool]") { parallel_for(iota_iterator(0), iota_iterator(100), [&](int) { - n.fetch_add(1, std::memory_order_relaxed); + std::atomic_ref{ n }.fetch_add(1, std::memory_order_relaxed); }); }); });