├── .github └── workflows │ └── build.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CMakeLists.txt ├── CMakePresets.json ├── Documentation ├── ChangeLog.md ├── algorithms.md ├── deploying.md └── inspiration.md ├── Example ├── CMakeLists.txt └── main.cpp ├── Inspiration ├── CMakeLists.txt └── qlistview_all_columns_selected.cpp ├── KDAlgorithmsConfig.cmake.in ├── LICENSE.txt ├── LICENSES └── MIT.txt ├── README.md ├── REUSE.toml ├── _clang-format ├── appveyor.yml ├── run ├── src ├── kdalgorithms.h └── kdalgorithms_bits │ ├── cartesian_product.h │ ├── filter.h │ ├── find_if.h │ ├── generate.h │ ├── insert_wrapper.h │ ├── invoke.h │ ├── is_const_method.h │ ├── is_detected.h │ ├── method_tests.h │ ├── operators.h │ ├── read_iterator_wrapper.h │ ├── reserve_helper.h │ ├── return_type_trait.h │ ├── shared.h │ ├── to_function_object.h │ ├── transform.h │ ├── tuple_utils.h │ └── zip.h └── tests ├── ContainerObserver.h ├── copy_observer.cpp ├── copy_observer.h ├── test_install ├── CMakeLists.txt └── main.cpp ├── tst_constraints.cpp ├── tst_kdalgorithms.cpp └── tst_return_type_traits.cpp /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2023-2024 Klarälvdalens Datakonsult AB, a KDAB Group company 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | name: CI 6 | 7 | on: 8 | push: 9 | branches: 10 | - main 11 | pull_request: 12 | branches: 13 | - main 14 | 15 | jobs: 16 | build: 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: 22 | - ubuntu-latest 23 | - windows-latest 24 | - macos-latest 25 | preset: 26 | - cpp14 27 | - cpp17 28 | - cpp20 29 | - cpp23 30 | config: 31 | - qt_version: 5.15.2 32 | macos_architectures: "x86_64" 33 | - qt_version: 6.6.2 34 | macos_architectures: "x86_64;arm64" 35 | 36 | steps: 37 | - name: Install Qt with options and default aqtversion 38 | uses: jurplel/install-qt-action@v3 39 | with: 40 | aqtversion: null # use whatever the default is 41 | version: ${{ matrix.config.qt_version }} 42 | cache: true 43 | 44 | - name: Checkout sources 45 | uses: actions/checkout@v4 46 | 47 | - name: Install ninja-build tool (must be after Qt due PATH changes) 48 | uses: turtlesec-no/get-ninja@main 49 | 50 | - name: Make sure MSVC is found when Ninja generator is in use 51 | if: ${{ runner.os == 'Windows' }} 52 | uses: ilammy/msvc-dev-cmd@v1 53 | 54 | - name: Configure Debug Build 55 | run: > 56 | cmake -S . -B ./build-debug --preset ${{ matrix.preset }} 57 | -DCMAKE_OSX_ARCHITECTURES="${{ matrix.config.macos_architectures }}" 58 | -DCMAKE_BUILD_TYPE=Debug 59 | -DBUILD_TESTING=ON 60 | -DKDALGORITHMS_BUILD_TEST=ON 61 | 62 | - name: Build Project (Debug) 63 | run: cmake --build ./build-debug 64 | 65 | - name: Run Debug tests 66 | id: tests1 67 | run: ctest --test-dir ./build-debug -C Debug --output-on-failure 68 | 69 | # KDAlgorithms CI only matters (and builds) for the tests and since 70 | # the only value of a Release build is that maybe the compiler 71 | # does some optimization that breaks them we do both builds 72 | # in the same worker. 73 | - name: Configure Release Build 74 | run: > 75 | cmake -S . -B ./build-release --preset ${{ matrix.preset }} 76 | -DCMAKE_OSX_ARCHITECTURES="${{ matrix.config.macos_architectures }}" 77 | -DCMAKE_BUILD_TYPE=Release 78 | -DBUILD_TESTING=ON 79 | -DKDALGORITHMS_BUILD_TEST=ON 80 | -DCMAKE_INSTALL_PREFIX=installed 81 | 82 | - name: Build Project (Release) 83 | run: cmake --build ./build-release 84 | 85 | - name: Run Release tests 86 | id: tests2 87 | run: ctest --test-dir ./build-release -C Release --output-on-failure 88 | 89 | - name: Install Project 90 | run: cmake --install ./build-release 91 | 92 | - name: Build standalone example against systemwide KDAlgorithms 93 | run: | 94 | cd tests/test_install 95 | cmake -DCMAKE_PREFIX_PATH="../../installed" . 96 | cmake --build . 97 | 98 | - name: Read tests log when it fails 99 | uses: andstor/file-reader-action@v1 100 | if: ${{ steps.tests1.outcome == 'failure' || steps.tests2.outcome == 'failure' }} 101 | with: 102 | path: "./build/Testing/Temporary/LastTest.log" 103 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | build 3 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | ci: 4 | autoupdate_schedule: monthly 5 | repos: 6 | - repo: https://github.com/fsfe/reuse-tool 7 | rev: v5.0.2 8 | hooks: 9 | - id: reuse 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(kdalgorithms 3 | LANGUAGES CXX 4 | ) 5 | 6 | if(POLICY CMP0077) # Yes, let the parent project set BUILD_TESTING before we include CTest.cmake below 7 | cmake_policy(SET CMP0077 NEW) 8 | endif() 9 | 10 | add_library(kdalgorithms INTERFACE) 11 | add_library(KDAB::KDAlgorithms ALIAS kdalgorithms) 12 | target_include_directories(kdalgorithms INTERFACE 13 | $ 14 | $ 15 | ) 16 | 17 | if(MSVC) 18 | target_compile_options(kdalgorithms INTERFACE /Zc:__cplusplus) 19 | endif() 20 | 21 | include(CTest) 22 | 23 | if (CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) 24 | set(MAIN_PROJECT ON) 25 | endif() 26 | 27 | option(KDALGORITHMS_BUILD_TEST "Build the kdalgorithms unit tests when BUILD_TESTING is enabled." ${MAIN_PROJECT}) 28 | 29 | if(BUILD_TESTING AND ${KDALGORITHMS_BUILD_TEST}) 30 | 31 | # This library support C++14 and above. 32 | if (NOT DEFINED CMAKE_CXX_STANDARD) 33 | set(CMAKE_CXX_STANDARD 20) 34 | endif() 35 | 36 | add_compile_options(-Wall) 37 | 38 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 39 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wno-deprecated -Wextra -Woverloaded-virtual -Winit-self -Wmissing-include-dirs -Wunused -Wno-div-by-zero -Werror=undef -Wpointer-arith -Wmissing-noreturn -Werror=return-type") 40 | endif() 41 | 42 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 43 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunused-parameter") 44 | endif() 45 | 46 | if(MSVC) # Check if we are using the Visual Studio compiler 47 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus") # Make __cplusplus match the actual C++ version used 48 | endif() 49 | 50 | # Needed for the unit tests 51 | find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Widgets REQUIRED) 52 | find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED COMPONENTS Core Test) 53 | set(CMAKE_AUTOMOC TRUE) 54 | 55 | enable_testing() 56 | add_executable(tst_kdalgorithms 57 | src/kdalgorithms.h 58 | 59 | src/kdalgorithms_bits/read_iterator_wrapper.h 60 | src/kdalgorithms_bits/find_if.h 61 | src/kdalgorithms_bits/filter.h 62 | src/kdalgorithms_bits/generate.h 63 | src/kdalgorithms_bits/insert_wrapper.h 64 | src/kdalgorithms_bits/is_const_method.h 65 | src/kdalgorithms_bits/is_detected.h 66 | src/kdalgorithms_bits/method_tests.h 67 | src/kdalgorithms_bits/operators.h 68 | src/kdalgorithms_bits/reserve_helper.h 69 | src/kdalgorithms_bits/return_type_trait.h 70 | src/kdalgorithms_bits/shared.h 71 | src/kdalgorithms_bits/to_function_object.h 72 | src/kdalgorithms_bits/transform.h 73 | src/kdalgorithms_bits/zip.h 74 | src/kdalgorithms_bits/tuple_utils.h 75 | src/kdalgorithms_bits/invoke.h 76 | src/kdalgorithms_bits/cartesian_product.h 77 | 78 | tests/tst_kdalgorithms.cpp 79 | tests/tst_constraints.cpp 80 | tests/copy_observer.h 81 | tests/copy_observer.cpp 82 | ) 83 | 84 | add_test(NAME tst_kdalgorithms COMMAND tst_kdalgorithms) 85 | target_link_libraries(tst_kdalgorithms Qt${QT_VERSION_MAJOR}::Test) 86 | 87 | add_executable(tst_return_type_traits tests/tst_return_type_traits.cpp) 88 | 89 | # Make it show up in Qt Creator 90 | add_custom_target(additional_files SOURCES 91 | README.md 92 | run 93 | Documentation/algorithms.md 94 | Documentation/deploying.md 95 | Documentation/inspiration.md 96 | Documentation/ChangeLog.md 97 | ) 98 | 99 | add_subdirectory(Inspiration) 100 | endif() 101 | 102 | install(TARGETS kdalgorithms EXPORT KDAlgorithmsTargets) 103 | 104 | install(EXPORT KDAlgorithmsTargets 105 | FILE KDAlgorithmsTargets.cmake 106 | NAMESPACE KDAB:: 107 | DESTINATION lib/cmake/KDAlgorithms 108 | ) 109 | 110 | install(FILES 111 | src/kdalgorithms.h 112 | DESTINATION include/kdalgorithms 113 | ) 114 | 115 | install(FILES 116 | src/kdalgorithms_bits/read_iterator_wrapper.h 117 | src/kdalgorithms_bits/find_if.h 118 | src/kdalgorithms_bits/filter.h 119 | src/kdalgorithms_bits/generate.h 120 | src/kdalgorithms_bits/insert_wrapper.h 121 | src/kdalgorithms_bits/is_const_method.h 122 | src/kdalgorithms_bits/is_detected.h 123 | src/kdalgorithms_bits/method_tests.h 124 | src/kdalgorithms_bits/operators.h 125 | src/kdalgorithms_bits/reserve_helper.h 126 | src/kdalgorithms_bits/return_type_trait.h 127 | src/kdalgorithms_bits/shared.h 128 | src/kdalgorithms_bits/to_function_object.h 129 | src/kdalgorithms_bits/transform.h 130 | src/kdalgorithms_bits/zip.h 131 | src/kdalgorithms_bits/tuple_utils.h 132 | src/kdalgorithms_bits/invoke.h 133 | src/kdalgorithms_bits/cartesian_product.h 134 | DESTINATION include/kdalgorithms/kdalgorithms_bits 135 | ) 136 | 137 | include(CMakePackageConfigHelpers) 138 | 139 | configure_package_config_file( 140 | KDAlgorithmsConfig.cmake.in 141 | ${CMAKE_CURRENT_BINARY_DIR}/KDAlgorithmsConfig.cmake 142 | INSTALL_DESTINATION lib/cmake/KDAlgorithms 143 | ) 144 | 145 | install(FILES 146 | ${CMAKE_CURRENT_BINARY_DIR}/KDAlgorithmsConfig.cmake 147 | DESTINATION lib/cmake/KDAlgorithms 148 | ) 149 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "configurePresets": [ 4 | { 5 | "name": "cpp14", 6 | "generator": "Ninja", 7 | "cacheVariables": { 8 | "CMAKE_CXX_STANDARD": "14" 9 | } 10 | }, 11 | { 12 | "name": "cpp17", 13 | "generator": "Ninja", 14 | "cacheVariables": { 15 | "CMAKE_CXX_STANDARD": "17" 16 | } 17 | }, 18 | { 19 | "name": "cpp20", 20 | "generator": "Ninja", 21 | "cacheVariables": { 22 | "CMAKE_CXX_STANDARD": "20" 23 | } 24 | }, 25 | { 26 | "name": "cpp23", 27 | "generator": "Ninja", 28 | "cacheVariables": { 29 | "CMAKE_CXX_STANDARD": "23" 30 | } 31 | }, 32 | { 33 | "name": "gcc", 34 | "hidden": true, 35 | "cacheVariables": { 36 | "CMAKE_CXX_COMPILER": "g++" 37 | }, 38 | "binaryDir": "${sourceDir}/build/${presetName}" 39 | }, 40 | { 41 | "name": "clang", 42 | "hidden": true, 43 | "cacheVariables": { 44 | "CMAKE_CXX_COMPILER": "clang++" 45 | }, 46 | "binaryDir": "${sourceDir}/build/${presetName}" 47 | }, 48 | { 49 | "name": "cpp14-gcc", 50 | "inherits": ["cpp14", "gcc"] 51 | }, 52 | { 53 | "name": "cpp14-clang", 54 | "inherits": ["cpp14", "clang"] 55 | }, 56 | { 57 | "name": "cpp17-gcc", 58 | "inherits": ["cpp17", "gcc"] 59 | }, 60 | { 61 | "name": "cpp17-clang", 62 | "inherits": ["cpp17", "clang"] 63 | }, 64 | { 65 | "name": "cpp20-gcc", 66 | "inherits": ["cpp20", "gcc"] 67 | }, 68 | { 69 | "name": "cpp20-clang", 70 | "inherits": ["cpp20", "clang"] 71 | }, 72 | { 73 | "name": "cpp23-gcc", 74 | "inherits": ["cpp23", "gcc"] 75 | }, 76 | { 77 | "name": "cpp23-clang", 78 | "inherits": ["cpp23", "clang"] 79 | } 80 | ] 81 | } 82 | -------------------------------------------------------------------------------- /Documentation/ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Version 1.4 released 2 | * Minimal range support 3 | * New algorithms cartesian_product 4 | * New algorithm transformed_map_values 5 | 6 | # Version 1.3 released 7 | * Introduce algorithms sum and sum_if 8 | * Do not require operator< if not needed in remove_duplicates(3 months ago) 9 | 10 | # Version 1.2 released 11 | * Added new algorithm multi_transform 12 | * compile fix: void_t isn't available before C++17 13 | * Call std::invoke if we have it (#58) 14 | * renamed the bits subdirectory to kdabalgorithms_bits 15 | * Improved sort_by (#57) 16 | 17 | # Version 1.1 released 18 | * New algorithms sort_by and sorted_by 19 | * Remove std::move() on temporaries, as detected by gcc 13 (#55) 20 | * Fix issue #50 - find_if creates dangling references 21 | * Ensure min_value and max_value works on containers without .empty() method. 22 | * Renamed min_element to min_value, and max_element to max_value (see issue #11) 23 | 24 | # 2023-02-27 Version 1.0.0 released 25 | -------------------------------------------------------------------------------- /Documentation/algorithms.md: -------------------------------------------------------------------------------- 1 | Algorithms 2 | ========== 3 | 4 | Modifying algorithms 5 | 6 | - copy 7 | - copied 8 | - filter 9 | - transform / transformed 10 | - filtered_transformed 11 | - transformed_map_values 12 | - reverse 13 | - sort / sorted 14 | - sort_by / sorted_by 15 | - remove_duplicates 16 | - erase / erase_if 17 | 18 | Queries 19 | 20 | - all_of / any_of / none_of 21 | - contains 22 | - value_in 23 | - find_if / mutable_find_if 24 | - count 25 | - min_value / max_value (C++17) 26 | - min_value_greater_than / max_value_less_than / max_value_less_than_unordered / min_value_greater_than_unordered (C++17) 27 | - is_permutation 28 | - get_match (C++17) / get_match_or_default 29 | - index_of_match 30 | - has_duplicates 31 | - is_sorted 32 | 33 | Other 34 | 35 | - accumulate 36 | - accumulate_if 37 | - sum / sum_if 38 | - iota 39 | - generate_n 40 | - generate_until 41 | - for_each 42 | - partitioned 43 | - multi_partitioned 44 | - zip 45 | - product 46 | 47 | 48 | 49 | copy 50 | ----------------------- 51 | This algorithm appends the content of one collection to another. 52 | 53 | ``` 54 | std::vector src = ...; 55 | std::list dest = ...; 56 | kdalgorithms::copy(src, dest); 57 | ``` 58 | 59 | To only copy some elements, see the algorithms filter and filtered 60 | 61 | See [std::copy](https://en.cppreference.com/w/cpp/algorithm/copy) for the algorithm from the standard. 62 | 63 | copied 64 | --------------------------- 65 | This algorithm returns a copy of the input elements, with the container changed in the process, simlimar to transformed. 66 | If you do not need to change the container, then simply use the contaioners copy constrcutor instead of this algorithm. 67 | 68 | ``` 69 | std::vector vec{1, 2, 3, 4, 1, 3}; 70 | auto result = kdalgorithms::copied(vec); 71 | // result is now an unordered_set with the items 1,2,3,4 in. 72 | ``` 73 | 74 | The items themselves may also be implicit converted in the process: 75 | 76 | ``` 77 | std::vector vec{1, 2, 3, 4, 1, 3}; 78 | auto result = kdalgorithms::copied>(vec); 79 | // result is now an unordered_set of doubles with the items 1,2,3,4 in. 80 | ``` 81 | 82 | filter / filtered 83 | -------------------------------------- 84 | The simplest way to use filtered is the code below, which takes a vector of ints 85 | and returns a new vector, with the elements matching the provided predicate. 86 | 87 | > 88 | ``` 89 | auto is_odd = [] (int i) { return i%2 == 1; }; 90 | std::vector ints{1,2,3,4,5}; 91 | auto odds = kdalgorithms::filtered(ints, is_odd); 92 | ``` 93 | 94 | The algorithm may change the container on the fly: 95 | 96 | > 97 | ``` 98 | auto is_odd = [] (int i) { return i%2 == 1; }; 99 | std::list ints{1,2,3,4,5}; 100 | auto odds = kdalgorithms::filtered(ints, is_odd); 101 | // ods = std::vector{1,3,5} 102 | ``` 103 | 104 | 105 | There is also a variant, which does the filtering inline (that is the result will be in the provided container). 106 | 107 | > 108 | ``` 109 | auto is_odd = [] (int i) { return i%2 == 1; }; 110 | std::vector ints{1,2,3,4,5}; 111 | kdalgorithms::filter(ints, is_odd); 112 | // ints = {1, 3, 5} 113 | ``` 114 | 115 | See [std::copy_if](https://en.cppreference.com/w/cpp/algorithm/copy) for the algorithm from the standard. 116 | 117 | 118 | transform / transformed 119 | ----------------------------------------------- 120 | This set of algorithms takes a collection and return a collection of the same size 121 | with all the elements transformed by the provided method. 122 | 123 | The first example has the same container as input and output: 124 | 125 | ``` 126 | std::vector ints{1,2,3}; 127 | auto square = [] (int i) { return i*i; } 128 | auto result = kdalgorithms::transformed(ints, square); 129 | ``` 130 | 131 | The type of container and items may also change during the transformation 132 | 133 | ``` 134 | std::vector ints{1,2,3}; 135 | auto toString = [] (int i) { return QString::number(i); } 136 | QVector result = kdalgorithms::transformed(ints, toString); 137 | ``` 138 | 139 | Finally there is a version which does an inline transform: 140 | 141 | ``` 142 | std::vector ints{1,2,3}; 143 | auto square = [] (int i) { return i*i; } 144 | kdalgorithms::transform(ints, square); 145 | // ints = {1,4,9} 146 | ``` 147 | 148 | transformed on maps 149 | ------------------- 150 | The transform functions can unfortunately not automatically deduce the 151 | type of the result container - even when it is just the same type as the input container, 152 | therefore two additional functions are provided: 153 | 154 | ``` 155 | std::map map{{1, "abc"}, {2, "def"}, {3, "hij"}, {4, "klm"}}; 156 | auto doubleKeys = [](const auto& item) { 157 | return std::make_pair(item.first * 2, item.second); 158 | }; 159 | auto result = kdalgorithms::transformed_to_same_container(map, doubleKeys); 160 | // result = {2, "abc"}, {4, "def"}, {6, "hij"}, {8, "klm"} 161 | ``` 162 | 163 | 164 | ``` 165 | std::map map{{1, "abc"}, {2, "def"}, {3, "hij"}, {4, "klm"}}; 166 | auto result = kdalgorithms::transformed_with_new_return_type>( 167 | map, [](auto item) { return std::make_pair(item.second, item.first); }); 168 | // result = {{"abc", 1}, {"def", 2}, {"hij", 3}, {"klm", 4}}; 169 | ``` 170 | 171 | See [std::transform](https://en.cppreference.com/w/cpp/algorithm/transform) for the algorithm from the standard. 172 | 173 | filtered_transformed 174 | ------------------------------------------- 175 | filtered_transformed is a combination of transformed and filtered. 176 | The function takes a container and throws away any item that doesn't match the filter, and applies the transform on the remaining. 177 | 178 | ``` 179 | std::vector intVector{1, 2, 3, 4}; 180 | auto squareItem = [] (int i) { return i*i; }; 181 | auto isOdd = [] (int i) { return i%2 == 1; }; 182 | 183 | auto result = kdalgorithms::filtered_transformed(intVector, squareItem, isOdd); 184 | // result = {1, 9} 185 | ``` 186 | 187 | filtered_transformed supports the same features as regular transformed does, 188 | including using member functions, and using a different container for the result: 189 | 190 | Member functions: 191 | 192 | ``` 193 | auto result = kdalgorithms::filtered_transformed(structVec, &Struct::sumPairs, 194 | &Struct::isKeyGreaterThanValue); 195 | ``` 196 | 197 | Changing container 198 | 199 | ``` 200 | auto result = kdalgorithms::filtered_transformed(intVector, squareItem, isOdd); 201 | ``` 202 | 203 | transformed_map_values 204 | ----------------------------------------------------------- 205 | Another special case of transforming is to only transform the values in a map, ie. not the keys. 206 | 207 | ``` 208 | std::map map{{1, 2}, {2, 3}, {3, 4}}; 209 | auto toString = [](int i) { return std::to_string(i); }; 210 | auto result = kdalgorithms::transformed_map_values(map, toString); 211 | // result is now std::map{{1, "2"}, {2, "3"}, {3, "4"}}; 212 | ``` 213 | 214 | A more involving example would be this: 215 | 216 | ``` 217 | struct TimeOnProjects 218 | { 219 | int projectID; 220 | int hours; 221 | }; 222 | using TimeList = QList; 223 | 224 | TimeList timeOnProjects{{1, 10}, {2, 20}, {1, 30}, {3, 40}, {2, 12}}; 225 | 226 | auto map = kdalgorithms::multi_partitioned(timeOnProjects, &TimeOnProjects::projectID); 227 | auto sumList = [](const TimeList &list) { 228 | return kdalgorithms::sum(list, &TimeOnProjects::hours); 229 | }; 230 | auto result = kdalgorithms::transformed_map_values(map, sumList); 231 | // result is std::map{{1, 40}, {2, 32}, {3, 40}}; 232 | ``` 233 | 234 | 235 | reverse / reversed 236 | ----------------------------------------- 237 | 238 | ``` 239 | std::vector ints{1,2,3}; 240 | auto result = kdalgorithms::reversed(ints); 241 | // result = {3,2,1} 242 | ``` 243 | 244 | And there is also an inline version: 245 | 246 | ``` 247 | std::vector ints{1,2,3}; 248 | kdalgorithms::reverse(ints); 249 | // ints = {3,2,1} 250 | ``` 251 | 252 | See [std::reverse](https://en.cppreference.com/w/cpp/algorithm/reverse) for the algorithm from the standard. 253 | 254 | 255 | sort / sorted 256 | -------------------------------- 257 | This algorithm comes in two version sort (inline) and sorted (returns the result). Besides the collection 258 | it may also take a comparison function. 259 | 260 | ``` 261 | std::vector ints{3,1,2}; 262 | auto result = kdalgorithms::sorted(ints); 263 | // result = 1,2,3 264 | ``` 265 | 266 | Inline: 267 | 268 | ``` 269 | std::vector ints{3,1,2}; 270 | kdalgorithms::sort(ints); 271 | // ints = 1,2,3 272 | ``` 273 | 274 | Inline and with comparison function 275 | 276 | ``` 277 | std::vector ints{3,1,2}; 278 | kdalgorithms::sort(ints, std::greater()); 279 | // ints = 3,2,1 280 | ``` 281 | 282 | See [std::sort](https://en.cppreference.com/w/cpp/algorithm/sort) for the algorithm from the standard. 283 | 284 | sort_by / sorted_by 285 | ------------------------------------------ 286 | **sort** may take a second parameter, which is the predicate for sorting. 287 | However, in many situations you simply have a container of struct, and want to sort it by one of the members of the struct. 288 | This is exactly what **sort_by** does. 289 | 290 | ``` 291 | struct Person 292 | { 293 | std::string name; 294 | int age; 295 | }; 296 | std::vector people{{"John", 25}, {"Jane", 20}, {"Bob", 27}}; 297 | kdalgorithms::sort_by(people, &Person::age); 298 | // people == {{"Jane", 20}, {"John", 25}, {"Bob", 27}} 299 | ``` 300 | 301 | As an alternative to specifying a member variable you may specify a member function: 302 | 303 | ``` 304 | std::vector list{"John", "James", "Bob"}; 305 | kdalgorithms::sort_by(list, &std::string::length); 306 | // list = Bob, John, James 307 | ``` 308 | 309 | You may also specify an extraction function as the second parameter: 310 | 311 | ``` 312 | std::map scores{{"John", 25}, {"Jane", 20}, {"Bob", 27}}; 313 | std::vector list{"John", "Jane", "Bob"}; 314 | auto score = [&](const std::string &name) { return scores[name]; }; 315 | kdalgorithms::sort_by(list, score); 316 | 317 | list = Jane, John, Bob 318 | ``` 319 | 320 | ### sort order 321 | The sort order may be specified for sort_by, using a third argument: 322 | 323 | ``` 324 | std::vector vec{{1, 3}, {3, 4}, {3, 2}, {1, 2}}; 325 | kdalgorithms::sort_by(vec, &Struct::value, kdalgorithms::descending); 326 | // vec = {3, 4}, {1, 3}, {3, 2}, {1, 2} 327 | ``` 328 | 329 | **sorted_by** is similar to sort_by, except that it returns a sorted copy of the container provided. 330 | 331 | 332 | is_sorted 333 | --------------------------------- 334 | Tells whether a sequence is sorted. An optional second argument is used for comparison. 335 | 336 | ``` 337 | auto result = kdalgorithms::is_sorted(std::vector{1, 3, 2, 4}); 338 | // result = true 339 | ``` 340 | 341 | ``` 342 | struct S 343 | { 344 | int x; 345 | int y; 346 | }; 347 | std::vector unsorted{{2, 3}, {1, 4}, {3, 2}, {4, 1}}; 348 | auto result = kdalgorithms::is_sorted(unsorted, [](Struct x, Struct y) { return x.key < y.key; }); 349 | // result = false 350 | ``` 351 | 352 | See [std::is_sorted](https://en.cppreference.com/w/cpp/algorithm/is_sorted) for the algorithm from the standard. 353 | 354 | 355 | all_of / any_of / none_of 356 | --------------------------------------------------- 357 | ``` 358 | std::vector ints{1,2,3}; 359 | auto is_odd = [] (int i) { return i%2 == 1; }; 360 | bool answer = kdalgorithms::any_of(ints, is_odd); 361 | ``` 362 | 363 | See [std::all_of, std::any_of, std:none_of](https://en.cppreference.com/w/cpp/algorithm/all_any_none_of) for the algorithms from the standard. 364 | 365 | contains 366 | -------------------------------- 367 | The algorithm *contains* is searching for an item, but in contrast to *any_of* it does so by simply specifying the item. 368 | 369 | ``` 370 | std::vector ints{1,2,3}; 371 | bool answer = kdalgorithms::contains(ints, 4); 372 | ``` 373 | 374 | The function is also overloaded with an initializer list, which makes it possible to write code like this: 375 | 376 | ``` 377 | enum class Column {ColumnA, ColumnB, ColumnC, ColumnD, ColumnE}; 378 | Column column = ...; 379 | 380 | if (kdalgorithms::contains({ColumnA, ColumnC, ColumnE}, column)) 381 | { 382 | .... 383 | } 384 | ``` 385 | 386 | However, kdalgorithms::value_in has a more pleasing syntax for that usecase. 387 | 388 | see [std::find](https://en.cppreference.com/w/cpp/algorithm/find) for the algorithm from the standard. 389 | 390 | value_in 391 | -------------------------------- 392 | 393 | This is similar to *contains* but with the arguments switched around. This makes it possible to write code 394 | that feels a lot like *value in range* from languages like Python: 395 | 396 | ``` 397 | if (kdalgorithms::value_in(column, {ColumnA, ColumnC, ColumnE})) 398 | { 399 | .... 400 | } 401 | ``` 402 | 403 | find_if / mutable_find_if 404 | ---------------------------------------------- 405 | KDAlgorithms' version of find_if, takes a complete collection rather than two iterators. 406 | This, however, poses the problem of which iterator to use when testing for no result. We've 407 | solved that by returns a proxy object with a simple boolean test method on. 408 | 409 | ``` 410 | std::vector vec{1, 2, 3, 4, 5}; 411 | auto result = kdalgorithms::find_if(vec, [](int i) { return i > 2; }); 412 | if (result) 413 | std::cout << *result << std::endl; 414 | else 415 | std::cout << "ahh nothing, right\n"; 416 | 417 | // prints: 3 418 | ``` 419 | 420 | If you want to modify the result of find_if, then you explicitly need to ask for a mutable version: 421 | 422 | ``` 423 | std::vector vec{1, 2, 3, 4, 5}; 424 | auto result = kdalgorithms::mutable_find_if(vec, [](int i) { return i > 2; }); 425 | assert(result.has_result()); 426 | *result = 42; 427 | for (auto i : vec) 428 | std::cout << i << ","; 429 | std::cout << std::endl; 430 | 431 | // prints: 1,2,42,4,5, 432 | ``` 433 | 434 | It is possible to get to the underlying iterator from the result. You, however, need to ensure to use the **begin** and **end** iterators found in the result object. 435 | 436 | ``` 437 | struct Person 438 | { 439 | int age; 440 | bool isDeveloper; 441 | }; 442 | 443 | std::vector vec{{20, true}, {21, false}, {30, true}, {35, false}, {35, true}}; 444 | auto result = 445 | kdalgorithms::mutable_find_if(vec, [](const auto &person) { return person.age > 30; }); 446 | 447 | // Observe we use result.begin and result.iterator 448 | std::partition(result.begin, result.iterator, 449 | [](const auto &person) { return person.isDeveloper; }); 450 | 451 | std::for_each(result.begin, result.iterator(), [](const auto &person) { 452 | std::cout << std::boolalpha << "(" << person.age << "," << person.isDeveloper << ") "; 453 | }); 454 | std::cout << std::endl; 455 | 456 | // prints: (20,true) (30,true) (21,false) 457 | ``` 458 | 459 | see [std::find_if](https://en.cppreference.com/w/cpp/algorithm/find_if) for the algorithm from the standard. 460 | 461 | count / count_if 462 | ------------------------------------- 463 | ``` 464 | std::vector vec{1, 2, 1, 3, 2, 1, 5}; 465 | auto result = kdalgorithms::count(vec, 1); 466 | ``` 467 | 468 | ``` 469 | std::vector vec{1, 2, 1, 3, 2, 1, 5}; 470 | auto result = kdalgorithms::count_if(vec, [](int i) { return i > 2; }); 471 | ``` 472 | 473 | See [std::count](https://en.cppreference.com/w/cpp/algorithm/count) and [std::count_if](https://en.cppreference.com/w/cpp/algorithm/count_if) for the algorithm from the standard. 474 | 475 | min_value / max_value (C++17) 476 | -------------------------------------------------------------- 477 | [std::min](https://en.cppreference.com/w/cpp/algorithm/min) and [std::max](https://en.cppreference.com/w/cpp/algorithm/max) 478 | can compare two values, and in addition to that it can find the smallest/largest item in a initializer_list. On the other hand, 479 | [std::min_element](https://en.cppreference.com/w/cpp/algorithm/min_element) and [std::max_element](https://en.cppreference.com/w/cpp/algorithm/max_element) 480 | can search for items in generic containers, but returns an iterator 481 | 482 | Our version works on general containers and returns a [std::optional](https://en.cppreference.com/w/cpp/utility/optional) with the item. 483 | 484 | ``` 485 | std::vector ints{4,1,3,2}; 486 | std::optional item = kdalgorithms::max_value(ints); 487 | // item.value() = 4 488 | 489 | std::vector ints{}; 490 | std::optional item = kdalgorithms::max_value(ints); 491 | // item.has_value() = false 492 | ``` 493 | 494 | It is also possible to provide a comparison function: 495 | 496 | ``` 497 | std::vector ints{4,1,3,2}; 498 | auto result = kdalgorithms::min_value(int_vector, std::greater()); 499 | // result.value() == 4 500 | ``` 501 | 502 | OK, I admit, that's a silly example. 503 | 504 | See [std::min_element](https://en.cppreference.com/w/cpp/algorithm/min_element) and [std::max_element](https://en.cppreference.com/w/cpp/algorithm/max_element) for the algorithm from the standard. 505 | 506 | 507 | min_value_greater_than | max_value_less_than | max_value_less_than_unordered | min_value_greater_than_unordered (C++17) 508 | ------------------------------------------------------------------------------------------------ 509 | When searching for the minimum value greater than a specific item or when searching for the maximum value 510 | less than a specific item, the algorithms lower_bound and upper_bound comes into play. 511 | 512 | They can, however, be hard to wrap your head around, which is why this library provides the two 513 | most common use cases above. 514 | Both functions returns a [std::optional](https://en.cppreference.com/w/cpp/utility/optional). 515 | 516 | ``` 517 | std::vector ints{1,2,3,4}; 518 | std::vector unsortedInts{2,4,1,3}; 519 | std::optional result = kdalgorithms::max_value_less_than(ints, 4); 520 | // result.value() = 3 521 | std::optional resultForUnsortedContainer = kdalgorithms::max_value_less_than_unordered(unsortedInts, 4); 522 | // max_value_less_than_unordered works on the unsorted container and it has O(n) complexity, so use it as per your use case :) 523 | // resultForUnsorted.value() = 3; 524 | 525 | result = kdalgorithms::max_value_less_than(ints, -1); 526 | // result.has_value() = false 527 | ``` 528 | 529 | The algorithms may also take a comparison function: 530 | 531 | ``` 532 | struct S 533 | { 534 | int x; 535 | int y; 536 | }; 537 | 538 | std::vector vec{{1, 4}, {2, 3}, {3, 2}, {4, 1}}; 539 | auto compare = [](const S &v1, const S &v2) { return v1.x < v2.x; }; 540 | 541 | auto result = kdalgorithms::max_value_less_than(vec, S{4, 4}, compare); 542 | // result == S{3,2} 543 | ``` 544 | 545 | *Observe, that due to the usage of std::optional, the above functions are only available when 546 | compiling with C++17 or above.* 547 | 548 | is_permutation 549 | ------------------------------------------ 550 | ``` 551 | std::vector x{1,2,3,4} 552 | std::vector y{4, 1, 3, 2}; 553 | bool b = kdalgorithms::is_permutation(x,y)); 554 | // b = true 555 | ``` 556 | 557 | See [std::is_permutation](https://en.cppreference.com/w/cpp/algorithm/is_permutation) for the algorithm from the standard. 558 | 559 | accumulate 560 | ----------------------------------- 561 | The simplest form of accumulate takes a collection, and applies *plus* on the items: 562 | 563 | ``` 564 | std::vector ints{1,2,3,4}; 565 | auto result = kdalgorithms::accumulate(ints); 566 | // result = 10 567 | ``` 568 | 569 | A much more common usage is to specify a mapping function: 570 | 571 | ``` 572 | std::vector ints{1,2,3,4}; 573 | auto sum_doubles = [](int subResult, int x) { return subResult + x * x; }; 574 | int result = kdalgorithms::accumulate(ints, sum_doubles); 575 | // result = 1*1 + 2*2 + 3*3 + 4*4 576 | ``` 577 | 578 | Observe that the initial value doesn't need to be provided in the above. 579 | If you need to specify it, you can do so as an optional third parameter: 580 | 581 | ``` 582 | std::vector ints{1,2,3,4}; 583 | auto factorial = [](int sub_total, int value) { return sub_total * value; }; 584 | auto result = kdalgorithms::accumulate(int_vector, factorial, 1); 585 | // result = 1*1*2*3*4 586 | ``` 587 | 588 | The initial value is the default value for the return type of the mapping function. 589 | This value must, however, be deducable without having to instantiate the arguments to the lambda expression. 590 | That, for example, isn't the case for this function: 591 | 592 | ``` 593 | auto to_comma_seperated_string = [](auto sub_total, int i) { 594 | return sub_total + "," + QString::number(i); 595 | }; 596 | ``` 597 | 598 | Here, the type of the function is the type of calling opertor+ on whatever the type of sub_total is. 599 | In such cases, where you are using auto in the lambda expression, you have to provide an initial value: 600 | 601 | ``` 602 | std::vector ints{1,2,3,4}; 603 | auto to_comma_seperated_string = [](auto sub_total, int i) { 604 | return sub_total + "," + QString::number(i); 605 | }; 606 | auto result = kdalgorithms::accumulate(int_vector, to_comma_seperated_string, QString("0")); 607 | // result = "0,1,2,3,4" 608 | ``` 609 | 610 | ### Accumulate and maps 611 | 612 | It is also possible to use accumulate with maps as this example shows: 613 | 614 | ``` 615 | std::map map{{1, 10}, {2, 20}, {3, 30}, {4, 40}}; 616 | int result = 617 | kdalgorithms::accumulate(map, [](int res, const std::pair &pair) { 618 | return res + pair.first * pair.second; 619 | }); 620 | // result = 1*10 + 2*20 + 3*30 + 4*40 621 | ``` 622 | 623 | Notice, however, that you in the above need to explicitly specify the type of the second parameter to the lambda expression, 624 | which admittedly is a bit cumbersome, so in those cases, it is easier to simply specify the initial value: 625 | 626 | ``` 627 | std::map map{{1, 10}, {2, 20}, {3, 30}, {4, 40}}; 628 | int result = kdalgorithms::accumulate( 629 | map, [](int res, const auto &pair) { return res + pair.first * pair.second; }, 0); 630 | ``` 631 | 632 | See [std::accumulate](https://en.cppreference.com/w/cpp/algorithm/accumulate) for the algorithm from the standard. 633 | 634 | ### Accumulating with a builder object 635 | 636 | Given that accumulate accepts pointer to member functions as argument, a neat way to build the result is using a builder object: 637 | 638 | ``` 639 | struct ResultBuilder 640 | { 641 | ResultBuilder &append(const std::string &other) 642 | { 643 | result += "/" + other; 644 | return *this; 645 | } 646 | std::string result; 647 | }; 648 | 649 | { 650 | std::vector list{"abc", "def", "hij"}; 651 | auto result = kdalgorithms::accumulate(list, &ResultBuilder::append, ResultBuilder()); 652 | result.result = "/abc/def/hij"; 653 | } 654 | ``` 655 | 656 | 657 | accumulate_if 658 | ----------------------------------------- 659 | This is similar to accumulate, with the addition that it only accumulate if the provided predicate evaluates to true. 660 | 661 | ``` 662 | std::vector ints{1,2,3,4}; 663 | auto sumDoubles = [](int x, int y) { return x + y * y; }; 664 | auto greaterThan = [](int value) { return [value](int test) { return test > value; }; }; 665 | int result = kdalgorithms::accumulate_if(ints, sumDoubles, greatherThan(2)); 666 | // result = 25 667 | ``` 668 | 669 | sum / sum_if 670 | ------------------------------ 671 | The accumulate function above allows you to specify an alternative to **operator+**, however 672 | in most cases the only reason you have for that is to extract a value from a struct or class, as is the case here: 673 | ``` 674 | struct Struct 675 | { 676 | int key; 677 | int value; 678 | }; 679 | const std::vector structVec{{1, 4}, {2, 3}, {3, 2}, {4, 1}}; 680 | auto result = kdalgorithms::accumulate( structVec, 681 | [](int subResult, const Struct &s) { return subResult + s.value; }, 682 | 0); 683 | // result is now 10 684 | ``` 685 | 686 | This is cumbersome to the extend that a regular for loop is more readable. To the rescue comes **sum** and **sum_if**, 687 | which instead of a function to build the result, takes a function to extract the values to be summed: 688 | 689 | ``` 690 | // Same Struct as above 691 | auto result = kdalgorithms::sum(structVec, [](const Struct &s) { return s.value; }); 692 | ``` 693 | 694 | Taking advantage of the fact that kdalgorithms allows you to specify a pointer to a member variable in place of the function above, 695 | reduced the code to this: 696 | 697 | ``` 698 | auto result = kdalgorithms::sum(structVec, &Struct::value); 699 | ``` 700 | 701 | Similar to accumulate_if, there is a version of **sum** called **sum_if**, which takes a predicate, to 702 | decide if a given item should be included in the result: 703 | 704 | ``` 705 | auto result = kdalgorithms::sum_if(structVec, &Struct::value, 706 | [](const Struct &s) { return s.key > 1; }); 707 | // result is now 6 708 | ``` 709 | 710 | get_match (C++17) / get_match_or_default 711 | ------------------------------------------------- 712 | This function exist in two variants, they differ on what they do in case the item searched for 713 | doesn't exist in the collection. **get_match** returns an optional (and thus requires C++17), 714 | while **get_match_or_default** returns a default constructed item. 715 | 716 | ``` 717 | struct Struct 718 | { 719 | int key; 720 | int value; 721 | }; 722 | std::vector vec { ... }; 723 | 724 | std::optional result1 725 | = kdalgorithms::get_match(vec, [] (Struct s) { return s.key == 42; }; 726 | 727 | Struct result2 728 | = kdalgorithms::get_match_or_default(vec, [] (Struct s) { return s.key == 42; }; 729 | ``` 730 | 731 | index_of_match 732 | ------------------------------------------- 733 | This function returns the index of the first match in the given container 734 | 735 | ``` 736 | std::vector vec{{1, 2}, {2, 1}, {3, 3}, {4, 4}}; 737 | result = kdalgorithms::index_of_match(vec, &Struct::hasEqualKeyValuePair); 738 | // result = 2 739 | ``` 740 | 741 | remove_duplicates 742 | ------------------------------------- 743 | **remove_duplicates** takes a collection and removes duplicates. 744 | It does so inline (that is, it doesn't return a new collection). 745 | 746 | The second parameter tells whether the collection should be sorted first. 747 | 748 | ``` 749 | std::vector vec{3, 1, 2, 2, 1}; 750 | kdalgorithms::remove_duplicates(vec, kdalgorithms::do_not_sort); 751 | // vec = {3, 1, 2, 1} 752 | ``` 753 | 754 | ``` 755 | std::vector vec{3, 1, 2, 2, 1}; 756 | kdalgorithms::remove_duplicates(vec, kdalgorithms::do_sort); 757 | // vec = {1, 2, 3} 758 | ``` 759 | 760 | See [std::unique](https://en.cppreference.com/w/cpp/algorithm/unique) for the algorithm from the standard. 761 | 762 | has_duplicates 763 | ------------------------------------------- 764 | **has_duplicates** takes a collection (which doesn't need to be ordered), and tells whether there are any duplicates in there. 765 | 766 | ``` 767 | std::vector vec{3, 1, 2, 1}; 768 | auto result = kdalgorithms::has_duplicates(vec, kdalgorithms::do_sort); 769 | // result = true 770 | ``` 771 | 772 | erase / erase_if 773 | --------------------------------------- 774 | **erase** removes all instances of a given value, while **erase_if** remove all instances matching a predicate. 775 | 776 | ``` 777 | std::vector vec{1, 2, 1, 3}; 778 | kdalgorithms::erase(vec, 1); 779 | // vec = {2,3} 780 | ``` 781 | 782 | ``` 783 | struct Struct 784 | { 785 | int key; 786 | int value; 787 | }; 788 | auto with_key = [](int key) { return [key](const Struct &s) { return s.key == key; }; }; 789 | 790 | std::vector vec{{2, 3}, {1, 4}, {2, 2}, {4, 1}}; 791 | kdalgorithms::erase_if(vec, with_key(2)); 792 | // vec = {{1,4}, {4,1}} 793 | ``` 794 | 795 | - See [std::remove / std::remove_if](https://en.cppreference.com/w/cpp/algorithm/remove) for the algorithm from the standard, 796 | - See [std::ranges::remove / std::ranges::remove_if](https://en.cppreference.com/w/cpp/algorithm/ranges/remove) for the C++20 ranges implementation 797 | - See [std::erase / std::erase_if](https://en.cppreference.com/w/cpp/container/vector/erase2) for the C++20 implementation. 798 | 799 | iota 800 | ----------------------- 801 | Generate a container with a number of elements in incremental order 802 | 803 | ``` 804 | std::vector result = kdalgorithms::iota(10, 5); 805 | // result = {10, 11, 12, 13, 14}; 806 | ``` 807 | 808 | *iota* might also be used with just a single argument, in which case it generates a container of that many elements starting at 0: 809 | 810 | ``` 811 | std::vector result = kdalgorithms::iota(5); 812 | // result = {0, 1, 2, 3, 4}; 813 | ``` 814 | If you are familiar with Python, then this one argument version is equivalent to the python expression "`range(5)`" 815 | 816 | See [std::iota](https://en.cppreference.com/w/cpp/algorithm/iota) for the algorithm from the standard. 817 | 818 | generate_n 819 | ------------------------------ 820 | This function generates n elements into an existing container by calling a generator function. 821 | 822 | ``` 823 | std::unordered_set result; 824 | kdalgorithms::generate_n(result, 5, [](int index) { return index*index; }); 825 | // result = {0, 1, 4, 9, 16}; 826 | ``` 827 | 828 | Observe: In contrast to [std::generate_n](https://en.cppreference.com/w/cpp/algorithm/generate_n) our version's generate function can take the index as a parameter. 829 | In other words kdalgorithms::generate_n will call operator(int) if it exists, and otherwise call operator(). 830 | 831 | So the following version also works, and is closer to the behavior of std::generate_n: 832 | 833 | ``` 834 | std::vector result; 835 | kdalgorithms::generate_n(result, 5, [i = 0]() mutable { return ++i; }); 836 | // result = {1, 2, 3, 4, 5}; 837 | ``` 838 | 839 | See [std::generate_n](https://en.cppreference.com/w/cpp/algorithm/generate_n) for the algorithm from the standard, 840 | 841 | generate_until 842 | ------------------------------- 843 | The function produces a container of items generated by a generator. 844 | The generator will continue until it produces an empty element. 845 | 846 | ####Example with std::optional (requires C++17) 847 | 848 | ``` 849 | auto generator = [x = 0]() mutable -> std::optional { 850 | if (x < 4) { 851 | ++x; 852 | return x*x; 853 | } 854 | return {}; 855 | }; 856 | 857 | std::vector result = kdalgorithms::generate(generator); 858 | // result = {1, 4, 9, 16} 859 | ``` 860 | 861 | ####Example with a unique_ptr (works with C++14) 862 | 863 | ``` 864 | auto generator = [x = 0]() mutable -> std::unique_ptr { 865 | if (x < 4) { 866 | ++x; 867 | return std::make_unique(x * x); 868 | } 869 | return {}; 870 | }; 871 | 872 | auto result = kdalgorithms::generate_until(generator); 873 | // result = {1, 4, 9, 16} 874 | ``` 875 | 876 | Similar to transform, the return container can be specified as a template parameter: 877 | 878 | ``` 879 | auto result = kdalgorihms::generate(generator); 880 | ``` 881 | 882 | It is also possible to use your own type as return type from the generator, in which case they must supply these two: 883 | 884 | ``` 885 | int operator*(); 886 | operator bool(); 887 | ``` 888 | See the unit tests for an example implementation. 889 | 890 | See [std::generate](https://en.cppreference.com/w/cpp/algorithm/generate) for the algorithm from the standard, 891 | 892 | for_each 893 | ------------------------------- 894 | This algorithm invokes a specific function on each element of a container. 895 | 896 | ``` 897 | kdalgorithms::for_each(structVec, &Struct::print); 898 | ``` 899 | 900 | I'm sure there are good and valid usecases for using for_each, but will nevertheless claims that in 98% of the cases there are alternative algorithms which are a better match. 901 | 902 | See [std::for_each](https://en.cppreference.com/w/cpp/algorithm/for_each) for the algorithm from the standard, 903 | 904 | partitioned 905 | --------------------------------- 906 | partitioned takes a container and a predicate, and returns two new containers, the first with the items matching the predicate, the second with the items not matching. 907 | 908 | ``` 909 | std::vector vec{4, 1, 3, 2}; 910 | const auto result = kdalgorithms::partition(vec, [](int i) { return i > 2; }); 911 | // result.in = {4, 3}, result.out = {1, 2} 912 | ``` 913 | 914 | If your compiler supports C++17, then structural bindings may make the above slightlt more readable. 915 | 916 | ``` 917 | std::vector vec{4, 1, 3, 2}; 918 | const auto& [in, out] = kdalgorithms::partition(vec, [](int i) { return i > 2; }); 919 | // in = {4, 3}, out = {1, 2} 920 | ``` 921 | 922 | If the container provided is an x-value (expiring value), then the items will be moved over rather than copied over. 923 | 924 | Similar to transformed, it is possible to change the container type: 925 | 926 | ``` 927 | std::vector vec{4, 1, 3, 2}; 928 | const auto& [in, out] = 929 | kdalgorithms::partitioned(vec, [](int i) { return i > 2; }); 930 | // in = std::unordered_set{4, 3}, out = std::unordered_set{1, 2} 931 | ``` 932 | 933 | See [std::partition](https://en.cppreference.com/w/cpp/algorithm/partition) for the algorithm from the standard. 934 | 935 | multi_partitioned 936 | ------------------------------------------------- 937 | While the partitioned algorithm splits a container into two parts, the multi_partitioned algorithm splits a 938 | sequential container into a map based on a key function. 939 | 940 | ``` 941 | struct Person 942 | { 943 | QString name; 944 | int age; 945 | }; 946 | 947 | auto partitioningFunction = [](const Person &p) { 948 | auto floor = 10 * (p.age / 10); 949 | return QString("%1-%2").arg(floor).arg(floor + 9); 950 | }; 951 | 952 | auto result = kdalgorithms::multi_partitioned(people, partitioningFunction); 953 | // result is now a std::map> with these entries; 954 | // {"40-49", {{"Ivan", 42}, {"Till", 44}}} 955 | // {"50-59", {{"Jesper", 52}, {"Kalle", 53}}} 956 | ``` 957 | 958 | As always, the items will be moved over if they are x-values. Also, you may use the member variables as the split function: 959 | 960 | ``` 961 | auto result = kdalgorithms::multi_partitioned(people, &Person::age); 962 | ``` 963 | 964 | Observe: There are no standard algorithms matching this one. 965 | 966 | 967 | zip 968 | --------------------- 969 | zip takes a number of containers and return one container with tuples for each of the items in the input containers. 970 | 971 | ``` 972 | std::vector x{1, 2, 3}; 973 | std::deque y{'a', 'b', 'c'}; 974 | std::list x{"hello", "kdalgorithms", "world"}; 975 | 976 | auto result = kdalgorithms::zip(x, y, z); 977 | // result is a std::vector> 978 | // and the values are {1, 'a', "hello"}, {2, 'b', "kdalgorithms"}, {3, 'c', "world"} 979 | ``` 980 | 981 | As all other algorithms, the items will be moved over if the input container is an xvalue (an expiring value which can be stolen). 982 | Further it is possible to change the result container: 983 | 984 | ``` 985 | auto result = kdalgorithms::zip(v1, v2); 986 | ``` 987 | 988 | See [boost::compine](https://www.boost.org/doc/libs/1_81_0/libs/range/doc/html/range/reference/utilities/combine.html) for similar algorithm in boost, and [std::ranges::views::zip](https://en.cppreference.com/w/cpp/ranges/zip_view) for the C++23 version. 989 | 990 | 991 | cartesian_product 992 | ----------------------------- 993 | cartesian_product takes a number of containers and returns a cartesian product of the items. 994 | 995 | ``` 996 | const std::array x = {'A', 'B'}; 997 | const std::vector y = {1, 2, 3}; 998 | const std::list z = {"α", "β", "γ", "δ"}; 999 | 1000 | auto result = kdalgorithms::product(x, y, z); 1001 | // result is: 1002 | // std::vector>{ 1003 | // {'A', 1, "α"}, {'A', 1, "β"}, {'A', 1, "γ"}, {'A', 1, "δ"}, 1004 | // {'A', 2, "α"}, {'A', 2, "β"}, {'A', 2, "γ"}, {'A', 2, "δ"}, 1005 | // {'A', 3, "α"}, {'A', 3, "β"}, {'A', 3, "γ"}, {'A', 3, "δ"}, 1006 | // {'B', 1, "α"}, {'B', 1, "β"}, {'B', 1, "γ"}, {'B', 1, "δ"}, 1007 | // {'B', 2, "α"}, {'B', 2, "β"}, {'B', 2, "γ"}, {'B', 2, "δ"}, 1008 | // {'B', 3, "α"}, {'B', 3, "β"}, {'B', 3, "γ"}, {'B', 3, "δ"}, 1009 | // }; 1010 | ``` 1011 | 1012 | It is also possible to specify the return type: 1013 | ``` 1014 | auto result = kdalgorithms::cartesian_product( x, y, z ); 1015 | // result is: 1016 | // std::deque>{ ... } 1017 | ``` 1018 | 1019 | See [std::cartesian_product](https://en.cppreference.com/w/cpp/ranges/cartesian_product_view) 1020 | -------------------------------------------------------------------------------- /Documentation/deploying.md: -------------------------------------------------------------------------------- 1 | Deploying KDAlgorithms with CMake 2 | ================================= 3 | 4 | A minimal CMake project showing the deployment of KDAlgorithms can be found in the *Example* subdirectory. 5 | What it basically shows is that you need to add something along these lines to your CMakeLists.txt file: 6 | 7 | ``` 8 | add_subdirectory(3rdparty/kdalgorithms) 9 | target_link_libraries(Example kdalgorithms) 10 | ``` 11 | 12 | Where you replace *Example* with whatever your build target is called. 13 | 14 | Then in your sources, you simply **#include ** 15 | 16 | That's all. 17 | 18 | -------------------------------------------------------------------------------- /Documentation/inspiration.md: -------------------------------------------------------------------------------- 1 | Just for Inspiration 2 | ==================== 3 | This page contains a number of (well one for now) examples, where you may use algorithms to write clearer code. 4 | 5 | It took me (Jesper) quite a long time before I could really appreciate usage of algorithms, and furthermore 6 | I was often struggling to find the right one to use. 7 | I was also often paralyzed when I had to chose between two solutions to the same problem: 8 | one using a relative simple for loop and one which was more complex but using algorithms. 9 | 10 | Today, I have settled with the understanding that algorithms is a means to an end, not the end itself. 11 | Their primary purpose is to make the code more expressive - so instead of having to read through a for loop to understand exactly what it is building up, I can see that it for example filters out some items, and transform the result to another format. 12 | 13 | 14 | Traversing a list which do not provide iterators 15 | ------------------------------------------------ 16 | (Example in Inspiration/qlistview_all_columns_selected.cpp) 17 | 18 | Sometimes you want to filter, transform, or apply any other algorithm to a *list* provided to you via an API which do *not* offer iterators, and which you therefore *can not* apply algorithms on. 19 | 20 | One solution is of course to create a wrapper object providing an iterator API, but as this solution shows, another solution is to simply create a list of indexes to iterate. 21 | 22 | ### The Problem 23 | Imagine you want to check if all columns of a given row in a table (here QTableWidget) is selected. This looks a lot like all_of, doesn't it? 24 | 25 | To get to the selected items, you will have to ask the table for its QItemSelectionModel, which in turn, naturally does not offer a *are all columns for a given row selected* API, nor does it offer an iterator to traverse all columns for a given row (which would have been a really weird API to begin with, if it did). 26 | 27 | What the selection model does offer you is to answer yes or no to the question is a given (row, column) selected. 28 | 29 | A solution often looks like this: 30 | 31 | ``` 32 | bool allColumnsSelected = true; 33 | int row = m_table->currentRow(); 34 | auto selectionModel = m_table->selectionModel(); 35 | auto model = m_table->model(); 36 | for (int i = 0; i < m_table->columnCount(); ++i) { 37 | if (!selectionModel->isSelected(model->index(row, i))) { 38 | allColumnsSelected = false; 39 | break; 40 | } 41 | } 42 | 43 | if (allColumnsSelected) 44 | doSomething(); 45 | ``` 46 | 47 | The code is proper Qt code, and might not be super readable to you if you are not a Qt developer, but even if you are, you need to read all lines carefully to understand it. The key part in the above is the explicit for loop over all columns. 48 | 49 | Here is a solution using KDAlgorithms, and more specifically **all_of**: 50 | 51 | ``` 52 | auto columns = kdalgorithms::iota(m_table->columnCount()); 53 | auto isColumnSelected = [row = m_table->currentRow(), 54 | selectionModel = m_table->selectionModel(), 55 | model = m_table->model()](int column) { 56 | return selectionModel->isSelected(model->index(row, column)); 57 | }; 58 | 59 | if (kdalgorithms::all_of(columns, isColumnSelected)) 60 | doSomething(); 61 | ``` 62 | 63 | The trick is to iterate (implicitly inside the algorithms) over the list of indexes for the columns. We do so by first fetching a list of all indexes (the variable *columns*). Following that we create a lambda expression to answer if a given column is selected, and now the test is super readable. 64 | 65 | ### Some after thoughts 66 | If you have that feeling that you have seen this trick before, namely creating a list of the indexes and operating on that, then chances are you occasionally write Python code. In python you often find code like: 67 | 68 | ``` 69 | vector = [1, 2, 3, 4, 5] 70 | for i in range(len(vector)): 71 | if vector[i] % 2 == 1: 72 | vector[i] = 0 73 | ``` 74 | 75 | -------------------------------------------------------------------------------- /Example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(Example) 3 | set(CMAKE_CXX_STANDARD 14) 4 | 5 | add_executable(Example main.cpp) 6 | 7 | add_subdirectory(.. kdalgorithmsbuild) 8 | # Would normally be something like 9 | # add_subdirectory(3rdparty/kdalgorithms) 10 | target_link_libraries(Example kdalgorithms) 11 | -------------------------------------------------------------------------------- /Example/main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** This file is part of KDAlgorithms 4 | ** 5 | ** SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company 6 | ** 7 | ** SPDX-License-Identifier: MIT 8 | ** 9 | ****************************************************************************/ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | int main() 17 | { 18 | auto vec = kdalgorithms::iota(1, 100); 19 | auto odds = kdalgorithms::filtered(vec, [](int i) { return i % 2 == 1; }); 20 | auto result = kdalgorithms::accumulate(odds, [](const std::string &partial, int value) { 21 | return partial + "," + std::to_string(value); 22 | }); 23 | 24 | std::cout << result << "\n"; 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /Inspiration/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(Inspiration) 3 | 4 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 5 | find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Gui) 6 | find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED COMPONENTS Core Gui Widgets) 7 | set(CMAKE_AUTOMOC TRUE) 8 | set(CMAKE_AUTORCC TRUE) 9 | 10 | 11 | add_executable(qlistview_all_columns_selected 12 | qlistview_all_columns_selected.cpp 13 | ) 14 | target_link_libraries(qlistview_all_columns_selected Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) 15 | -------------------------------------------------------------------------------- /Inspiration/qlistview_all_columns_selected.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** This file is part of KDAlgorithms 4 | ** 5 | ** SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company 6 | ** 7 | ** SPDX-License-Identifier: MIT 8 | ** 9 | ****************************************************************************/ 10 | 11 | #include "../src/kdalgorithms.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | /* 19 | For details see https://github.com/KDAB/KDAlgorithms/blob/main/Documentation/inspiration.md 20 | */ 21 | class Test : public QWidget 22 | { 23 | public: 24 | Test() 25 | { 26 | auto layout = new QVBoxLayout(this); 27 | 28 | m_table = new QTableWidget(5, 5); 29 | layout->addWidget(m_table); 30 | 31 | m_label1 = new QLabel("Select Something"); 32 | layout->addWidget(m_label1); 33 | connect(m_table, &QTableWidget::currentCellChanged, this, &Test::updateLabelV1); 34 | 35 | m_label2 = new QLabel("Select Something"); 36 | layout->addWidget(m_label2); 37 | connect(m_table, &QTableWidget::currentCellChanged, this, &Test::updateLabelV2); 38 | 39 | m_label3 = new QLabel("Select Something"); 40 | layout->addWidget(m_label3); 41 | connect(m_table, &QTableWidget::currentCellChanged, this, &Test::updateLabelV3); 42 | } 43 | 44 | private: 45 | /* The normal implementation using for loops */ 46 | void updateLabelV1() 47 | { 48 | bool allColumnsSelected = true; 49 | int row = m_table->currentRow(); 50 | auto selectionModel = m_table->selectionModel(); 51 | auto model = m_table->model(); 52 | for (int i = 0; i < m_table->columnCount(); ++i) { 53 | if (!selectionModel->isSelected(model->index(row, i))) { 54 | allColumnsSelected = false; 55 | break; 56 | } 57 | } 58 | 59 | if (allColumnsSelected) 60 | m_label1->setText("All columns selected"); 61 | else 62 | m_label1->setText("Not all columns selected"); 63 | } 64 | 65 | /* The improved version using algorithms */ 66 | void updateLabelV2() 67 | { 68 | auto columns = kdalgorithms::iota(m_table->columnCount()); 69 | auto isColumnSelected = [row = m_table->currentRow(), 70 | selectionModel = m_table->selectionModel(), 71 | model = m_table->model()](int column) { 72 | return selectionModel->isSelected(model->index(row, column)); 73 | }; 74 | 75 | if (kdalgorithms::all_of(columns, isColumnSelected)) 76 | m_label2->setText("All columns selected"); 77 | else 78 | m_label2->setText("Not all columns selected"); 79 | } 80 | 81 | /* The version I would have written without algorithms */ 82 | void updateLabelV3() 83 | { 84 | bool allColumnsSelected = [this] { 85 | int row = m_table->currentRow(); 86 | auto selectionModel = m_table->selectionModel(); 87 | auto model = m_table->model(); 88 | for (int i = 0; i < m_table->columnCount(); ++i) { 89 | if (!selectionModel->isSelected(model->index(row, i))) { 90 | return false; 91 | } 92 | } 93 | return true; 94 | }(); 95 | 96 | if (allColumnsSelected) 97 | m_label3->setText("All columns selected"); 98 | else 99 | m_label3->setText("Not all columns selected"); 100 | } 101 | 102 | QTableWidget *m_table; 103 | QLabel *m_label1; 104 | QLabel *m_label2; 105 | QLabel *m_label3; 106 | }; 107 | 108 | int main(int argc, char **argv) 109 | { 110 | QApplication app(argc, argv); 111 | 112 | Test test; 113 | test.resize(600, 300); 114 | test.show(); 115 | app.exec(); 116 | } 117 | -------------------------------------------------------------------------------- /KDAlgorithmsConfig.cmake.in: -------------------------------------------------------------------------------- 1 | 2 | @PACKAGE_INIT@ 3 | 4 | include("${CMAKE_CURRENT_LIST_DIR}/KDAlgorithmsTargets.cmake") 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | KDAlgorithms is Copyright 2022 Klarälvdalens Datakonsult AB (KDAB), 4 | and is available under the terms of the MIT license. 5 | 6 | See the full license text in the LICENSES folder. 7 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Algorithm helpers - kdalgorithms 2 | ================================ 3 | 4 | The purpose of this library is to make it easier to work with algorithms in C++14 and up. 5 | 6 | * Documentation of all the algorithms 7 | * Deploying the algorithms with CMake 8 | * Get inspired by some real world examples 9 | * Get inspired by all the unit tests 10 | * Youtube video introducing the library 11 | * A blog post on KDAlgorithms 12 | * Change Log 13 | 14 | Example - filtered 15 | ------------------ 16 | 17 | With C++11 algorithms you might write: 18 | 19 | ``` 20 | std::vector result; 21 | auto is_odd = [] (int i) {return i % 2 == 1; }; 22 | std::copy_if(my_list.cbegin(), my_list.cend(), std::back_inserter(result), is_odd); 23 | ``` 24 | 25 | With this library you can instead write: 26 | 27 | ``` 28 | auto is_odd = [] (int i) {return i % 2 == 1; }; 29 | auto result = kdalgorithms::filtered(my_list, is_odd); 30 | ``` 31 | 32 | Observe that it: 33 | 34 | 1. takes a complete collection rather than an iterator pairs 35 | 2. returns the result rather than taking an iterator to where it should go 36 | 37 | Example - max_value_less_than 38 | ----------------------------- 39 | 40 | When searching in a collection for the largest element no larger than a given value, 41 | a good algorithmic choice is std::upper_bound, or ... is it std::lower_bound, and what exactly does is return? 42 | 43 | Here is how to do it with this library: 44 | 45 | ``` 46 | std::optional result = kdalgorithms::max_value_less_than(int_vector, 4); 47 | ``` 48 | 49 | By returning an optional rather than an iterator, you do not need to worry whether you should check it against 50 | int_vector.begin() or int_vector.cbegin(). It is also more obvious that the value might be non-existent - 51 | after all, that is what std::optional is all about. 52 | 53 | Combining Algorithms 54 | -------------------- 55 | kdalgorithms offers and, or and not operators, which makes it possible to write code like this: 56 | 57 | ``` 58 | using namespace kdalgorithms::Operators; 59 | std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 60 | const auto is_odd = [](int num) { return num % 2 == 1; }; 61 | const auto is_greather_than = [](int num) { return [num](int value) { return value > num; }; }; 62 | const auto is_dividable_by = [](int num) { 63 | return [num](int value) { return value % num == 0; }; 64 | }; 65 | 66 | auto result = kdalgorithms::filtered(vec, is_odd || (is_greather_than(5) && !is_dividable_by(3))); 67 | ``` 68 | 69 | Using member functions as predicates 70 | ------------------------------------ 71 | Throughout the documentation we will used lambda expressions for predicates and comparison methods 72 | given to algorithms. It is, however, also possible to use pointer to member functions: 73 | 74 | ``` 75 | struct S 76 | { 77 | int x; 78 | int y; 79 | bool hasEqualKeyValuePair() const { return x == y; } 80 | }; 81 | std::vector vec{{2, 3}, {1, 1}, {2, 2}, {4, 1}}; 82 | kdalgorithms::erase_if(vec, &Struct::hasEqualKeyValuePair); 83 | ``` 84 | 85 | Be aware though that the method must be const. 86 | 87 | Lots of Examples 88 | ---------------- 89 | In this document you will find lots of examples. More, however, may be found in the 90 | unit tests. 91 | 92 | Qt 93 | -- 94 | kdalgorithms supports all of Qt's containers, with the exception that with Qt5 QSet isn't supported. 95 | 96 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | SPDX-PackageName = "KDAlgorithms" 3 | SPDX-PackageSupplier = "" 4 | SPDX-PackageDownloadLocation = "https://www.github.com/KDAB/KDAlgorithms" 5 | 6 | [[annotations]] 7 | path = ["run", ".gitignore", "CMakeLists.txt", "KDAlgorithmsConfig.cmake.in", "CMakePresets.json", "README.md", "_clang-format", ".pre-commit-config.yaml", "appveyor.yml", "Documentation/**", "Example/CMakeLists.txt", "Inspiration/CMakeLists.txt", "tests/test_install/CMakeLists.txt", "REUSE.toml"] 8 | precedence = "aggregate" 9 | SPDX-FileCopyrightText = "2022 Klarälvdalens Datakonsult AB, a KDAB Group company " 10 | SPDX-License-Identifier = "MIT" 11 | -------------------------------------------------------------------------------- /_clang-format: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Olivier Goffart 2 | # 3 | # You may use this file under the terms of the 3-clause BSD license. 4 | # See the file LICENSE from this package for details. 5 | 6 | # This is the clang-format configuration style to be used by Qt, 7 | # based on the rules from https://wiki.qt.io/Qt_Coding_Style and 8 | # https://wiki.qt.io/Coding_Conventions 9 | 10 | --- 11 | # Webkit style was loosely based on the Qt style 12 | BasedOnStyle: WebKit 13 | 14 | Standard: Cpp11 15 | ColumnLimit: 100 16 | 17 | # Disable reflow of qdoc comments: indentation rules are different. 18 | # Translation comments are also excluded 19 | CommentPragmas: "^!|^:" 20 | 21 | # We want a space between the type and the star for pointer types 22 | PointerBindsToType: false 23 | 24 | # We want to break before the operators, but not before a '=' 25 | BreakBeforeBinaryOperators: NonAssignment 26 | 27 | # Braces are usually attached, but not after functions or classes declaration 28 | BreakBeforeBraces: Custom 29 | BraceWrapping: 30 | AfterClass: true 31 | AfterControlStatement: false 32 | AfterEnum: false 33 | AfterFunction: true 34 | AfterNamespace: false 35 | AfterObjCDeclaration: false 36 | AfterStruct: true 37 | AfterUnion: false 38 | BeforeCatch: false 39 | BeforeElse: false 40 | IndentBraces: false 41 | Cpp11BracedListStyle: true 42 | SpaceBeforeCpp11BracedList: false 43 | 44 | # The coding style does not specify the following, but this is what gives 45 | # results closest to the existing code. 46 | AlignAfterOpenBracket: true 47 | AlwaysBreakTemplateDeclarations: true 48 | 49 | # Ideally we should also allow less short function in a single line, but 50 | # clang-format does not handle that 51 | AllowShortFunctionsOnASingleLine: Inline 52 | 53 | # The coding style specifies some include order categories: 54 | # #include "header_include.h" 55 | # #include "module_includes.h" 56 | # #include 57 | # #include 58 | # #include 59 | SortIncludes: true 60 | 61 | # macros for which the opening brace stays attached 62 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] 63 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | #---------------------------------# 2 | # general configuration # 3 | #---------------------------------# 4 | 5 | # version format 6 | version: 1.0.{build}-{branch} 7 | 8 | # branches to build 9 | branches: 10 | except: 11 | - gh-pages 12 | 13 | # Do not build on tags (GitHub and BitBucket) 14 | skip_tags: false 15 | 16 | #---------------------------------# 17 | # environment configuration # 18 | #---------------------------------# 19 | 20 | # Build worker image 21 | image: 22 | - Ubuntu2004 23 | - macos-monterey 24 | - Visual Studio 2019 25 | 26 | # scripts that are called at very beginning, before repo cloning 27 | init: 28 | - git config --global core.autocrlf input 29 | 30 | #---------------------------------# 31 | # build configuration # 32 | #---------------------------------# 33 | 34 | # build platform, i.e. x86, x64, Any CPU. This setting is optional. 35 | platform: 36 | - x64 37 | 38 | # build Configuration, i.e. Debug, Release, etc. 39 | configuration: 40 | - Release 41 | - Debug 42 | 43 | environment: 44 | matrix: 45 | - preset: cpp14 46 | - preset: cpp17 47 | - preset: cpp20 48 | - preset: cpp23 49 | 50 | #as off 11 December 2022, the default Xcode is v14.1 (should support cpp20 and cpp23 51 | #matrix: 52 | # exclude: 53 | # - image: macos-monterey 54 | # preset: cpp20 55 | # - image: macos-monterey 56 | # preset: cpp23 57 | 58 | install: 59 | - sh: if [ "`uname -s`" = "Darwin" ]; then brew install ninja; else sudo apt-get -y update; fi 60 | 61 | before_build: 62 | - cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" 63 | - cmd: set PATH=C:\Qt\6.2\msvc2019_64\bin;C:\Qt\5.15\msvc2019_64\bin;%PATH% 64 | - sh: if [ "`uname -s`" = "Darwin" ]; then export PATH=$HOME/Qt/6.2/macos/bin:$HOME/Qt/5.15/clang_64/bin:$PATH; else export PATH=$HOME/Qt/6.2/gcc_64/bin:$HOME/Qt/5.15/gcc_64/bin:$PATH; fi 65 | 66 | build_script: 67 | - mkdir build 68 | - cd build 69 | - cmd: cmake -G Ninja -DCMAKE_BUILD_TYPE=%CONFIGURATION% --preset=%preset% .. 70 | - sh: cmake -G Ninja -DCMAKE_BUILD_TYPE=$CONFIGURATION --preset=$preset .. 71 | - cmake --build . 72 | - ctest --test-dir . 73 | 74 | # to disable automatic builds 75 | #build: off 76 | 77 | #---------------------------------# 78 | # tests configuration # 79 | #---------------------------------# 80 | 81 | # to disable automatic tests 82 | test: off 83 | 84 | #---------------------------------# 85 | # deployment configuration # 86 | #---------------------------------# 87 | 88 | deploy: off 89 | 90 | #---------------------------------# 91 | # notifications # 92 | #---------------------------------# 93 | notifications: 94 | # Email 95 | - provider: Email 96 | to: 97 | - allen.winter@kdab.com 98 | - jesper.pedersen@kdab.com 99 | on_build_success: false 100 | on_build_failure: true 101 | -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | if [[ $PATH != *"5.15"* ]]; then 4 | echo "Please setup Qt5, as Qt6 will force C++17" 5 | exit 6 | fi 7 | 8 | rm -rf build 9 | for compiler in gcc clang; do 10 | for cpp in cpp14 cpp17 cpp20 cpp23; do 11 | echo -e "\n\n========= $compiler - $cpp ===========" 12 | cmake --preset $cpp-$compiler . 13 | cd build/$cpp-$compiler 14 | ninja || exit 1 15 | cd ../.. 16 | done 17 | done 18 | 19 | for compiler in gcc clang; do 20 | for cpp in cpp14 cpp17 cpp20 cpp23; do 21 | echo -e "\n\n========= $compiler - $cpp ===========" 22 | cd build/$cpp-$compiler 23 | ./tst_kdalgorithms -silent 24 | 25 | if [ ! $? -eq 0 ]; then 26 | echo -e "\n\n\n FAILURE \n\n\n" 27 | exit 1 28 | fi 29 | 30 | cd ../.. 31 | done 32 | done 33 | -------------------------------------------------------------------------------- /src/kdalgorithms.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** This file is part of KDAlgorithms 4 | ** 5 | ** SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company 6 | ** 7 | ** SPDX-License-Identifier: MIT 8 | ** 9 | ****************************************************************************/ 10 | 11 | #pragma once 12 | 13 | #include "kdalgorithms_bits/cartesian_product.h" 14 | #include "kdalgorithms_bits/filter.h" 15 | #include "kdalgorithms_bits/find_if.h" 16 | #include "kdalgorithms_bits/generate.h" 17 | #include "kdalgorithms_bits/insert_wrapper.h" 18 | #include "kdalgorithms_bits/invoke.h" 19 | #include "kdalgorithms_bits/method_tests.h" 20 | #include "kdalgorithms_bits/operators.h" 21 | #include "kdalgorithms_bits/read_iterator_wrapper.h" 22 | #include "kdalgorithms_bits/reserve_helper.h" 23 | #include "kdalgorithms_bits/return_type_trait.h" 24 | #include "kdalgorithms_bits/shared.h" 25 | #include "kdalgorithms_bits/to_function_object.h" 26 | #include "kdalgorithms_bits/transform.h" 27 | #include "kdalgorithms_bits/zip.h" 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #if __cplusplus >= 201703L 36 | #include 37 | #endif 38 | 39 | namespace kdalgorithms { 40 | 41 | // -------------------- copy -------------------- 42 | template 43 | #if __cplusplus >= 202002L 44 | requires ContainerOfType> 45 | #endif 46 | void copy(InputContainer &&input, OutputContainer &output) 47 | { 48 | bool foundReserve = detail::reserve(output, input.size() + output.size()); 49 | if (!foundReserve && detail::is_same_object(input, output)) { 50 | remove_cvref_t tmp = input; 51 | copy(tmp, output); 52 | return; 53 | } 54 | auto range = read_iterator_wrapper(std::forward(input)); 55 | std::copy(range.begin(), range.end(), detail::insert_wrapper(output)); 56 | } 57 | 58 | // -------------------- copied -------------------- 59 | template 60 | #if __cplusplus >= 202002L 61 | requires ContainerOfType> 62 | #endif 63 | ResultContainer copied(InputContainer &&input) 64 | { 65 | static_assert(!std::is_same::value, 66 | "Use copy constructor instead of kdalgorithms::copied"); 67 | ResultContainer result; 68 | kdalgorithms::copy(std::forward(input), result); 69 | return result; 70 | } 71 | 72 | template