├── .github └── workflows │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── Config.cmake.in ├── LICENSE ├── README.md ├── conanfile.py ├── include └── more_concepts │ ├── associative_containers.hpp │ ├── base_concepts.hpp │ ├── base_containers.hpp │ ├── detail │ ├── mock_iterator.hpp │ └── type_traits.hpp │ ├── mock_iterator.hpp │ ├── more_concepts.hpp │ └── sequence_containers.hpp └── tests ├── CMakeLists.txt └── more_concepts ├── CMakeLists.txt ├── test_containers.cpp ├── test_main.cpp └── test_mock_iterator.cpp /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | BUILD_TYPE: Debug 7 | BUILD_DIR: "${{github.workspace}}/build" 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-20.04 12 | 13 | strategy: 14 | matrix: 15 | include: 16 | - cc: gcc-10 17 | cxx: g++-10 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - name: Create Build Environment 23 | run: cmake -E make_directory "${BUILD_DIR}" 24 | 25 | - name: Configure CMake 26 | shell: bash 27 | run: > 28 | cmake \ 29 | -S "${GITHUB_WORKSPACE}" \ 30 | -B "${BUILD_DIR}" \ 31 | -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ 32 | -DBUILD_TESTING=ON 33 | env: 34 | CC: ${{matrix.cc}} 35 | CXX: ${{matrix.cxx}} 36 | 37 | - name: Build 38 | shell: bash 39 | run: cmake --build "${BUILD_DIR}" --config ${BUILD_TYPE} 40 | 41 | - name: Test 42 | working-directory: ${{github.workspace}}/build 43 | shell: bash 44 | run: ctest -C ${BUILD_TYPE} 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE files 2 | .idea 3 | .vscode 4 | .vs 5 | 6 | # Common build folder names 7 | build 8 | build-* 9 | cmake-build-* 10 | out 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | set(MORE_CONCEPTS_VERSION 0.1.1) 3 | project(MoreConcepts VERSION "${MORE_CONCEPTS_VERSION}") 4 | 5 | option(BUILD_TESTING "Build the testing tree." OFF) 6 | if(BUILD_TESTING) 7 | enable_testing() 8 | add_subdirectory(tests) 9 | endif() 10 | 11 | add_library(more_concepts INTERFACE) 12 | add_library(more_concepts::more_concepts ALIAS more_concepts) 13 | target_compile_features(more_concepts INTERFACE cxx_std_20) 14 | set_property(TARGET more_concepts PROPERTY VERSION ${MORE_CONCEPTS_VERSION}) 15 | target_include_directories(more_concepts INTERFACE 16 | $ 17 | $ 18 | ) 19 | 20 | install(TARGETS more_concepts 21 | EXPORT MoreConceptsTargets 22 | LIBRARY DESTINATION lib 23 | ARCHIVE DESTINATION lib 24 | RUNTIME DESTINATION bin 25 | INCLUDES DESTINATION include 26 | ) 27 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include DESTINATION .) 28 | install(EXPORT MoreConceptsTargets 29 | FILE MoreConceptsTargets.cmake 30 | NAMESPACE more_concepts:: 31 | DESTINATION lib/cmake 32 | ) 33 | 34 | include(CMakePackageConfigHelpers) 35 | configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in 36 | "${CMAKE_CURRENT_BINARY_DIR}/MoreConceptsConfig.cmake" 37 | INSTALL_DESTINATION . 38 | ) 39 | write_basic_package_version_file( 40 | "${CMAKE_CURRENT_BINARY_DIR}/MoreConceptsConfigVersion.cmake" 41 | VERSION ${MORE_CONCEPTS_VERSION} 42 | COMPATIBILITY AnyNewerVersion 43 | ) 44 | install(FILES 45 | "${CMAKE_CURRENT_BINARY_DIR}/MoreConceptsConfig.cmake" 46 | "${CMAKE_CURRENT_BINARY_DIR}/MoreConceptsConfigVersion.cmake" 47 | DESTINATION . 48 | ) 49 | -------------------------------------------------------------------------------- /Config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/lib/cmake/MoreConceptsTargets.cmake") 4 | 5 | check_required_components(more_concepts) 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Michal Jankovič 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # More concepts 2 | [![Build](https://github.com/MiSo1289/more_concepts/workflows/Build/badge.svg)](https://github.com/MiSo1289/more_concepts/actions?query=workflow%3ABuild) 3 | ```c++ 4 | #include 5 | ``` 6 | This library aims to provide general purpose concepts that are not available in the C++20 concepts library, most notably container concepts. It also provides utilities for writing your own concepts (a `mock_iterator` that can mock any iterator category - see bellow). 7 | 8 | ### Notes 9 | 10 | At the moment, only GCC 10.2 is confirmed to be able to compile all of this library. 11 | 12 | ### Installing with Conan 13 | 14 | If you use Conan to manage dependencies: 15 | 16 | 1. Clone this repository and create the package in your cache: `conan create ` 17 | 2. Add the reference to your `conanfile.txt` (or `conanfile.py`), e.g. `more_concepts/0.1.1` 18 | 3. Install the dependency before building: `conan install -b missing -if `, where `` is your top-level directory (containing your conanfile). 19 | 20 | ## Container concepts 21 | 22 | The container concepts are intended to serve as an abstraction for the STL container interfaces, allowing writing constrained generic code that can use any container of some category, both standard and third-party (as long as it provides an STL compliant interface). 23 | 24 | While a lot of generic algorithms can do with range and iterator concepts, this is sometimes not enough - for example, how does one write a constrained container adaptor? Let's say you want to implement a priority queue that can use any index-able sequence type with back-insertion to store the heap. Using this library, you could express this as: 25 | ```c++ 26 | template Seq = std::vector> 28 | requires more_concepts::back_growable_container 29 | class priority_queue; 30 | ``` 31 | 32 | ### General container concepts 33 | ```c++ 34 | #include 35 | ``` 36 | - `container` - Satisfied by all well-behaved (not `vector`) standard containers. 37 | - `mutable_container` - A container that allows mutable iteration. Satisfied by all standard containers except `set`. 38 | - `sized_container` - A container that knows its size. Satisfied by all standard containers except `forward_list`. 39 | - `clearable_container` - A container that can be cleared . Satisfied by all standard containers except `array`. 40 | - `reversible_container` - A container that allows reverse iteration. Satisfied by all standard containers except `forward_list`. 41 | 42 | For each of the above, an `_of` version is provided (e.g. `reversible_container_of`), that also requires the `value_type` to be the same as specified. 43 | 44 | ### Sequence container concepts 45 | ```c++ 46 | #include 47 | ``` 48 | - `sequence_container` - A mutable container that represents linear ordering of elements (corresponds to the standard `SequenceContainer` named requirement). Provides efficient access to the beginning of the sequence. Satisfied by all standard sequence containers, namely `vector`, `array`, `basic_string`, `deque`, `list`, and `forward_list`. 49 | - `double_ended_container` - A sized and reversible sequence container that provides efficient access to the end of the sequence. Satisfied by all standard sequence containers except `forward_list`. 50 | - `random_access_container` - A double-ended sequence container that provides indexed access to elements. Satisfied by `vector`, `array`, `basic_string`, and `deque`. 51 | - `contiguous_container` - A random-access sequence container, stored contiguously in memory. Satisfied by `vector`, `array`, and `basic_string`. 52 | - `resizable_sequence_container` - A clearable double-ended container that allows resizing, range construction and assignment, and insertion / erasure in the middle). Satisfied by `vector`, `basic_string`, `deque`, and `list`. 53 | - `inplace_constructing_sequence_container` - Extends the `resizable_sequence_container` interface with in-place construction. Satisfied by all standard models of `resizable_sequence_container` except `basic_string`. 54 | - `front_growable_container` - A sequence container that allows efficient inserting / erasure at the front. Satisfied by `deque`, `list`, and `forward_list`. 55 | - `inplace_front_constructing_container` - Extends the `front_growable_container` interface with in-place construction. Satisfied by all standard models of `front_growable_container`. 56 | - `back_growable_container` - A double-ended container that allows efficient inserting / erasure at the back. Satisfied by `vector`, `basic_string`, `deque`, and `list`. 57 | - `inplace_back_constructing_container` - Extends the `back_growable_container` interface with in-place construction. Satisfied by all standard models of `back_growable_container` except `basic_string`. 58 | 59 | For each sequence container concept, an `_of` version is also provided. 60 | 61 | ### Associative container concepts 62 | ```c++ 63 | #include 64 | ``` 65 | - `associative_container` - A container that provides fast lookup of objects based on keys. Represents a union of the standard named requirements `AssociativeContainer` and `UnorderedAssociativeContainer`. Satisfied by all standard associative containers. 66 | - `unique_associative_container` - An associative container with unique keys. Satisfied by `set`, `map`, `unordered_set` and `unordered_map`. 67 | - `multiple_associative_container` - An associative container with non-unique keys. Satisfied by `multiset`, `multimap`, `unordered_multiset` and `unordered_multimap`. 68 | - `map_container` - An associative container representing a key-value mapping. Satisfied by `map`, `multimap`, `unordered_map` and `unordered_multimap`. 69 | - `unique_map_container` - A map container with unique keys. Satisfied by `map` and `unordered_map`. 70 | - `multiple_map_container` - A map container with non-unique keys. Satisfied by `multimap` and `unordered_multimap`. 71 | 72 | For each associative container / map concept, `ordered_` and `unordered_` versions are available (e.g. `ordered_unique_map_container`). 73 | 74 | For each generic (non-map) associative container concept, an `_of` version is available. 75 | 76 | For each map container concept, an `_of` version is available. 77 | 78 | ### General concepts 79 | ```c++ 80 | #include 81 | ``` 82 | Concepts that are simple wrappers over standard type traits: 83 | - `decayed` - Types that are non-reference, non-c-array, non-function or function reference, non-const and non-volatile. Assigning an object of this type to an auto variable preserves the type. Used to constrain the `value_type` of containers. 84 | - `aggregate` - Types that support aggregate initialization. 85 | - `trivial` - Types that can be `memcpy`-ied, and don't need any (non-trivial) initialization or destruction. 86 | - `enum_type` - Scoped and unscoped enumeration types. 87 | - `error_code_enum` - Error enum that can be used to construct a `std::error_code`. 88 | - `error_condition_enum` - Error enum that can be used to construct a `std::error_condition`. 89 | 90 | Function concepts: 91 | - `invocable_as` - Function types that can be called with `std::invoke` using one or more function signatures. The return type of each signature is only checked for convertibility. 92 | - `callable_as` - Function types that can be called with the function-call operator using one or more function signatures. The return type of each signature must be matched exactly. 93 | - `hash_function` - corresponds to the `Hash` standard named requirement. Used to define the `unordered_associative_container` concept. 94 | 95 | ## Utilities 96 | 97 | ### Mock iterator 98 | ```c++ 99 | #include 100 | ``` 101 | The `mock_iterator` class template can be used to write concepts that require some operation to accept any iterator of some category. 102 | 103 | Template parameters: 104 | - `T` - iterator value type. 105 | - `IteratorCategory` - can be one of the standard iterator category tags (e.g. `std::input_iterator_tag`). The mock iterator provides the minimal needed interface to satisfy the requested category. E.g. for the input and output iterator categories, a proxy reference type is used instead of a raw reference. 106 | - `RWCategory` - can be one of `mutable_iterator_tag`, `const_iterator_tag`. Indicates whether the mock iterator should support write access. 107 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from conan import ConanFile 4 | from conan.tools.cmake import CMakeToolchain, CMake 5 | from conan.tools.files import load, copy 6 | 7 | class MoreConcepts(ConanFile): 8 | name = "more_concepts" 9 | revision_mode = "scm" 10 | description = "C++20 concepts library, providing container concepts and more." 11 | homepage = "https://github.com/MiSo1289/more_concepts" 12 | url = "https://github.com/MiSo1289/more_concepts" 13 | license = "MIT" 14 | settings = ("os", "compiler", "arch", "build_type") 15 | exports_sources = ( 16 | "include/*", 17 | "tests/*", 18 | "CMakeLists.txt", 19 | ) 20 | no_copy_source = True 21 | 22 | def package(self): 23 | copy(self, "*.hpp", self.source_folder, self.package_folder) 24 | 25 | def package_id(self): 26 | self.info.clear() 27 | 28 | def package_info(self): 29 | self.cpp_info.bindirs = [] 30 | self.cpp_info.libdirs = [] 31 | 32 | def set_version(self): 33 | try: 34 | content = load(self, "CMakeLists.txt") 35 | version = re.search("set\\(MORE_CONCEPTS_VERSION (.*)\\)", content).group(1) 36 | self.version = version.strip() 37 | except OSError: 38 | return None 39 | -------------------------------------------------------------------------------- /include/more_concepts/associative_containers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "more_concepts/base_concepts.hpp" 9 | #include "more_concepts/base_containers.hpp" 10 | #include "more_concepts/mock_iterator.hpp" 11 | 12 | namespace more_concepts 13 | { 14 | /// A container that provides fast lookup of objects based on keys. 15 | /// Satisfied by all standard set and map types. 16 | /// 17 | /// Note: this concept corresponds to a common subset of the standard named requirements 18 | /// AssociativeContainer and UnorderedAssociativeContainer. 19 | /// These correspond to the ordered_associative_container and 20 | /// unordered_associative_container concepts respectively. 21 | /// 22 | /// Note: the container is required to support .emplace_hint(const_iterator, Args...) 23 | /// for every constructor of the form value_type(Args...). 24 | template 25 | concept associative_container 26 | = container and 27 | sized_container and 28 | clearable_container and 29 | std::default_initializable and 30 | requires(C& cont, C const& const_cont) 31 | { 32 | typename C::key_type; 33 | 34 | // Searching 35 | requires 36 | requires(typename C::key_type const& key) 37 | { 38 | { const_cont.count(key) } -> std::same_as; 39 | { const_cont.contains(key) } -> std::same_as; 40 | { cont.find(key) } -> std::same_as; 41 | { const_cont.find(key) } -> std::same_as; 42 | { 43 | cont.equal_range(key) 44 | } -> std::same_as>; 45 | { 46 | const_cont.equal_range(key) 47 | } -> std::same_as>; 48 | }; 49 | 50 | // Emplacement (default constructor) 51 | requires not std::default_initializable or 52 | requires(typename C::const_iterator const& hint) 53 | {{ cont.emplace_hint(hint) } -> std::same_as; }; 54 | 55 | // Copy-enabled operations 56 | requires not std::copyable or 57 | requires( 58 | typename C::value_type const& value, 59 | typename C::const_iterator const& hint, 60 | mock_const_iterator const& first, 61 | mock_const_iterator const& last, 62 | std::initializer_list const& init_list) 63 | { 64 | // Range construction and assignment 65 | C(first, last); 66 | C(init_list); 67 | cont = init_list; 68 | 69 | // Copy insertion 70 | { cont.insert(hint, value) } -> std::same_as; 71 | 72 | // Range insertion 73 | cont.insert(first, last); 74 | cont.insert(init_list); 75 | 76 | // Emplacement (copy constructor) 77 | { cont.emplace_hint(hint, value) } -> std::same_as; 78 | }; 79 | 80 | // Move-enabled operations 81 | requires not std::movable or 82 | requires( 83 | typename C::value_type&& tmp_value, 84 | typename C::const_iterator const& hint) 85 | { 86 | // Move insertion 87 | { cont.insert(hint, std::move(tmp_value)) } -> std::same_as; 88 | 89 | // Emplacement (move constructor) 90 | { cont.emplace_hint(hint, std::move(tmp_value)) } -> std::same_as; 91 | }; 92 | 93 | // Erasure 94 | requires 95 | requires( 96 | typename C::const_iterator const& pos, 97 | typename C::const_iterator const& first, 98 | typename C::const_iterator const& last, 99 | typename C::key_type const& key) 100 | { 101 | { cont.erase(pos) } -> std::same_as; 102 | { cont.erase(first, last) } -> std::same_as; 103 | { cont.erase(key) } -> std::same_as; 104 | }; 105 | }; 106 | 107 | /// An associative container without multiple key occurrence. 108 | /// Satisfied by set, map, unordered_set and unordered_map. 109 | /// 110 | /// Note: the container is required to support .emplace(Args...) for every constructor 111 | /// of the form value_type(Args...). 112 | template 113 | concept unique_associative_container 114 | = associative_container and 115 | requires(C& cont, C const& const_cont) 116 | { 117 | // Emplacement (default constructor) 118 | requires not std::default_initializable or 119 | requires(typename C::const_iterator const& hint) 120 | {{ cont.emplace() } -> std::same_as>; }; 121 | 122 | // Copy-enabled operations 123 | requires not std::copyable or 124 | requires(typename C::value_type const& value) 125 | { 126 | // Copy insertion 127 | { cont.insert(value) } -> std::same_as>; 128 | 129 | // Emplacement (copy constructor) 130 | { cont.emplace(value) } -> std::same_as>; 131 | }; 132 | 133 | // Move-enabled operations 134 | requires not std::movable or 135 | requires(typename C::value_type&& tmp_value) 136 | { 137 | // Move insertion 138 | { cont.insert(std::move(tmp_value)) } -> std::same_as>; 139 | 140 | // Emplacement (move constructor) 141 | { cont.emplace(std::move(tmp_value)) } -> std::same_as>; 142 | }; 143 | }; 144 | 145 | /// An associative container with multiple key occurrence. 146 | /// Satisfied by multiset, multimap, unordered_multiset and unordered_multimap. 147 | /// 148 | /// Note: the container is required to support .emplace(Args...) for every constructor 149 | /// of the form value_type(Args...). 150 | template 151 | concept multiple_associative_container 152 | = associative_container and 153 | requires(C& cont, C const& const_cont) 154 | { 155 | // Emplacement (default constructor) 156 | requires not std::default_initializable or 157 | requires(typename C::const_iterator const& hint) 158 | {{ cont.emplace() } -> std::same_as; }; 159 | 160 | // Copy-enabled operations 161 | requires not std::copyable or 162 | requires(typename C::value_type const& value) 163 | { 164 | // Copy insertion 165 | { cont.insert(value) } -> std::same_as; 166 | 167 | // Emplacement (copy constructor) 168 | { cont.emplace(value) } -> std::same_as; 169 | }; 170 | 171 | // Move-enabled operations 172 | requires not std::movable or 173 | requires(typename C::value_type&& tmp_value) 174 | { 175 | // Move insertion 176 | { cont.insert(std::move(tmp_value)) } -> std::same_as; 177 | 178 | // Emplacement (move constructor) 179 | { cont.emplace(std::move(tmp_value)) } -> std::same_as; 180 | }; 181 | }; 182 | 183 | /// An associative container ordered on keys. 184 | /// Satisfied by set, map, multiset and multimap. 185 | /// 186 | /// Note: corresponds to the standard AssociativeContainer named requirement. 187 | template 188 | concept ordered_associative_container 189 | = associative_container and 190 | requires(C& cont, C const& const_cont) 191 | { 192 | // Comparators 193 | typename C::key_compare; 194 | typename C::value_compare; 195 | requires std::strict_weak_order< 196 | typename C::key_compare, typename C::key_type, typename C::key_type>; 197 | requires std::strict_weak_order< 198 | typename C::value_compare, typename C::value_type, typename C::value_type>; 199 | 200 | // Ordered associative containers of ordered values should provide a lexicographical ordering 201 | requires not std::totally_ordered or 202 | std::totally_ordered; 203 | 204 | // Observers 205 | { const_cont.key_comp() } -> std::same_as; 206 | { const_cont.value_comp() } -> std::same_as; 207 | 208 | // Construction with comparator 209 | requires 210 | requires(typename C::key_compare const& key_comp) 211 | { 212 | C(key_comp); 213 | 214 | requires not std::copyable or 215 | requires( 216 | mock_const_iterator const& first, 217 | mock_const_iterator const& last, 218 | std::initializer_list const& init_list) 219 | { 220 | C(first, last, key_comp); 221 | C(init_list, key_comp); 222 | }; 223 | }; 224 | 225 | // Binary searching 226 | requires 227 | requires(typename C::key_type const& key) 228 | { 229 | { cont.lower_bound(key) } -> std::same_as; 230 | { const_cont.lower_bound(key) } -> std::same_as; 231 | { cont.upper_bound(key) } -> std::same_as; 232 | { const_cont.upper_bound(key) } -> std::same_as; 233 | }; 234 | }; 235 | 236 | /// An ordered associative container without multiple key occurrence. 237 | /// Satisfied by set and map. 238 | template 239 | concept ordered_unique_associative_container 240 | = unique_associative_container and ordered_associative_container; 241 | 242 | /// An ordered associative container with multiple key occurrence. 243 | /// Satisfied by multiset and multimap. 244 | template 245 | concept ordered_multiple_associative_container 246 | = multiple_associative_container and ordered_associative_container; 247 | 248 | /// An associative container based on key hashing (hash table). 249 | /// Satisfied by unordered_set, unordered_map, unordered_multiset and unordered_multimap. 250 | /// 251 | /// Note: corresponds to the standard UnorderedAssociativeContainer named requirement. 252 | template 253 | concept unordered_associative_container 254 | = associative_container and 255 | requires(C& cont, C const& const_cont) 256 | { 257 | // Key hash function 258 | typename C::hasher; 259 | requires hash_function; 260 | 261 | // Key equality predicate 262 | typename C::key_equal; 263 | requires std::equivalence_relation< 264 | typename C::key_equal, typename C::key_type, typename C::key_type>; 265 | 266 | // Observers 267 | { const_cont.hash_function() } -> std::same_as; 268 | { const_cont.key_eq() } -> std::same_as; 269 | 270 | // Hash policy 271 | requires 272 | requires(float const& ml, std::size_t n) { 273 | { const_cont.load_factor() } -> std::same_as; 274 | { const_cont.max_load_factor() } -> std::same_as; 275 | cont.max_load_factor(ml); 276 | cont.rehash(n); 277 | cont.reserve(n); 278 | }; 279 | 280 | // Minimal bucket interface 281 | { const_cont.bucket_count() } -> std::same_as; 282 | // Not requiring local_iterator etc as its not provided by many popular 283 | // non-std hash tables, e.g. absl::flat_hash_set and tsl::robin_map. 284 | 285 | // Construction with bucket count, hash function, and equality predicate 286 | requires 287 | requires( 288 | std::size_t const& bucket_count, 289 | typename C::hasher const& hash, 290 | typename C::key_equal const& equal) 291 | { 292 | C(bucket_count); 293 | C(bucket_count, hash); 294 | C(bucket_count, hash, equal); 295 | 296 | requires not std::copyable or 297 | requires( 298 | mock_const_iterator const& first, 299 | mock_const_iterator const& last, 300 | std::initializer_list const& init_list) 301 | { 302 | C(first, last, bucket_count); 303 | C(first, last, bucket_count, hash); 304 | C(first, last, bucket_count, hash, equal); 305 | C(init_list, bucket_count); 306 | C(init_list, bucket_count, hash); 307 | C(init_list, bucket_count, hash, equal); 308 | }; 309 | }; 310 | }; 311 | 312 | /// A hash-based associative container without multiple key occurrence. 313 | /// Satisfied by unordered_set and unordered_map. 314 | template 315 | concept unordered_unique_associative_container 316 | = unique_associative_container and unordered_associative_container; 317 | 318 | /// A hash-based associative container with multiple key occurrence. 319 | /// Satisfied by unordered_multiset and unordered_multimap. 320 | template 321 | concept unordered_multiple_associative_container 322 | = multiple_associative_container and unordered_associative_container; 323 | 324 | /// An associative container that stores a mapped object alongside its keys. 325 | /// Satisfied by map, multimap, unordered_map and unordered_multimap. 326 | template 327 | concept map_container 328 | = associative_container and 329 | requires(C& cont, C const& const_cont) 330 | { 331 | typename C::mapped_type; 332 | 333 | requires 334 | requires(typename C::const_iterator const& hint) 335 | { 336 | requires not std::copyable or 337 | requires(typename C::key_type const& key) 338 | { 339 | // Emplacement (key copy, mapped object default constructor) 340 | requires not std::default_initializable or 341 | requires 342 | { 343 | { 344 | cont.emplace_hint(hint, key) 345 | } -> std::same_as; 346 | { 347 | cont.emplace_hint( 348 | hint, 349 | std::piecewise_construct, 350 | std::forward_as_tuple(key), 351 | std::forward_as_tuple()) 352 | } -> std::same_as; 353 | }; 354 | 355 | // Emplacement (key copy, mapped object copy constructor) 356 | requires not std::copyable or 357 | requires(typename C::mapped_type const& obj) 358 | { 359 | { 360 | cont.emplace_hint(hint, key, obj) 361 | } -> std::same_as; 362 | { 363 | cont.emplace_hint( 364 | hint, 365 | std::piecewise_construct, 366 | std::forward_as_tuple(key), 367 | std::forward_as_tuple(obj)) 368 | } -> std::same_as; 369 | }; 370 | 371 | // Emplacement (key copy, mapped object move constructor) 372 | requires not std::movable or 373 | requires(typename C::mapped_type&& obj) 374 | { 375 | { 376 | cont.emplace_hint(hint, key, std::move(obj)) 377 | } -> std::same_as; 378 | { 379 | cont.emplace_hint( 380 | hint, 381 | std::piecewise_construct, 382 | std::forward_as_tuple(key), 383 | std::forward_as_tuple(std::move(obj))) 384 | } -> std::same_as; 385 | }; 386 | }; 387 | 388 | requires not std::movable or 389 | requires(typename C::key_type&& key) 390 | { 391 | // Emplacement (key move, mapped object default constructor) 392 | requires not std::default_initializable or 393 | requires 394 | { 395 | { 396 | cont.emplace_hint(hint, std::move(key)) 397 | } -> std::same_as; 398 | { 399 | cont.emplace_hint( 400 | hint, 401 | std::piecewise_construct, 402 | std::forward_as_tuple(std::move(key)), 403 | std::forward_as_tuple()) 404 | } -> std::same_as; 405 | }; 406 | 407 | // Emplacement (key move, mapped object copy constructor) 408 | requires not std::copyable or 409 | requires(typename C::mapped_type const& obj) 410 | { 411 | { 412 | cont.emplace_hint(hint, std::move(key), obj) 413 | } -> std::same_as; 414 | { 415 | cont.emplace_hint( 416 | hint, 417 | std::piecewise_construct, 418 | std::forward_as_tuple(std::move(key)), 419 | std::forward_as_tuple(obj)) 420 | } -> std::same_as; 421 | }; 422 | 423 | // Emplacement (key move, mapped object move constructor) 424 | requires not std::movable or 425 | requires(typename C::mapped_type&& obj) 426 | { 427 | { 428 | cont.emplace_hint(hint, std::move(key), std::move(obj)) 429 | } -> std::same_as; 430 | { 431 | cont.emplace_hint( 432 | hint, 433 | std::piecewise_construct, 434 | std::forward_as_tuple(std::move(key)), 435 | std::forward_as_tuple(std::move(obj))) 436 | } -> std::same_as; 437 | }; 438 | }; 439 | }; 440 | }; 441 | 442 | /// A map container without multiple key occurrence. 443 | /// Satisfied by map and unordered_map. 444 | /// 445 | /// Note: the container is required to support 446 | /// .try_emplace(key_type const&, Args...) 447 | /// .try_emplace(key_type&&, Args...) 448 | /// .try_emplace(const_iterator, key_type const&, Args...) 449 | /// .try_emplace(const_iterator, key_type&&, Args...) 450 | /// for every constructor of the form mapped_type(Args...), and 451 | /// .insert_or_assign(key_type const&, M&&) 452 | /// .insert_or_assign(key_type&&, M&&) 453 | /// .insert_or_assign(const_iterator, key_type const&, M&&) 454 | /// .insert_or_assign(const_iterator, key_type&&, M&&) 455 | /// for every type M where std::is_assignable_v is true. 456 | template 457 | concept unique_map_container 458 | = map_container and 459 | requires(C& cont, C const& const_cont) 460 | { 461 | // Element access 462 | requires 463 | requires(typename C::key_type const& key, typename C::key_type const& tmp_key) 464 | { 465 | // operator[] access 466 | requires not std::default_initializable or 467 | requires 468 | { 469 | // Key copy 470 | requires not std::copyable or 471 | requires {{ cont[key] } -> std::same_as; }; 472 | 473 | // Key move 474 | requires not std::movable or 475 | requires 476 | {{ cont[std::move(tmp_key)] } -> std::same_as; }; 477 | }; 478 | 479 | // Checked access 480 | { cont.at(key) } -> std::same_as; 481 | { const_cont.at(key) } -> std::same_as; 482 | }; 483 | 484 | requires 485 | requires(typename C::const_iterator const& hint) 486 | { 487 | // Key copy 488 | requires not std::copyable or 489 | requires(typename C::key_type const& key) 490 | { 491 | // Emplacement (key copy, mapped object default constructor) 492 | requires not std::default_initializable or 493 | requires 494 | { 495 | { 496 | cont.emplace(key) 497 | } -> std::same_as>; 498 | { 499 | cont.emplace( 500 | std::piecewise_construct, 501 | std::forward_as_tuple(key), 502 | std::forward_as_tuple()) 503 | } -> std::same_as>; 504 | { 505 | cont.try_emplace(key) 506 | } -> std::same_as>; 507 | { 508 | cont.try_emplace(hint, key) 509 | } -> std::same_as; 510 | }; 511 | 512 | // Mapped object copy 513 | requires not std::copyable or 514 | requires(typename C::mapped_type const& obj) 515 | { 516 | // Insertion 517 | { 518 | cont.insert_or_assign(key, obj) 519 | } -> std::same_as>; 520 | { 521 | cont.insert_or_assign(hint, key, obj) 522 | } -> std::same_as; 523 | 524 | // Emplacement (key copy, mapped object copy constructor) 525 | { 526 | cont.emplace(key, obj) 527 | } -> std::same_as>; 528 | { 529 | cont.emplace( 530 | std::piecewise_construct, 531 | std::forward_as_tuple(key), 532 | std::forward_as_tuple(obj)) 533 | } -> std::same_as>; 534 | { 535 | cont.try_emplace(key, obj) 536 | } -> std::same_as>; 537 | { 538 | cont.try_emplace(hint, key, obj) 539 | } -> std::same_as; 540 | }; 541 | 542 | // Mapped object move 543 | requires not std::movable or 544 | requires(typename C::mapped_type&& obj) 545 | { 546 | // Insertion 547 | { 548 | cont.insert_or_assign(key, std::move(obj)) 549 | } -> std::same_as>; 550 | { 551 | cont.insert_or_assign(hint, key, std::move(obj)) 552 | } -> std::same_as; 553 | 554 | // Emplacement (key copy, mapped object move constructor) 555 | { 556 | cont.emplace(key, std::move(obj)) 557 | } -> std::same_as>; 558 | { 559 | cont.emplace( 560 | std::piecewise_construct, 561 | std::forward_as_tuple(key), 562 | std::forward_as_tuple(std::move(obj))) 563 | } -> std::same_as>; 564 | { 565 | cont.try_emplace(key, std::move(obj)) 566 | } -> std::same_as>; 567 | { 568 | cont.try_emplace(hint, key, std::move(obj)) 569 | } -> std::same_as; 570 | }; 571 | }; 572 | 573 | // Key move 574 | requires not std::movable or 575 | requires(typename C::key_type&& key) 576 | { 577 | // Emplacement (key move, mapped object default constructor) 578 | requires not std::default_initializable or 579 | requires 580 | { 581 | { 582 | cont.emplace(std::move(key)) 583 | } -> std::same_as>; 584 | { 585 | cont.emplace( 586 | std::piecewise_construct, 587 | std::forward_as_tuple(std::move(key)), 588 | std::forward_as_tuple()) 589 | } -> std::same_as>; 590 | { 591 | cont.try_emplace(std::move(key)) 592 | } -> std::same_as>; 593 | { 594 | cont.try_emplace(hint, std::move(key)) 595 | } -> std::same_as; 596 | }; 597 | 598 | // Mapped object copy 599 | requires not std::copyable or 600 | requires(typename C::mapped_type const& obj) 601 | { 602 | // Insertion 603 | { 604 | cont.insert_or_assign(std::move(key), obj) 605 | } -> std::same_as>; 606 | { 607 | cont.insert_or_assign(hint, std::move(key), obj) 608 | } -> std::same_as; 609 | 610 | // Emplacement (key move, mapped object copy constructor) 611 | { 612 | cont.emplace(std::move(key), obj) 613 | } -> std::same_as>; 614 | { 615 | cont.emplace( 616 | std::piecewise_construct, 617 | std::forward_as_tuple(std::move(key)), 618 | std::forward_as_tuple(obj)) 619 | } -> std::same_as>; 620 | { 621 | cont.try_emplace(std::move(key), obj) 622 | } -> std::same_as>; 623 | { 624 | cont.try_emplace(hint, std::move(key), obj) 625 | } -> std::same_as; 626 | }; 627 | 628 | // Mapped object move 629 | requires not std::movable or 630 | requires(typename C::mapped_type&& obj) 631 | { 632 | // Insertion 633 | { 634 | cont.insert_or_assign(std::move(key), std::move(obj)) 635 | } -> std::same_as>; 636 | { 637 | cont.insert_or_assign(hint, std::move(key), std::move(obj)) 638 | } -> std::same_as; 639 | 640 | // Emplacement (key move, mapped object move constructor) 641 | { 642 | cont.emplace(std::move(key), std::move(obj)) 643 | } -> std::same_as>; 644 | { 645 | cont.emplace( 646 | std::piecewise_construct, 647 | std::forward_as_tuple(std::move(key)), 648 | std::forward_as_tuple(std::move(obj))) 649 | } -> std::same_as>; 650 | { 651 | cont.try_emplace(std::move(key), std::move(obj)) 652 | } -> std::same_as>; 653 | { 654 | cont.try_emplace(hint, std::move(key), std::move(obj)) 655 | } -> std::same_as; 656 | }; 657 | }; 658 | }; 659 | }; 660 | 661 | /// A map container with multiple key occurrence. 662 | /// Satisfied by multimap and unordered_multimap. 663 | template 664 | concept multiple_map_container 665 | = map_container and 666 | requires(C& cont, C const& const_cont) 667 | { 668 | requires 669 | requires(typename C::const_iterator const& hint) 670 | { 671 | requires not std::copyable or 672 | requires(typename C::key_type const& key) 673 | { 674 | // Emplacement (key copy, mapped object default constructor) 675 | requires not std::default_initializable or 676 | requires 677 | { 678 | { 679 | cont.emplace(key) 680 | } -> std::same_as; 681 | { 682 | cont.emplace( 683 | std::piecewise_construct, 684 | std::forward_as_tuple(key), 685 | std::forward_as_tuple()) 686 | } -> std::same_as; 687 | }; 688 | 689 | // Emplacement (key copy, mapped object copy constructor) 690 | requires not std::copyable or 691 | requires(typename C::mapped_type const& obj) 692 | { 693 | { 694 | cont.emplace(key, obj) 695 | } -> std::same_as; 696 | { 697 | cont.emplace( 698 | std::piecewise_construct, 699 | std::forward_as_tuple(key), 700 | std::forward_as_tuple(obj)) 701 | } -> std::same_as; 702 | }; 703 | 704 | // Emplacement (key copy, mapped object move constructor) 705 | requires not std::movable or 706 | requires(typename C::mapped_type&& obj) 707 | { 708 | { 709 | cont.emplace(key, std::move(obj)) 710 | } -> std::same_as; 711 | { 712 | cont.emplace( 713 | std::piecewise_construct, 714 | std::forward_as_tuple(key), 715 | std::forward_as_tuple(std::move(obj))) 716 | } -> std::same_as; 717 | }; 718 | }; 719 | 720 | requires not std::movable or 721 | requires(typename C::key_type&& key) 722 | { 723 | // Emplacement (key move, mapped object default constructor) 724 | requires not std::default_initializable or 725 | requires 726 | { 727 | { 728 | cont.emplace(std::move(key)) 729 | } -> std::same_as; 730 | { 731 | cont.emplace( 732 | std::piecewise_construct, 733 | std::forward_as_tuple(std::move(key)), 734 | std::forward_as_tuple()) 735 | } -> std::same_as; 736 | }; 737 | 738 | // Emplacement (key copy, mapped object copy constructor) 739 | requires not std::copyable or 740 | requires(typename C::mapped_type const& obj) 741 | { 742 | { 743 | cont.emplace(std::move(key), obj) 744 | } -> std::same_as; 745 | { 746 | cont.emplace( 747 | std::piecewise_construct, 748 | std::forward_as_tuple(std::move(key)), 749 | std::forward_as_tuple(obj)) 750 | } -> std::same_as; 751 | }; 752 | 753 | // Emplacement (key copy, mapped object move constructor) 754 | requires not std::movable or 755 | requires(typename C::mapped_type&& obj) 756 | { 757 | { 758 | cont.emplace(std::move(key), std::move(obj)) 759 | } -> std::same_as; 760 | { 761 | cont.emplace( 762 | std::piecewise_construct, 763 | std::forward_as_tuple(std::move(key)), 764 | std::forward_as_tuple(std::move(obj))) 765 | } -> std::same_as; 766 | }; 767 | }; 768 | }; 769 | }; 770 | 771 | /// An ordered map container. 772 | /// Satisfied by map and multimap. 773 | template 774 | concept ordered_map_container = map_container and ordered_associative_container; 775 | 776 | /// An ordered map container without multiple key occurrence. 777 | /// Satisfied by map. 778 | template 779 | concept ordered_unique_map_container 780 | = unique_map_container and ordered_map_container; 781 | 782 | /// An ordered map container with multiple key occurrence. 783 | /// Satisfied by multimap. 784 | template 785 | concept ordered_multiple_map_container 786 | = multiple_map_container and ordered_map_container; 787 | 788 | /// A hash-based map container. 789 | /// Satisfied by unordered_map and unordered_multimap. 790 | template 791 | concept unordered_map_container = map_container and unordered_associative_container; 792 | 793 | /// A hash-based map container without multiple key occurrence. 794 | /// Satisfied by unordered_map. 795 | template 796 | concept unordered_unique_map_container 797 | = unique_map_container and unordered_map_container; 798 | 799 | /// A hash-based map container with multiple key occurrence. 800 | /// Satisfied by unordered_multimap. 801 | template 802 | concept unordered_multiple_map_container 803 | = multiple_map_container and unordered_map_container; 804 | 805 | template 806 | concept associative_container_of 807 | = container_of and 808 | associative_container and 809 | std::same_as; 810 | 811 | template 812 | concept unique_associative_container_of 813 | = associative_container_of and unique_associative_container; 814 | 815 | template 816 | concept multiple_associative_container_of 817 | = associative_container_of and multiple_associative_container; 818 | 819 | template 820 | concept ordered_associative_container_of 821 | = associative_container_of and ordered_associative_container; 822 | 823 | template 824 | concept ordered_unique_associative_container_of 825 | = unique_associative_container_of and ordered_unique_associative_container; 826 | 827 | template 828 | concept ordered_multiple_associative_container_of 829 | = multiple_associative_container_of and ordered_multiple_associative_container; 830 | 831 | template 832 | concept unordered_associative_container_of 833 | = associative_container_of and unordered_associative_container; 834 | 835 | template 836 | concept unordered_unique_associative_container_of 837 | = unique_associative_container_of and unordered_unique_associative_container; 838 | 839 | template 840 | concept unordered_multiple_associative_container_of 841 | = multiple_associative_container_of and unordered_multiple_associative_container; 842 | 843 | template 844 | concept map_container_of 845 | = map_container and 846 | std::same_as and 847 | std::same_as; 848 | 849 | template 850 | concept unique_map_container_of 851 | = map_container_of and unique_map_container; 852 | 853 | template 854 | concept multiple_map_container_of 855 | = map_container_of and multiple_map_container; 856 | 857 | template 858 | concept ordered_map_container_of 859 | = map_container_of and 860 | ordered_map_container; 861 | 862 | template 863 | concept ordered_unique_map_container_of 864 | = unique_map_container_of and ordered_unique_map_container; 865 | 866 | template 867 | concept ordered_multiple_map_container_of 868 | = multiple_map_container_of and ordered_multiple_map_container; 869 | 870 | template 871 | concept unordered_map_container_of 872 | = map_container_of and 873 | unordered_map_container; 874 | 875 | template 876 | concept unordered_unique_map_container_of 877 | = unique_map_container_of and unordered_unique_map_container; 878 | 879 | template 880 | concept unordered_multiple_map_container_of 881 | = multiple_map_container_of and unordered_multiple_map_container; 882 | } 883 | -------------------------------------------------------------------------------- /include/more_concepts/base_concepts.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "more_concepts/detail/type_traits.hpp" 9 | 10 | namespace more_concepts 11 | { 12 | /// Types that are non-reference, non-c-array, non-function or function reference, 13 | /// non-const and non-volatile. 14 | /// 15 | /// Assigning an object of this type to an auto variable preserves the type. 16 | template 17 | concept decayed = std::same_as>; 18 | 19 | /// Types that support aggregate initialization: 20 | /// https://en.cppreference.com/w/cpp/language/aggregate_initialization 21 | template 22 | concept aggregate = std::is_aggregate_v; 23 | 24 | /// Types which can be trivially constructed and destructed (as in without performing 25 | /// any (de)initialization at all), and can be trivially copied (as in copying is equivalent 26 | /// to calling memcpy). 27 | template 28 | concept trivial = std::is_trivial_v; 29 | 30 | /// Type is a scoped or unscoped enumeration (enum / enum class). 31 | template 32 | concept enum_type = std::is_enum_v; 33 | 34 | /// An enum type that represents an error, and can be used to construct a std::error_code 35 | /// object. 36 | template 37 | concept error_code_enum = enum_type and std::is_error_code_enum_v; 38 | 39 | /// An enum type that represents an error, and can be used to construct a std::error_condition 40 | /// object. 41 | template 42 | concept error_condition_enum = enum_type and std::is_error_condition_enum_v; 43 | 44 | /// Types that can be called with std::invoke using one or more function signatures. 45 | /// The return type of each signature is only checked for convertibility. 46 | template 47 | concept invocable_as = 48 | requires(Signatures& ... signatures) 49 | { 50 | ([] (auto(&)(Args...) -> Ret) 51 | requires std::is_invocable_r_v 52 | {}(signatures), ...); 53 | }; 54 | 55 | /// Types that can be called with the function call operator using one or more function 56 | /// signatures. The return type of each signature must match exactly. 57 | template 58 | concept callable_as = 59 | requires(Signatures& ... signatures) 60 | { 61 | // Call operator checking is deferred to a type trait; 62 | // doing it inline breaks the compiler (GCC 10.2) 63 | ([] (auto(&)(Args...) -> Ret) 64 | requires detail::is_callable_r_v 65 | {}(signatures), ...); 66 | }; 67 | 68 | /// From https://en.cppreference.com/w/cpp/named_req/Hash: 69 | /// A Hash is a function object for which the output depends only on the input 70 | /// and has a very low probability of yielding the same output given different input values. 71 | /// 72 | /// Used to define the unordered_associative_container concept. 73 | template 74 | concept hash_function 75 | = callable_as< 76 | Fn const, 77 | auto(KeyType&) -> std::size_t, 78 | auto(KeyType const&) -> std::size_t>; 79 | } 80 | -------------------------------------------------------------------------------- /include/more_concepts/base_containers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "more_concepts/base_concepts.hpp" 9 | 10 | namespace more_concepts 11 | { 12 | /// From https://en.cppreference.com/w/cpp/named_req/Container: 13 | /// "A Container is an object used to store other objects and taking care 14 | /// of the management of the memory used by the objects it contains." 15 | /// 16 | /// Satisfied by all well-behaved standard containers. 17 | /// Is not satisfied by vector. 18 | template 19 | concept container = 20 | requires(C& cont, C const& const_cont) 21 | { 22 | typename C::value_type; 23 | requires decayed; 24 | 25 | typename C::reference; 26 | typename C::const_reference; 27 | // Well-behaved containers should not rely on proxies for iterators / references. 28 | requires std::same_as; 29 | requires std::same_as; 30 | 31 | typename C::iterator; 32 | typename C::const_iterator; 33 | requires std::forward_iterator; 34 | requires std::forward_iterator; 35 | requires std::convertible_to; 36 | requires std::same_as, typename C::value_type>; 37 | requires std::same_as, typename C::value_type>; 38 | requires std::same_as, typename C::reference> or 39 | std::same_as, typename C::const_reference>; 40 | requires std::same_as, typename C::const_reference>; 41 | 42 | typename C::difference_type; 43 | typename C::size_type; 44 | requires std::signed_integral; 45 | requires std::unsigned_integral; 46 | // size_type should be able to represent all positive values of difference_type. 47 | requires std::in_range( 48 | std::numeric_limits::max()); 49 | // difference_type should be the same as the one defined by iterator_traits. 50 | requires std::same_as< 51 | typename C::difference_type, 52 | typename std::iterator_traits::difference_type>; 53 | requires std::same_as< 54 | typename C::difference_type, 55 | typename std::iterator_traits::difference_type>; 56 | 57 | // Containers of equality comparable values should be equality comparable 58 | requires not std::equality_comparable or 59 | std::equality_comparable; 60 | 61 | // Containers should satisfy at least the strongest object concept satisfied by value_type. 62 | requires not std::movable or 63 | std::movable; 64 | requires not std::copyable or 65 | std::copyable; 66 | requires not std::semiregular or 67 | std::semiregular; 68 | requires not std::regular or 69 | std::regular; 70 | 71 | // Iterators 72 | { cont.begin() } -> std::same_as; 73 | { cont.end() } -> std::same_as; 74 | { const_cont.begin() } -> std::same_as; 75 | { const_cont.end() } -> std::same_as; 76 | { cont.cbegin() } -> std::same_as; 77 | { cont.cend() } -> std::same_as; 78 | 79 | // Capacity 80 | { const_cont.max_size() } -> std::same_as; 81 | { const_cont.empty() } -> std::convertible_to; 82 | }; 83 | 84 | /// A container that allows mutable access to its elements. 85 | /// Satisfied by all standard containers except set. 86 | template 87 | concept mutable_container 88 | = container and 89 | std::same_as, typename C::reference>; 90 | 91 | /// A container that knows its size. 92 | /// Satisfied by all standard containers except forward_list. 93 | template 94 | concept sized_container 95 | = container and 96 | requires(C const& const_cont) 97 | { 98 | { const_cont.size() } -> std::same_as; 99 | }; 100 | 101 | /// A container that can be cleared. 102 | /// Satisfied by all standard containers except array. 103 | template 104 | concept clearable_container 105 | = container and 106 | requires(C& cont) 107 | { 108 | cont.clear(); 109 | }; 110 | 111 | /// A container that allows reverse iteration. 112 | /// Satisfied by all standard containers except forward_list. 113 | template 114 | concept reversible_container 115 | = container and 116 | requires(C& cont, C const& const_cont) 117 | { 118 | requires std::bidirectional_iterator; 119 | requires std::bidirectional_iterator; 120 | 121 | typename C::reverse_iterator; 122 | typename C::const_reverse_iterator; 123 | requires std::bidirectional_iterator; 124 | requires std::bidirectional_iterator; 125 | requires std::convertible_to< 126 | typename C::reverse_iterator, 127 | typename C::const_reverse_iterator>; 128 | requires std::same_as< 129 | typename C::difference_type, 130 | typename std::iterator_traits::difference_type>; 131 | requires std::same_as< 132 | typename C::difference_type, 133 | typename std::iterator_traits::difference_type>; 134 | 135 | { cont.rbegin() } -> std::same_as; 136 | { cont.rend() } -> std::same_as; 137 | { const_cont.rbegin() } -> std::same_as; 138 | { const_cont.rend() } -> std::same_as; 139 | { cont.crbegin() } -> std::same_as; 140 | { cont.crend() } -> std::same_as; 141 | }; 142 | 143 | /// A container of a specific value type. 144 | template 145 | concept container_of = container and std::same_as; 146 | 147 | template 148 | concept mutable_container_of = container_of and mutable_container; 149 | 150 | template 151 | concept sized_container_of = container_of and sized_container; 152 | 153 | template 154 | concept clearable_container_of = container_of and clearable_container; 155 | 156 | template 157 | concept reversible_container_of = container_of and reversible_container; 158 | } 159 | -------------------------------------------------------------------------------- /include/more_concepts/detail/mock_iterator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace more_concepts::detail 7 | { 8 | template 9 | class mock_iterator_proxy_reference 10 | { 11 | public: 12 | auto operator=(T const&) const -> mock_iterator_proxy_reference& 13 | requires writable; 14 | 15 | auto operator=(T&&) const -> mock_iterator_proxy_reference& 16 | requires writable; 17 | 18 | operator T() const 19 | requires readable; 20 | 21 | auto operator->() const -> T const* 22 | requires readable; 23 | 24 | auto operator->() const -> T* 25 | requires readable and writable; 26 | }; 27 | 28 | template 29 | struct mock_iterator_value_type_def 30 | { 31 | }; 32 | 33 | template 34 | struct mock_iterator_value_type_def 35 | { 36 | using value_type = void; 37 | }; 38 | 39 | template IteratorCategory> 40 | struct mock_iterator_value_type_def 41 | { 42 | using value_type = T; 43 | }; 44 | 45 | template 46 | struct mock_iterator_reference_def 47 | { 48 | }; 49 | 50 | template 51 | struct mock_iterator_reference_def 52 | { 53 | using reference = void; 54 | 55 | protected: 56 | using deref_result = mock_iterator_proxy_reference; 57 | using arrow_result = void; 58 | }; 59 | 60 | template 61 | struct mock_iterator_reference_def 62 | { 63 | using reference = mock_iterator_proxy_reference; 64 | 65 | protected: 66 | using deref_result = reference; 67 | using arrow_result = reference; 68 | }; 69 | 70 | template IteratorCategory> 71 | struct mock_iterator_reference_def 72 | { 73 | using reference = T&; 74 | 75 | protected: 76 | using deref_result = reference; 77 | using arrow_result = T*; 78 | }; 79 | 80 | template IteratorCategory> 81 | struct mock_iterator_reference_def 82 | { 83 | using reference = T const&; 84 | 85 | protected: 86 | using deref_result = reference; 87 | using arrow_result = T const*; 88 | }; 89 | 90 | template 91 | struct mock_iterator_element_type_def 92 | { 93 | }; 94 | 95 | template IteratorCategory> 96 | struct mock_iterator_element_type_def 97 | { 98 | //using element_type = T; 99 | }; 100 | } 101 | -------------------------------------------------------------------------------- /include/more_concepts/detail/type_traits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace more_concepts::detail 6 | { 7 | template 8 | struct is_callable_r : std::false_type 9 | { 10 | }; 11 | 12 | template 13 | requires 14 | requires(Fn& fn, Args&& ... args) 15 | {{ static_cast(fn)(std::forward(args)...) } -> std::same_as; } 16 | struct is_callable_r : std::true_type 17 | { 18 | }; 19 | 20 | template 21 | inline constexpr auto is_callable_r_v = is_callable_r::value; 22 | } 23 | -------------------------------------------------------------------------------- /include/more_concepts/mock_iterator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "more_concepts/detail/mock_iterator.hpp" 7 | 8 | namespace more_concepts 9 | { 10 | struct mutable_iterator_tag 11 | { 12 | }; 13 | struct const_iterator_tag 14 | { 15 | }; 16 | 17 | /// A dummy implementation of a specific (legacy / concept-based) iterator category. 18 | /// Can be used to check whether a function accepts any iterator of some category. 19 | /// While this is not fool-proof (a function can be written to accept mock_iterator 20 | /// specifically), it is generally good enough. 21 | template 22 | class mock_iterator final 23 | : public detail::mock_iterator_value_type_def, 24 | public detail::mock_iterator_reference_def< 25 | T, IteratorCategory, std::same_as>, 26 | public detail::mock_iterator_element_type_def 27 | { 28 | public: 29 | using iterator_category = IteratorCategory; 30 | using difference_type = std::ptrdiff_t; 31 | 32 | auto operator++() -> mock_iterator& { 33 | return {}; 34 | } 35 | 36 | auto operator++(int) -> mock_iterator { 37 | return {}; 38 | } 39 | 40 | auto operator*() const -> typename mock_iterator::deref_result { 41 | return {}; 42 | } 43 | 44 | auto operator->() const -> typename mock_iterator::arrow_result 45 | requires std::derived_from { 46 | return {}; 47 | } 48 | 49 | auto operator==(mock_iterator const&) const -> bool 50 | requires std::derived_from { 51 | return {}; 52 | } 53 | 54 | auto operator--() -> mock_iterator& 55 | requires std::derived_from { 56 | return {}; 57 | } 58 | 59 | auto operator--(int) -> mock_iterator 60 | requires std::derived_from { 61 | return {}; 62 | } 63 | 64 | auto operator+=(difference_type) -> mock_iterator& 65 | requires std::derived_from { 66 | return {}; 67 | } 68 | 69 | auto operator-=(difference_type) -> mock_iterator& 70 | requires std::derived_from { 71 | return {}; 72 | } 73 | 74 | auto operator[](difference_type) const -> typename mock_iterator::deref_result 75 | requires std::derived_from { 76 | return {}; 77 | } 78 | 79 | auto operator+(difference_type) const -> mock_iterator 80 | requires std::derived_from { 81 | return {}; 82 | } 83 | 84 | template D> 85 | friend auto operator+(D const&, mock_iterator const&) -> mock_iterator 86 | requires std::derived_from { 87 | return {}; 88 | } 89 | 90 | auto operator-(difference_type const&) const -> mock_iterator 91 | requires std::derived_from { 92 | return {}; 93 | } 94 | 95 | auto operator-(mock_iterator const&) const -> difference_type 96 | requires std::derived_from { 97 | return {}; 98 | } 99 | 100 | auto operator<(mock_iterator const&) const -> bool 101 | requires std::derived_from { 102 | return {}; 103 | } 104 | 105 | auto operator>(mock_iterator const&) const -> bool 106 | requires std::derived_from { 107 | return {}; 108 | } 109 | 110 | auto operator<=(mock_iterator const&) const -> bool 111 | requires std::derived_from { 112 | return {}; 113 | } 114 | 115 | auto operator>=(mock_iterator const&) const -> bool 116 | requires std::derived_from { 117 | return {}; 118 | } 119 | }; 120 | 121 | template 122 | using mock_const_iterator = mock_iterator; 123 | 124 | template 125 | using mock_mutable_iterator = mock_iterator; 126 | } 127 | -------------------------------------------------------------------------------- /include/more_concepts/more_concepts.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "more_concepts/associative_containers.hpp" 4 | #include "more_concepts/base_concepts.hpp" 5 | #include "more_concepts/base_containers.hpp" 6 | #include "more_concepts/mock_iterator.hpp" 7 | #include "more_concepts/sequence_containers.hpp" 8 | 9 | // TODO write readme 10 | -------------------------------------------------------------------------------- /include/more_concepts/sequence_containers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "more_concepts/base_containers.hpp" 8 | #include "more_concepts/mock_iterator.hpp" 9 | 10 | namespace more_concepts 11 | { 12 | /// From https://en.cppreference.com/w/cpp/named_req/SequenceContainer: 13 | /// A SequenceContainer is a Container that stores objects of the same type 14 | /// in a linear arrangement. 15 | /// 16 | /// Satisfied by all standard sequence containers. 17 | template 18 | concept sequence_container 19 | = container and 20 | requires(C& cont, C const& const_cont) 21 | { 22 | { cont.front() } -> std::same_as; 23 | { const_cont.front() } -> std::same_as; 24 | 25 | // Sequences of ordered values should provide a lexicographical ordering 26 | requires not std::totally_ordered or 27 | std::totally_ordered; 28 | }; 29 | 30 | /// A sized and reversible sequence container that allows efficient access at both ends. 31 | /// Satisfied by vector, array, basic_string, deque and list. 32 | template 33 | concept double_ended_container 34 | = sequence_container and 35 | sized_container and 36 | reversible_container and 37 | requires(C& cont, C const& const_cont) 38 | { 39 | { cont.back() } -> std::same_as; 40 | { const_cont.back() } -> std::same_as; 41 | }; 42 | 43 | /// A double-ended container that allows indexed access. 44 | /// Satisfied by vector, array, basic_string and deque. 45 | template 46 | concept random_access_container 47 | = double_ended_container and 48 | requires(C& cont, C const& const_cont, typename C::size_type const idx) 49 | { 50 | requires std::random_access_iterator; 51 | requires std::random_access_iterator; 52 | 53 | // Unchecked indexing 54 | { cont[idx] } -> std::same_as; 55 | { const_cont[idx] } -> std::same_as; 56 | 57 | // Bounds-checked indexing 58 | { cont.at(idx) } -> std::same_as; 59 | { const_cont.at(idx) } -> std::same_as; 60 | }; 61 | 62 | /// A random access container that stores elements in a contiguous memory region. 63 | /// Satisfied by vector, array and basic_string. 64 | template 65 | concept contiguous_container 66 | = random_access_container and 67 | requires(C& cont, C const& const_cont) 68 | { 69 | requires std::contiguous_iterator; 70 | requires std::contiguous_iterator; 71 | 72 | typename C::pointer; 73 | typename C::const_pointer; 74 | requires std::contiguous_iterator; 75 | requires std::contiguous_iterator; 76 | requires std::convertible_to; 77 | 78 | { cont.data() } -> std::same_as; 79 | { const_cont.data() } -> std::same_as; 80 | }; 81 | 82 | /// A sequence container that allows resizing, range construction and assignment, 83 | /// and insertion / erasure in the middle. 84 | /// Satisfied by vector, basic_string, deque and list. 85 | template 86 | concept resizable_sequence_container 87 | = double_ended_container and 88 | clearable_container and 89 | std::default_initializable and 90 | requires(C& cont, typename C::const_iterator const& pos) 91 | { 92 | // Copy-enabled operations 93 | requires not std::copyable or 94 | requires( 95 | typename C::size_type const& size, 96 | typename C::value_type const& value, 97 | mock_const_iterator const& first, 98 | mock_const_iterator const& last, 99 | std::initializer_list const& init_list) 100 | { 101 | // Copy insertion 102 | { cont.insert(pos, value) } -> std::same_as; 103 | { cont.insert(pos, first, last) } -> std::same_as; 104 | { cont.insert(pos, init_list) } -> std::same_as; 105 | 106 | // Range construction and assignment 107 | C(first, last); 108 | C(init_list); 109 | C(size, value); 110 | cont = init_list; 111 | cont.assign(first, last); 112 | cont.assign(init_list); 113 | cont.assign(size, value); 114 | 115 | // Resizing 116 | cont.resize(size); 117 | cont.resize(size, value); 118 | }; 119 | 120 | // Move insertion 121 | requires not std::movable or 122 | requires(typename C::value_type&& value) 123 | {{ cont.insert(pos, std::move(value)) } -> std::same_as; }; 124 | 125 | // Erasure 126 | requires 127 | requires( 128 | typename C::const_iterator const& first, 129 | typename C::const_iterator const& last) 130 | { 131 | { cont.erase(pos) } -> std::same_as; 132 | { cont.erase(first, last) } -> std::same_as; 133 | }; 134 | }; 135 | 136 | /// A resizable container that also allows in-place element construction. 137 | /// Satisfied by vector, deque and list. 138 | /// 139 | /// Note: the container is required to support .emplace(const_iterator, Args...) 140 | /// for every constructor of the form value_type(Args...). 141 | template 142 | concept inplace_constructing_sequence_container 143 | = resizable_sequence_container and 144 | requires(C& cont, typename C::const_iterator const& pos) 145 | { 146 | // Emplacement (default constructor) 147 | requires not std::default_initializable or 148 | requires {{ cont.emplace(pos) } -> std::same_as; }; 149 | 150 | // Emplacement (copy constructor) 151 | requires not std::copyable or 152 | requires(typename C::value_type const& value) 153 | {{ cont.emplace(pos, value) } -> std::same_as; }; 154 | 155 | // Emplacement (move constructor) 156 | requires not std::movable or 157 | requires(typename C::value_type&& value) 158 | {{ cont.emplace(pos, std::move(value)) } -> std::same_as; }; 159 | }; 160 | 161 | /// A sequence container that allows efficient insertion and erasure at the front. 162 | /// Satisfied by deque, list and forward_list. 163 | template 164 | concept front_growable_container 165 | = sequence_container and 166 | std::default_initializable and 167 | requires(C& cont, typename C::value_type const& value, typename C::value_type&& tmp_value) 168 | { 169 | // Front copy insertion 170 | requires not std::copyable or 171 | requires { cont.push_front(value); }; 172 | 173 | // Front move insertion 174 | requires not std::movable or 175 | requires { cont.push_front(std::move(tmp_value)); }; 176 | 177 | // Front erasure 178 | cont.pop_front(); 179 | }; 180 | 181 | /// A front-growable container that also allows inplace front construction. 182 | /// Satisfied by deque, list and forward_list. 183 | /// 184 | /// Note: the container is required to support .emplace_front(Args...) 185 | /// for every constructor of the form value_type(Args...). 186 | template 187 | concept inplace_front_constructing_container 188 | = front_growable_container and 189 | requires(C& cont, typename C::value_type const& value, typename C::value_type&& tmp_value) 190 | { 191 | // Front emplacement (default constructor) 192 | requires not std::default_initializable or 193 | requires {{ cont.emplace_front() } -> std::same_as; }; 194 | 195 | // Front emplacement (copy constructor) 196 | requires not std::copyable or 197 | requires {{ cont.emplace_front(value) } -> std::same_as; }; 198 | 199 | // Front emplacement (move constructor) 200 | requires not std::movable or 201 | requires {{ cont.emplace_front(std::move(tmp_value)) } -> std::same_as; }; 202 | }; 203 | 204 | /// A double-ended container that allows efficient insertion and erasure at the back. 205 | /// Satisfied by vector, basic_string, deque, and list. 206 | template 207 | concept back_growable_container 208 | = double_ended_container and 209 | std::default_initializable and 210 | requires(C& cont, typename C::value_type const& value, typename C::value_type&& tmp_value) 211 | { 212 | // Back copy insertion 213 | requires not std::copyable or 214 | requires { cont.push_back(value); }; 215 | 216 | // Back move insertion 217 | requires not std::movable or 218 | requires { cont.push_back(std::move(tmp_value)); }; 219 | 220 | // Back erasure 221 | cont.pop_back(); 222 | }; 223 | 224 | /// A back-growable container that also allows inplace back construction. 225 | /// Satisfied by vector, deque, and list. 226 | /// 227 | /// Note: the container is required to support .emplace_back(Args...) 228 | /// for every constructor of the form value_type(Args...). 229 | template 230 | concept inplace_back_constructing_container 231 | = back_growable_container and 232 | requires(C& cont, typename C::value_type const& value, typename C::value_type&& tmp_value) 233 | { 234 | // Back emplacement (default constructor) 235 | requires not std::default_initializable or 236 | requires {{ cont.emplace_back() } -> std::same_as; }; 237 | 238 | // Back emplacement (copy constructor) 239 | requires not std::copyable or 240 | requires {{ cont.emplace_back(value) } -> std::same_as; }; 241 | 242 | // Back emplacement (move constructor) 243 | requires not std::movable or 244 | requires {{ cont.emplace_back(std::move(tmp_value)) } -> std::same_as; }; 245 | }; 246 | 247 | template 248 | concept sequence_container_of = container_of and sequence_container; 249 | 250 | template 251 | concept double_ended_container_of = container_of and double_ended_container; 252 | 253 | template 254 | concept random_access_container_of = container_of and random_access_container; 255 | 256 | template 257 | concept contiguous_container_of = container_of and contiguous_container; 258 | 259 | template 260 | concept resizable_sequence_container_of = container_of and resizable_sequence_container; 261 | 262 | template 263 | concept inplace_constructing_sequence_container_of 264 | = container_of and inplace_back_constructing_container; 265 | 266 | template 267 | concept front_growable_container_of = container_of and front_growable_container; 268 | 269 | template 270 | concept inplace_front_constructing_container_of 271 | = container_of and inplace_front_constructing_container; 272 | 273 | template 274 | concept back_growable_container_of = container_of and back_growable_container; 275 | 276 | template 277 | concept inplace_back_constructing_container_of 278 | = container_of and inplace_back_constructing_container; 279 | } 280 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(more_concepts_tests) 2 | add_test( 3 | NAME more_concepts_tests 4 | COMMAND more_concepts_tests 5 | ) 6 | target_include_directories( 7 | more_concepts_tests 8 | 9 | PRIVATE 10 | "${CMAKE_CURRENT_SOURCE_DIR}" 11 | "${CMAKE_CURRENT_BINARY_DIR}" 12 | ) 13 | target_link_libraries( 14 | more_concepts_tests 15 | 16 | PRIVATE 17 | more_concepts::more_concepts 18 | ) 19 | 20 | add_subdirectory(more_concepts) 21 | -------------------------------------------------------------------------------- /tests/more_concepts/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | more_concepts_tests 3 | 4 | PRIVATE 5 | test_containers.cpp 6 | test_main.cpp 7 | test_mock_iterator.cpp 8 | ) 9 | -------------------------------------------------------------------------------- /tests/more_concepts/test_containers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "more_concepts/associative_containers.hpp" 13 | #include "more_concepts/base_containers.hpp" 14 | #include "more_concepts/sequence_containers.hpp" 15 | 16 | namespace 17 | { 18 | template 19 | struct require_containers 20 | { 21 | static_assert((more_concepts::container and ...)); 22 | static_assert((more_concepts::container_of and ...)); 23 | }; 24 | 25 | template 26 | struct require_mutable_containers : require_containers 27 | { 28 | static_assert((more_concepts::mutable_container and ...)); 29 | static_assert((more_concepts::mutable_container_of and ...)); 30 | }; 31 | 32 | template 33 | struct require_sized_containers : require_containers 34 | { 35 | static_assert((more_concepts::sized_container and ...)); 36 | static_assert((more_concepts::sized_container_of and ...)); 37 | }; 38 | 39 | template 40 | struct require_clearable_containers : require_containers 41 | { 42 | static_assert((more_concepts::clearable_container and ...)); 43 | static_assert((more_concepts::clearable_container_of and ...)); 44 | }; 45 | 46 | template 47 | struct require_reversible_containers : require_containers 48 | { 49 | static_assert((more_concepts::reversible_container and ...)); 50 | static_assert((more_concepts::reversible_container_of and ...)); 51 | }; 52 | 53 | template 54 | struct require_mutable_sequence_containers : require_mutable_containers 55 | { 56 | static_assert((more_concepts::sequence_container and ...)); 57 | static_assert((more_concepts::sequence_container_of and ...)); 58 | }; 59 | 60 | template 61 | struct require_double_ended_containers 62 | : require_mutable_sequence_containers, 63 | require_sized_containers, 64 | require_reversible_containers 65 | { 66 | static_assert((more_concepts::double_ended_container and ...)); 67 | static_assert((more_concepts::double_ended_container_of and ...)); 68 | }; 69 | 70 | template 71 | struct require_contiguous_random_access_containers : require_double_ended_containers 72 | { 73 | static_assert((more_concepts::contiguous_container and ...)); 74 | static_assert((more_concepts::contiguous_container_of and ...)); 75 | static_assert((more_concepts::random_access_container and ...)); 76 | static_assert((more_concepts::random_access_container_of and ...)); 77 | }; 78 | 79 | template 80 | struct require_resizable_containers 81 | : require_double_ended_containers, 82 | require_clearable_containers 83 | { 84 | static_assert((more_concepts::resizable_sequence_container and ...)); 85 | static_assert((more_concepts::resizable_sequence_container_of and ...)); 86 | }; 87 | 88 | 89 | template 90 | struct require_inplace_constructing_resizable_containers : require_resizable_containers 91 | { 92 | static_assert((more_concepts::inplace_constructing_sequence_container and ...)); 93 | static_assert((more_concepts::inplace_constructing_sequence_container_of and ...)); 94 | }; 95 | 96 | template 97 | struct require_front_growable_containers : require_mutable_sequence_containers 98 | { 99 | static_assert((more_concepts::front_growable_container and ...)); 100 | static_assert((more_concepts::front_growable_container_of and ...)); 101 | }; 102 | 103 | template 104 | struct require_inplace_front_constructing_containers : require_front_growable_containers 105 | { 106 | static_assert((more_concepts::inplace_front_constructing_container and ...)); 107 | static_assert((more_concepts::inplace_front_constructing_container_of and ...)); 108 | }; 109 | 110 | template 111 | struct require_back_growable_containers : require_double_ended_containers 112 | { 113 | static_assert((more_concepts::back_growable_container and ...)); 114 | static_assert((more_concepts::back_growable_container_of and ...)); 115 | }; 116 | 117 | template 118 | struct require_inplace_back_constructing_containers : require_back_growable_containers 119 | { 120 | static_assert((more_concepts::inplace_back_constructing_container and ...)); 121 | static_assert((more_concepts::inplace_back_constructing_container_of and ...)); 122 | }; 123 | 124 | template 125 | struct require_associative_containers 126 | : require_sized_containers, 127 | require_clearable_containers 128 | { 129 | static_assert((more_concepts::associative_container and ...)); 130 | static_assert((more_concepts::associative_container_of and ...)); 131 | }; 132 | 133 | template 134 | struct require_unique_associative_containers : require_associative_containers 135 | { 136 | static_assert((more_concepts::unique_associative_container and ...)); 137 | static_assert((more_concepts::unique_associative_container_of and ...)); 138 | }; 139 | 140 | template 141 | struct require_multiple_associative_containers : require_associative_containers 142 | { 143 | static_assert((more_concepts::multiple_associative_container and ...)); 144 | static_assert((more_concepts::multiple_associative_container_of and ...)); 145 | }; 146 | 147 | template 148 | struct require_ordered_associative_containers : require_associative_containers 149 | { 150 | static_assert((more_concepts::ordered_associative_container and ...)); 151 | static_assert((more_concepts::ordered_associative_container_of and ...)); 152 | }; 153 | 154 | template 155 | struct require_ordered_unique_associative_containers : require_ordered_associative_containers 156 | { 157 | static_assert((more_concepts::ordered_unique_associative_container and ...)); 158 | static_assert((more_concepts::ordered_unique_associative_container_of and ...)); 159 | }; 160 | 161 | template 162 | struct require_ordered_multiple_associative_containers : require_ordered_associative_containers 163 | { 164 | static_assert((more_concepts::ordered_multiple_associative_container and ...)); 165 | static_assert((more_concepts::ordered_multiple_associative_container_of and ...)); 166 | }; 167 | 168 | template 169 | struct require_unordered_associative_containers : require_associative_containers 170 | { 171 | static_assert((more_concepts::unordered_associative_container and ...)); 172 | static_assert((more_concepts::unordered_associative_container_of and ...)); 173 | }; 174 | 175 | template 176 | struct require_unordered_unique_associative_containers : require_unordered_associative_containers 177 | { 178 | static_assert((more_concepts::unordered_unique_associative_container and ...)); 179 | static_assert((more_concepts::unordered_unique_associative_container_of and ...)); 180 | }; 181 | 182 | template 183 | struct require_unordered_multiple_associative_containers 184 | : require_unordered_associative_containers 185 | { 186 | static_assert((more_concepts::unordered_multiple_associative_container and ...)); 187 | static_assert((more_concepts::unordered_multiple_associative_container_of and ...)); 188 | }; 189 | 190 | template 191 | struct require_map_containers : require_associative_containers 192 | { 193 | static_assert((more_concepts::map_container and ...)); 194 | static_assert((more_concepts::map_container_of and ...)); 195 | }; 196 | 197 | template 198 | struct require_unique_map_containers 199 | : require_map_containers, 200 | require_unique_associative_containers 201 | { 202 | static_assert((more_concepts::unique_map_container and ...)); 203 | static_assert((more_concepts::unique_map_container_of and ...)); 204 | }; 205 | 206 | template 207 | struct require_multiple_map_containers 208 | : require_map_containers, 209 | require_multiple_associative_containers 210 | { 211 | static_assert((more_concepts::multiple_map_container and ...)); 212 | static_assert((more_concepts::multiple_map_container_of and ...)); 213 | }; 214 | 215 | template 216 | struct require_ordered_map_containers 217 | : require_map_containers, 218 | require_ordered_associative_containers 219 | { 220 | static_assert((more_concepts::ordered_map_container and ...)); 221 | static_assert((more_concepts::ordered_map_container_of and ...)); 222 | }; 223 | 224 | template 225 | struct require_ordered_unique_map_containers 226 | : require_ordered_map_containers, 227 | require_unique_map_containers 228 | { 229 | static_assert((more_concepts::ordered_unique_map_container and ...)); 230 | static_assert((more_concepts::ordered_unique_map_container_of and ...)); 231 | }; 232 | 233 | template 234 | struct require_ordered_multiple_map_containers 235 | : require_ordered_map_containers, 236 | require_multiple_map_containers 237 | { 238 | static_assert((more_concepts::ordered_multiple_map_container and ...)); 239 | static_assert((more_concepts::ordered_multiple_map_container_of and ...)); 240 | }; 241 | 242 | template 243 | struct require_unordered_map_containers 244 | : require_map_containers, 245 | require_unordered_associative_containers 246 | { 247 | static_assert((more_concepts::unordered_map_container and ...)); 248 | static_assert((more_concepts::unordered_map_container_of and ...)); 249 | }; 250 | 251 | template 252 | struct require_unordered_unique_map_containers 253 | : require_unordered_map_containers, 254 | require_unique_map_containers 255 | { 256 | static_assert((more_concepts::unordered_unique_map_container and ...)); 257 | static_assert((more_concepts::unordered_unique_map_container_of and ...)); 258 | }; 259 | 260 | template 261 | struct require_unordered_multiple_map_containers 262 | : require_unordered_map_containers, 263 | require_multiple_map_containers 264 | { 265 | static_assert((more_concepts::unordered_multiple_map_container and ...)); 266 | static_assert((more_concepts::unordered_multiple_map_container_of and ...)); 267 | }; 268 | 269 | // Sequence containers 270 | 271 | using test_value_type = int; 272 | constexpr auto test_arr_size = 10; 273 | 274 | using test_array = std::array; 275 | using test_vector = std::vector; 276 | using test_string = std::basic_string; 277 | using test_deque = std::deque; 278 | using test_list = std::list; 279 | using test_forward_list = std::forward_list; 280 | 281 | constexpr auto contiguous_random_access = require_contiguous_random_access_containers< 282 | test_value_type, 283 | 284 | test_array, 285 | test_vector, 286 | test_string>{}; 287 | 288 | constexpr auto clearable = require_clearable_containers< 289 | test_value_type, 290 | 291 | test_vector, 292 | test_string, 293 | test_deque, 294 | test_list, 295 | test_forward_list>{}; 296 | 297 | constexpr auto double_ended = require_double_ended_containers< 298 | test_value_type, 299 | 300 | test_array, 301 | test_vector, 302 | test_string, 303 | test_deque, 304 | test_list>{}; 305 | 306 | constexpr auto resizable = require_resizable_containers< 307 | test_value_type, 308 | 309 | test_vector, 310 | test_string, 311 | test_deque, 312 | test_list>{}; 313 | 314 | constexpr auto inplace_constructing = require_inplace_constructing_resizable_containers< 315 | test_value_type, 316 | 317 | test_vector, 318 | test_deque, 319 | test_list>{}; 320 | 321 | constexpr auto front_growable = require_front_growable_containers< 322 | test_value_type, 323 | 324 | test_deque, 325 | test_list, 326 | test_forward_list>{}; 327 | 328 | constexpr auto inplace_front_constructing = require_inplace_front_constructing_containers< 329 | test_value_type, 330 | 331 | test_deque, 332 | test_list, 333 | test_forward_list>{}; 334 | 335 | constexpr auto back_growable = require_back_growable_containers< 336 | test_value_type, 337 | 338 | test_vector, 339 | test_string, 340 | test_deque, 341 | test_list>{}; 342 | 343 | constexpr auto inplace_back_constructing = require_inplace_back_constructing_containers< 344 | test_value_type, 345 | 346 | test_vector, 347 | test_deque, 348 | test_list>{}; 349 | 350 | 351 | // Associative containers 352 | 353 | using test_key_type = std::string; 354 | using test_kv_type = std::pair; 355 | 356 | using test_set = std::set; 357 | using test_multiset = std::multiset; 358 | using test_unordered_set = std::unordered_set; 359 | using test_unordered_multiset = std::unordered_multiset; 360 | using test_map = std::map; 361 | using test_multimap = std::multimap; 362 | using test_unordered_map = std::unordered_map; 363 | using test_unordered_multimap = std::unordered_multimap; 364 | 365 | constexpr auto ordered_unique_sets = require_ordered_unique_associative_containers< 366 | test_key_type, 367 | test_key_type, 368 | 369 | test_set>{}; 370 | 371 | constexpr auto ordered_multiple_sets = require_ordered_multiple_associative_containers< 372 | test_key_type, 373 | test_key_type, 374 | 375 | test_multiset>{}; 376 | 377 | constexpr auto unordered_unique_sets = require_unordered_unique_associative_containers< 378 | test_key_type, 379 | test_key_type, 380 | 381 | test_unordered_set>{}; 382 | 383 | constexpr auto unordered_multiple_sets = require_unordered_multiple_associative_containers< 384 | test_key_type, 385 | test_key_type, 386 | 387 | test_unordered_multiset>{}; 388 | 389 | constexpr auto ordered_unique_maps = require_ordered_unique_map_containers< 390 | test_kv_type, 391 | test_key_type, 392 | test_value_type, 393 | 394 | test_map>{}; 395 | 396 | constexpr auto ordered_multiple_maps = require_ordered_multiple_map_containers< 397 | test_kv_type, 398 | test_key_type, 399 | test_value_type, 400 | 401 | test_multimap>{}; 402 | 403 | constexpr auto unordered_unique_maps = require_unordered_unique_map_containers< 404 | test_kv_type, 405 | test_key_type, 406 | test_value_type, 407 | 408 | test_unordered_map>{}; 409 | 410 | constexpr auto unordered_multiple_maps = require_unordered_multiple_map_containers< 411 | test_kv_type, 412 | test_key_type, 413 | test_value_type, 414 | 415 | test_unordered_multimap>{}; 416 | } 417 | -------------------------------------------------------------------------------- /tests/more_concepts/test_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | auto main() -> int 4 | { 5 | return EXIT_SUCCESS; 6 | } 7 | -------------------------------------------------------------------------------- /tests/more_concepts/test_mock_iterator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "more_concepts/mock_iterator.hpp" 4 | 5 | namespace 6 | { 7 | using test_value_type = int; 8 | 9 | using mock_input_iterator = more_concepts::mock_const_iterator< 10 | test_value_type, std::input_iterator_tag>; 11 | using mock_forward_iterator = more_concepts::mock_const_iterator< 12 | test_value_type, std::forward_iterator_tag>; 13 | using mock_bidirectional_iterator = more_concepts::mock_const_iterator< 14 | test_value_type, std::bidirectional_iterator_tag>; 15 | using mock_random_access_iterator = more_concepts::mock_const_iterator< 16 | test_value_type, std::random_access_iterator_tag>; 17 | using mock_contiguous_iterator = more_concepts::mock_const_iterator< 18 | test_value_type, std::contiguous_iterator_tag>; 19 | 20 | using mock_output_iterator = more_concepts::mock_mutable_iterator< 21 | test_value_type, std::output_iterator_tag>; 22 | using mock_mutable_input_iterator = more_concepts::mock_mutable_iterator< 23 | test_value_type, std::input_iterator_tag>; 24 | using mock_mutable_forward_iterator = more_concepts::mock_mutable_iterator< 25 | test_value_type, std::forward_iterator_tag>; 26 | using mock_mutable_bidirectional_iterator = more_concepts::mock_mutable_iterator< 27 | test_value_type, std::bidirectional_iterator_tag>; 28 | using mock_mutable_random_access_iterator = more_concepts::mock_mutable_iterator< 29 | test_value_type, std::random_access_iterator_tag>; 30 | using mock_mutable_contiguous_iterator = more_concepts::mock_mutable_iterator< 31 | test_value_type, std::contiguous_iterator_tag>; 32 | } 33 | 34 | static_assert(std::input_iterator); 35 | static_assert(std::forward_iterator); 36 | static_assert(std::bidirectional_iterator); 37 | static_assert(std::random_access_iterator); 38 | static_assert(std::contiguous_iterator); 39 | 40 | static_assert(std::output_iterator); 41 | static_assert(std::output_iterator); 42 | static_assert(std::output_iterator); 43 | static_assert(std::output_iterator); 44 | static_assert(std::output_iterator); 45 | static_assert(std::output_iterator); 46 | 47 | static_assert(std::input_iterator); 48 | static_assert(std::forward_iterator); 49 | static_assert(std::bidirectional_iterator); 50 | static_assert(std::random_access_iterator); 51 | static_assert(std::contiguous_iterator); 52 | --------------------------------------------------------------------------------