├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── cmake └── mpark_patterns-config.cmake.in ├── include └── mpark │ ├── patterns.hpp │ └── patterns │ ├── anyof.hpp │ ├── as.hpp │ ├── config.hpp │ ├── detail │ ├── as_tuple.hpp │ ├── forwarder.hpp │ └── qualify_as.hpp │ ├── let.hpp │ ├── lib.hpp │ ├── match.hpp │ ├── optional.hpp │ ├── regex.hpp │ ├── vis.hpp │ └── when.hpp ├── support ├── single-header.py ├── wandbox.cpp └── wandbox.py └── test ├── CMakeLists.txt ├── README.md ├── aggregate.cpp ├── array.cpp ├── as.cpp ├── balance.cpp ├── calc.cpp ├── ds.cpp ├── identifier.cpp ├── intro.cpp ├── let.cpp ├── optional.cpp ├── regex.cpp ├── variadic.cpp └── when.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/googletest"] 2 | path = 3rdparty/googletest 3 | url = https://github.com/google/googletest.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | dist: xenial 3 | sudo: false 4 | 5 | branches: 6 | only: 7 | - master 8 | - dev 9 | 10 | git: 11 | depth: 1 12 | 13 | jobs: 14 | include: 15 | # ubuntu 16.04, gcc-7 16 | - env: VER=7 17 | compiler: gcc 18 | os: linux 19 | addons: { apt: { packages: ["g++-7"], sources: ["ubuntu-toolchain-r-test"] } } 20 | 21 | # ubuntu 16.04, gcc-8 22 | - env: VER=8 23 | compiler: gcc 24 | os: linux 25 | addons: { apt: { packages: ["g++-8"], sources: ["ubuntu-toolchain-r-test"] } } 26 | 27 | # ubuntu 16.04, clang-5.0 28 | - env: VER=5.0 29 | compiler: clang 30 | os: linux 31 | addons: { apt: { packages: ["clang-5.0"] } } 32 | 33 | # ubuntu 16.04, clang-6.0 34 | - env: VER=6.0 35 | compiler: clang 36 | os: linux 37 | addons: { apt: { packages: ["clang-6.0"] } } 38 | 39 | # ubuntu 16.04, clang-7 40 | - env: VER=7 41 | compiler: clang 42 | os: linux 43 | addons: { apt: { packages: ["clang-7"], sources: ["llvm-toolchain-xenial-7"] } } 44 | 45 | install: 46 | # Save the name of the compiler. 47 | - COMPILER=${CC} 48 | # Install newer cmake. 49 | - | 50 | if [ "${TRAVIS_OS_NAME}" = "osx" ]; then 51 | brew update 52 | brew upgrade cmake 53 | fi 54 | # Set the correct `CC` and `CXX` environment variables. 55 | - | 56 | if [ -n "${VER}" ]; then 57 | export CC="${CC}-${VER}" 58 | export CXX="${CXX}-${VER}" 59 | fi 60 | - ${CXX} --version 61 | # Install specific version of libc++. 62 | - | 63 | if [ "${TRAVIS_OS_NAME}" = "linux" ] && [ "${COMPILER}" = "clang" ] && [ -n "${VER}" ]; then 64 | LLVM_VER=$(echo "$(${CXX} --version)" | grep -Po "clang version \K([0-9]\.[0-9]\.[0-9])") 65 | 66 | LLVM_URL="http://llvm.org/releases/${LLVM_VER}/llvm-${LLVM_VER}.src.tar.xz" 67 | LIBCXX_URL="http://llvm.org/releases/${LLVM_VER}/libcxx-${LLVM_VER}.src.tar.xz" 68 | LIBCXXABI_URL="http://llvm.org/releases/${LLVM_VER}/libcxxabi-${LLVM_VER}.src.tar.xz" 69 | 70 | mkdir -p llvm-build llvm llvm/projects/libcxx llvm/projects/libcxxabi 71 | 72 | travis_retry wget -O - ${LLVM_URL} | tar --strip-components=1 -xJ -C llvm 73 | travis_retry wget -O - ${LIBCXX_URL} | tar --strip-components=1 -xJ -C llvm/projects/libcxx 74 | travis_retry wget -O - ${LIBCXXABI_URL} | tar --strip-components=1 -xJ -C llvm/projects/libcxxabi 75 | 76 | pushd llvm-build 77 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${DEPS} ../llvm 78 | cmake --build projects/libcxx --target install -- -j 2 79 | cmake --build projects/libcxxabi --target install -- -j 2 80 | popd 81 | 82 | export CXXFLAGS="${CXXFLAGS} -nostdinc++ -isystem ${DEPS}/include/c++/v1" 83 | export LDFLAGS="${LDFLAGS} -L${DEPS}/lib -lc++ -lc++abi" 84 | export LD_LIBRARY_PATH="${DEPS}/lib:${LD_LIBRARY_PATH}" 85 | fi 86 | 87 | script: 88 | - BUILDS=(Debug Release) 89 | - EXS=(ON OFF) 90 | - mkdir build 91 | - pushd build 92 | - | 93 | for BUILD in "${BUILDS[@]}"; do 94 | for EX in "${EXS[@]}"; do 95 | ( 96 | set -x 97 | cmake -DCMAKE_BUILD_TYPE="${BUILD}" \ 98 | -DMPARK_PATTERNS_EXCEPTIONS="${EX}" \ 99 | -DMPARK_PATTERNS_INCLUDE_TESTS=ON .. 100 | ) 101 | cmake --build . -- -j 2 102 | ctest -V 103 | done 104 | done 105 | - popd 106 | 107 | notifications: 108 | email: false 109 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MPark.Patterns 2 | # 3 | # Copyright Michael Park, 2017 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | cmake_minimum_required(VERSION 3.6.3) 9 | 10 | project(MPark.Patterns VERSION 0.1.0 LANGUAGES CXX) 11 | 12 | # Option. 13 | option(MPARK_PATTERNS_INCLUDE_TESTS "Build tests." OFF) 14 | 15 | # Target. 16 | add_library(mpark_patterns INTERFACE) 17 | target_include_directories(mpark_patterns INTERFACE 18 | $ 19 | $) 20 | 21 | # Config. 22 | include(CMakePackageConfigHelpers) 23 | 24 | configure_package_config_file( 25 | cmake/mpark_patterns-config.cmake.in 26 | "${CMAKE_CURRENT_BINARY_DIR}/cmake/mpark_patterns-config.cmake" 27 | INSTALL_DESTINATION lib/cmake/mpark_patterns 28 | NO_CHECK_REQUIRED_COMPONENTS_MACRO) 29 | 30 | write_basic_package_version_file( 31 | "${CMAKE_CURRENT_BINARY_DIR}/cmake/mpark_patterns-config-version.cmake" 32 | COMPATIBILITY AnyNewerVersion) 33 | 34 | # Export. 35 | export( 36 | TARGETS mpark_patterns 37 | FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/mpark_patterns-targets.cmake") 38 | 39 | # Install. 40 | install(TARGETS mpark_patterns EXPORT mpark_patterns) 41 | 42 | install( 43 | EXPORT mpark_patterns 44 | FILE mpark_patterns-targets.cmake 45 | DESTINATION lib/cmake/mpark_patterns) 46 | 47 | install(DIRECTORY include/mpark DESTINATION include) 48 | 49 | install( 50 | FILES 51 | "${CMAKE_CURRENT_BINARY_DIR}/cmake/mpark_patterns-config.cmake" 52 | "${CMAKE_CURRENT_BINARY_DIR}/cmake/mpark_patterns-config-version.cmake" 53 | DESTINATION lib/cmake/mpark_patterns) 54 | 55 | if(MPARK_PATTERNS_INCLUDE_TESTS) 56 | enable_testing() 57 | add_subdirectory(test) 58 | endif() 59 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MPark.Patterns 2 | 3 | > This is an experimental library that has evolved to [P2688], being proposed for C++26. 4 | 5 | [P2688]: https://wg21.link/p2688 6 | 7 | [![release][badge.release]][release] 8 | [![travis][badge.travis]][travis] 9 | [![license][badge.license]][license] 10 | [![godbolt][badge.godbolt]][godbolt] 11 | [![wandbox][badge.wandbox]][wandbox] 12 | 13 | [badge.release]: https://img.shields.io/github/release/mpark/patterns.svg 14 | [badge.travis]: https://travis-ci.org/mpark/patterns.svg?branch=master 15 | [badge.license]: http://img.shields.io/badge/license-boost-blue.svg 16 | [badge.godbolt]: https://img.shields.io/badge/try%20it-on%20godbolt-222266.svg 17 | [badge.wandbox]: https://img.shields.io/badge/try%20it-on%20wandbox-5cb85c.svg 18 | 19 | [release]: https://github.com/mpark/patterns/releases/latest 20 | [travis]: https://travis-ci.org/mpark/patterns 21 | [license]: https://github.com/mpark/patterns/blob/master/LICENSE.md 22 | [godbolt]: https://godbolt.org/g/b7WTDt 23 | [wandbox]: https://wandbox.org/permlink/bm5oBKAuHoSucEYG 24 | 25 | ## Introduction 26 | 27 | __MPark.Patterns__ is an experimental pattern matching library for __C++17__. 28 | 29 | It determines whether a given value __matches__ a __pattern__ and, if it does, 30 | __binds__ the desired portions of the value to a __handler__. 31 | 32 | Pattern matching has been introduced to many programming languages outside of 33 | the functional world, and this library draws inspiration from languages such as 34 | Haskell, OCaml, Rust, Scala, and Swift. 35 | 36 | ```cpp 37 | #include 38 | 39 | #include 40 | 41 | void fizzbuzz() { 42 | using namespace mpark::patterns; 43 | for (int i = 1; i <= 100; ++i) { 44 | match(i % 3, i % 5)( 45 | pattern(0, 0) = [] { std::printf("fizzbuzz\n"); }, 46 | pattern(0, _) = [] { std::printf("fizz\n"); }, 47 | pattern(_, 0) = [] { std::printf("buzz\n"); }, 48 | pattern(_, _) = [i] { std::printf("%d\n", i); }); 49 | } 50 | } 51 | 52 | int main() { 53 | fizzbuzz(); 54 | } 55 | ``` 56 | 57 | ## Basic Syntax 58 | 59 | ```cpp 60 | using namespace mpark::patterns; 61 | IDENTIFIERS(...); // optional 62 | match(...)( 63 | pattern(...) = [](...) { /* ... */ }, 64 | pattern(...) = [](...) { /* ... */ }, 65 | // ... 66 | ); 67 | ``` 68 | 69 | ## Types of Patterns 70 | 71 | ### Expression Pattern 72 | 73 | An _expression pattern_ matches if the value compares equal to 74 | the given expression. 75 | 76 | #### Requirements 77 | 78 | Given an expression `e` and a value `x`, `e == x` must be a valid expression. 79 | 80 | #### Syntax 81 | 82 | - `` 83 | 84 | #### Examples 85 | 86 | ```cpp 87 | int factorial(int n) { 88 | using namespace mpark::patterns; 89 | return match(n)(pattern(0) = [] { return 1; }, 90 | // ^ expression 91 | pattern(_) = [n] { return n * factorial(n - 1); }); 92 | } 93 | ``` 94 | 95 | ### Arg Pattern 96 | 97 | An _arg pattern_ matches any value, and __binds__ the value by passing it 98 | to the corresponding handler. This pattern can be repeated in a single pattern, 99 | and they match independent of each other. 100 | 101 | #### Requirements 102 | 103 | None. 104 | 105 | #### Syntax 106 | 107 | - `arg` 108 | - `arg()` 109 | 110 | #### Examples 111 | 112 | ```cpp 113 | int factorial(int n) { 114 | using namespace mpark::patterns; 115 | return match(n)(pattern(0) = [] { return 1; }, 116 | pattern(arg) = [](auto n) { return n * factorial(n - 1); }); 117 | // ^^^ arg 118 | } 119 | ``` 120 | 121 | ```cpp 122 | using P = std::pair; 123 | P p = {101, 202}; 124 | 125 | using namespace mpark::patterns; 126 | match(p)(pattern(arg(ds(101, _))) = [](P &p) { std::cout << "pair!\n"; }, 127 | // ^^^ pass the pair! 128 | pattern(_) = [] { std::cout << "not a pair\n"; }); 129 | // prints: "pair!" 130 | ``` 131 | 132 | ### Wildcard Pattern 133 | 134 | A _wildcard pattern_ matches any value and __discards__ the value by __not__ 135 | passing it to the corresponding handler. This pattern can be repeated within 136 | a single pattern, and they match independent of each other. 137 | 138 | #### Requirements 139 | 140 | None. 141 | 142 | #### Syntax 143 | 144 | - `_` (underscore) 145 | 146 | #### Examples 147 | 148 | ```cpp 149 | int factorial(int n) { 150 | using namespace mpark::patterns; 151 | return match(n)(pattern(0) = [] { return 1; }, 152 | pattern(_) = [n] { return n * factorial(n - 1); }); 153 | // ^ wildcard 154 | } 155 | ``` 156 | 157 | ### Identifier Pattern 158 | 159 | An _identifier pattern_ is similar to `arg` and `_` in that it matches any value. 160 | 161 | Two major differences are: 162 | 1. Ability to operate on __names__ rather than just `arg` or `_`. 163 | 2. When an identifier is repeated within a single pattern, the values bound 164 | to the repeated identifiers must compare equal for the pattern to match. 165 | 166 | There are two types of identifier patterns: __binding__ and __discarding__. 167 | 168 | #### Requirements 169 | 170 | If no identifiers are repeated, none. 171 | 172 | Otherwise, let `x` be a repeated identifer and `v, vs...` be the group of values 173 | matched by the repeated instances of `x`. Then `(... && (v == vs))` must be a 174 | valid expression. The same requirement applies to any other repeated identifiers 175 | in a single pattern. 176 | 177 | #### Syntax 178 | 179 | - `IDENTIFIERS(...);` 180 | - `` 181 | - `()` 182 | 183 | If `` has a leading `_` (underscore), it is a __discarding__ 184 | identifier. Otherwise, it is a __binding__ identifier. 185 | 186 | #### Examples 187 | 188 | ```cpp 189 | void is_same(int lhs, int rhs) { 190 | using namespace mpark::patterns; 191 | IDENTIFIERS(x, y); 192 | match(lhs, rhs)( 193 | pattern(x, x) = [](auto) { std::cout << "same\n"; }, 194 | // ^ ^ binding identifier (repeated) 195 | pattern(x, y) = [](auto, auto) { std::cout << "diff\n"; } 196 | // ^ ^ binding identifier 197 | ); 198 | } 199 | 200 | is_same(101, 101); // prints: "same" 201 | is_same(101, 202); // prints: "diff" 202 | ``` 203 | 204 | ```cpp 205 | void is_same(int lhs, rhs) { 206 | using namespace mpark::patterns; 207 | IDENTIFIERS(_x, _y); 208 | match(42, 42)( 209 | pattern(_x, _x) = [] { std::cout << "same\n"; }, 210 | // ^ ^ discarding identifier (repeated) 211 | pattern(_x, _y) = [] { std::cout << "diff\n"; } 212 | // ^ ^ discarding identifier 213 | ); 214 | } 215 | 216 | is_same(101, 101); // prints: "same" 217 | is_same(101, 202); // prints: "diff" 218 | ``` 219 | 220 | ```cpp 221 | std::tuple t = {101, 202}; 222 | std::optional o = 101; 223 | 224 | using namespace mpark::patterns; 225 | IDENTIFIERS(x, y); 226 | match(t, o)( 227 | pattern(ds(x, x), some(x)) = [](auto x) { 228 | std::cout << "They're all " << x << "!\n"; 229 | }, 230 | pattern(ds(x, y), some(x)) = [](auto x, auto y) { 231 | std::cout << "I recognize x-y-x!\n"; 232 | }, 233 | pattern(_) = [] { std::cout << "I don't know.\n"; }); 234 | // prints: "I recognize x-y-x!" 235 | ``` 236 | 237 | ### Destructure Pattern 238 | 239 | A _destructure pattern_ matches values that hold multiple values. 240 | 241 | #### Requirements 242 | 243 | Given a value `x` of type `T`, `T` must satisfy one of the following conditions: 244 | 1. `std::is_array_v == true`, or 245 | 2. `std::is_class_v == true` and `std::tuple_size` is a complete type, or 246 | 3. `std::is_aggregate_v == true` 247 | 248 | If (2), the following conditions must also be satisfied. 249 | - `std::tuple_size::value` must be a well-formed 250 | integer constant expression, and 251 | - `x.get()` or `get(x)` must be a valid expression for all `I` in 252 | `[0, std::tuple_size::value)` 253 | 254 | For aggregate types (3), the current implementation only supports types that 255 | contain upto 16 non-static data members. 256 | 257 | __NOTE__: These requirements are very similar to the requirements for 258 | [C++17 Structured Binding][structured-binding]. 259 | 260 | [structured-binding]: http://en.cppreference.com/w/cpp/language/structured_binding 261 | 262 | #### Syntax 263 | 264 | - `ds(...)` 265 | 266 | #### Examples 267 | 268 | ```cpp 269 | auto t = std::make_tuple(101, "hello", 1.1); 270 | 271 | // C++17 Structured Binding: 272 | const auto & [x, y, z] = t; 273 | // ... 274 | 275 | // MPark.Patterns: 276 | using namespace mpark::patterns; 277 | IDENTIFIERS(x, y, z); 278 | match(t)( 279 | pattern(ds(x, y, z)) = [](auto &&x, auto &&y, auto &&z) { /* ... */ }); 280 | // ^^^^^^^^^^^ destructure 281 | ``` 282 | 283 | __NOTE__: The top-level is wrapped by a `tuple`, allowing us to write: 284 | 285 | ```cpp 286 | void fizzbuzz() { 287 | for (int i = 1; i <= 100; ++i) { 288 | using namespace mpark::patterns; 289 | match(i % 3, i % 5)( 290 | pattern(0, 0) = [] { std::cout << "fizzbuzz\n"; }, 291 | pattern(0, _) = [] { std::cout << "fizz\n"; }, 292 | pattern(_, 0) = [] { std::cout << "buzz\n"; }, 293 | pattern(_, _) = [i] { std::cout << i << '\n'; }); 294 | } 295 | } 296 | ``` 297 | 298 | ### Optional Pattern 299 | 300 | An _optional pattern_ matches values that can be dereferenced, 301 | and tested as a `bool`. 302 | 303 | #### Requirements 304 | 305 | Given a value `x` of type `T`, `T` must satisfy all of the following conditions: 306 | - `*x` is a valid expression, and 307 | - `x` is contextually convertible to `bool` 308 | 309 | #### Syntax 310 | 311 | - `some()` 312 | - `none` 313 | 314 | #### Examples 315 | 316 | ```cpp 317 | int *p = nullptr; 318 | 319 | using namespace mpark::patterns; 320 | match(p)(pattern(some(_)) = [] { std::cout << "some\n"; }, 321 | pattern(none) = [] { std::cout << "none\n"; }); 322 | // prints: "none" 323 | ``` 324 | 325 | ```cpp 326 | std::optional o = 42; 327 | 328 | using namespace mpark::patterns; 329 | match(o)( 330 | pattern(some(arg)) = [](auto x) { std::cout << "some(" << x << ")\n"; }, 331 | pattern(none) = [] { std::cout << "none\n"; }); 332 | // prints: "some(42)" 333 | ``` 334 | 335 | ### As Pattern 336 | 337 | An _as pattern_ matches values that is capable of holding a value of multiple types. 338 | 339 | #### Requirements 340 | 341 | Given a value `x` of type `T`, and a pattern `as()`, 342 | `T` must satisfy one of the following conditions: 343 | 1. `std::is_polymorphic_v == true`, or 344 | 2. `std::is_class_v == true` and `std::variant_size` is a complete type, or 345 | 3. `x.any_cast()` or `any_cast(&x)` is a valid expression 346 | 347 | - If (1), `dynamic_cast(&x)` must be a valid expression, 348 | where `U'` is pointer to `U` with the same cv-qualifiers as `decltype(x)` 349 | - If (2), `x.get_if()` or `get_if(&x)` must be a valid expression 350 | 351 | #### Syntax 352 | 353 | - `as()` 354 | 355 | #### Examples 356 | 357 | ```cpp 358 | struct Shape { virtual ~Shape() = default; }; 359 | struct Circle : Shape {}; 360 | struct Square : Shape {}; 361 | 362 | std::unique_ptr shape = std::make_unique(); 363 | 364 | using namespace mpark::patterns; 365 | match(shape)(pattern(some(as(_))) = [] { std::cout << "Circle\n"; }, 366 | pattern(some(as(_))) = [] { std::cout << "Square\n"; }); 367 | // prints: "Square" 368 | ``` 369 | 370 | ```cpp 371 | using str = std::string; 372 | std::variant v = 42; 373 | 374 | using namespace mpark::patterns; 375 | match(v)(pattern(as(_)) = [] { std::cout << "int\n"; }, 376 | pattern(as(_)) = [] { std::cout << "str\n"; }); 377 | // prints: "int" 378 | ``` 379 | 380 | ```cpp 381 | using str = std::string; 382 | std::any a = str("hello"); 383 | 384 | using namespace mpark::patterns; 385 | match(a)(pattern(as(_)) = [] { std::cout << "int\n"; }, 386 | pattern(as(_)) = [] { std::cout << "str\n"; }); 387 | // prints: "str" 388 | ``` 389 | 390 | ### Visit Pattern 391 | 392 | A _visit pattern_ matches values that is capable of holding a value of 393 | closed set of types, and can be visited. 394 | 395 | #### Requirements 396 | 397 | Given a value `x`, The following code must be valid. 398 | 399 | ``` 400 | using std::visit; 401 | visit([](auto &&) {}, x);` 402 | ``` 403 | 404 | #### Syntax 405 | 406 | - `vis()` 407 | 408 | #### Examples 409 | 410 | ```cpp 411 | using str = std::string; 412 | std::variant v = "hello world!"; 413 | 414 | struct Visitor { 415 | void operator()(int n) const { std::cout << "int: " << n << '\n'; } 416 | void operator()(const str &s) const { std::cout << "str: " << s << '\n'; } 417 | }; 418 | 419 | using namespace mpark::patterns; 420 | match(v)(pattern(vis(arg)) = Visitor{}); 421 | // prints: "str: hello world!". 422 | ``` 423 | 424 | ### Alternation Pattern 425 | 426 | An _alternation pattern_ matches values that match any of the given patterns. 427 | 428 | #### Requirements 429 | 430 | None. 431 | 432 | #### Syntax 433 | 434 | - `anyof(...)` 435 | 436 | #### Examples 437 | 438 | ```cpp 439 | std::string s = "large"; 440 | 441 | using namespace mpark::patterns; 442 | match(s)( 443 | pattern(anyof("big", "large", "huge")) = [] { std::cout << "big!\n"; }, 444 | pattern(anyof("little", "small", "tiny")) = [] { std::cout << "small.\n"; }, 445 | pattern(_) = [] { std::cout << "unknown size.\n"; }); 446 | ``` 447 | 448 | ### Variadic Pattern 449 | 450 | A _variadic pattern_ matches 0 or more values that match a given pattern. 451 | 452 | #### Requirements 453 | 454 | - A _variadic pattern_ can only appear within a destructure pattern. 455 | - A _variadic pattern_ can only appear once. 456 | 457 | #### Syntax 458 | 459 | - `variadic()` 460 | 461 | #### Examples 462 | 463 | ```cpp 464 | auto x = std::make_tuple(101, "hello", 1.1); 465 | 466 | using namespace mpark::patterns; 467 | match(x)( 468 | pattern(ds(variadic(arg))) = [](const auto &... xs) { 469 | int dummy[] = { (std::cout << xs << ' ', 0)... }; 470 | (void)dummy; 471 | }); 472 | // prints: "101 hello 1.1 " 473 | ``` 474 | 475 | This could also be used to implement [C++17 `std::apply`][apply]: 476 | 477 | [apply]: http://en.cppreference.com/w/cpp/utility/apply 478 | 479 | ```cpp 480 | template 481 | decltype(auto) apply(F &&f, Tuple &&t) { 482 | using namespace mpark::patterns; 483 | return match(std::forward(t))( 484 | pattern(ds(variadic(arg))) = std::forward(f)); 485 | } 486 | ``` 487 | 488 | and even [C++17 `std::visit`][visit]: 489 | 490 | [visit]: http://en.cppreference.com/w/cpp/utility/variant/visit 491 | 492 | ```cpp 493 | template 494 | decltype(auto) visit(F &&f, Vs &&... vs) { 495 | using namespace mpark::patterns; 496 | return match(std::forward(vs)...)( 497 | pattern(variadic(vis(arg))) = std::forward(f)); 498 | } 499 | ``` 500 | 501 | We can even get a little fancier: 502 | 503 | ```cpp 504 | int x = 42; 505 | auto y = std::make_tuple(101, "hello", 1.1); 506 | 507 | using namespace mpark::patterns; 508 | match(x, y)( 509 | pattern(arg, ds(variadic(arg))) = [](const auto &... xs) { 510 | int dummy[] = { (std::cout << xs << ' ', 0)... }; 511 | (void)dummy; 512 | }); 513 | // prints: "42 101 hello 1.1 " 514 | ``` 515 | 516 | ## Pattern Guards 517 | 518 | While pattern matching is very useful to match values against patterns, 519 | it mainly focuses on equality comparisons. Pattern guards are used to 520 | test whether the bound values satisfy some predicate. 521 | 522 | ### `WHEN` 523 | 524 | A `WHEN` clause appears inside the handler as the only expression. 525 | A `WHEN` clause differs from an `if` statement in that if the conditional 526 | expression evaluates to `false`, the `WHEN` clause falls through to the 527 | next case, whereas the `if` statement would stay in the handler. 528 | 529 | #### Requirements 530 | 531 | The `WHEN` clause may only appear once inside the handler as the only 532 | expression, and the conditional expression must be contextually 533 | convertible to `bool`. 534 | 535 | #### Syntax 536 | 537 | - `WHEN() { /* ... */ };` 538 | 539 | #### Examples 540 | 541 | ```cpp 542 | using namespace mpark::patterns; 543 | IDENTIFIERS(x, y); 544 | match(101, 202)( 545 | pattern(x, x) = [](int) { std::cout << "EQ\n"; }, 546 | pattern(x, y) = [](int x, int y) { WHEN(x > y) { std::cout << "GT\n"; }; }, 547 | pattern(x, y) = [](int x, int y) { WHEN(x < y) { std::cout << "LT\n"; }; }); 548 | // prints: "LT" 549 | ``` 550 | 551 | ## Pattern Statements 552 | 553 | ### `if_let` 554 | 555 | This construct is simply syntactic sugar for the following situation: 556 | 557 | ```cpp 558 | std::optional o = 42; 559 | 560 | using namespace mpark::patterns; 561 | match(o)( 562 | pattern(some(arg)) = [](auto x) { ... }, 563 | pattern(_) = [] {}); 564 | ``` 565 | 566 | For this situation, `if_let` provides a cleaner interface: 567 | 568 | ```cpp 569 | std::optional o = 42; 570 | 571 | using namespace mpark::patterns; 572 | if_let (pattern(some(arg)) = o) = [](auto x) { 573 | // ... 574 | }; 575 | ``` 576 | 577 | ### `for_let` 578 | 579 | Suppose we want to match each element in a range-based `for` loop with pattern. 580 | We could imagine being able to write something like: 581 | 582 | ```cpp 583 | std::vector> pairs = {{0, 1}, {1, 1}, {0, 0}, {0, 2}}; 584 | 585 | for ((0, x) : pairs) { 586 | std::cout << x << ' '; 587 | } 588 | // prints: "1 0 2 " 589 | ``` 590 | 591 | With `if_let` we can say something close-ish: 592 | 593 | ```cpp 594 | std::vector> pairs = {{0, 1}, {1, 1}, {0, 0}, {0, 2}}; 595 | 596 | for (const auto &p : pairs) { 597 | using namespace mpark::patterns; 598 | if_let (pattern(ds(0, arg)) = p) = [](auto x) { 599 | std::cout << x << ' '; 600 | }; 601 | } 602 | // prints: "1 0 2 " 603 | ``` 604 | 605 | `for_let` allows the above example to be written more concisely: 606 | 607 | ```cpp 608 | std::vector> pairs = {{0, 1}, {1, 1}, {0, 0}, {0, 2}}; 609 | 610 | using namespace mpark::patterns; 611 | for_let (pattern(ds(0, arg)) = pairs) = [](auto x) { 612 | std::cout << x << ' '; 613 | }; 614 | // prints: "1 0 2 " 615 | ``` 616 | 617 | `for_let` also provides the `break` and `continue` statements with 618 | `return Break;` and `return Continue;` respectively. 619 | 620 | ```cpp 621 | std::vector> pairs = {{0, 1}, {1, 1}, {0, 0}, {0, 2}}; 622 | 623 | using namespace mpark::patterns; 624 | for_let (pattern(ds(0, arg)) = pairs) = [](auto x) { 625 | if (x == 1) return Break; 626 | std::cout << x << ' '; 627 | return Continue; 628 | }; 629 | // prints: "0 " 630 | ``` 631 | 632 | ```cpp 633 | std::vector> pairs = {{0, 1}, {1, 1}, {0, 0}, {0, 2}}; 634 | 635 | using namespace mpark::patterns; 636 | for_let (pattern(ds(0, arg)) = pairs) = [](auto x) { 637 | if (x == 0) return Continue; 638 | std::cout << x << ' '; 639 | return Continue; 640 | }; 641 | // prints: "1 2 " 642 | ``` 643 | 644 | ## Requirements 645 | 646 | This library requires a standard conformant __C++17__ compiler. 647 | The following compilers are continously tested: 648 | 649 | | Compiler | Operating System | Version String | 650 | |-------------|--------------------|------------------------------------------------------------------------------| 651 | | GCC 7.4.0 | Ubuntu 16.04.6 LTS | g++-7 (Ubuntu 7.4.0-1ubuntu1~16.04~ppa1) 7.4.0 | 652 | | GCC 8.1.0 | Ubuntu 16.04.6 LTS | g++-8 (Ubuntu 8.1.0-5ubuntu1~16.04) 8.1.0 | 653 | | Clang 5.0.0 | Ubuntu 16.04.6 LTS | clang version 5.0.0-3~16.04.1 (tags/RELEASE_500/final) | 654 | | Clang 6.0.0 | Ubuntu 16.04.6 LTS | clang version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) | 655 | | Clang 7.1.0 | Ubuntu 16.04.6 LTS | clang version 7.1.0-svn353565-1~exp1~20190408084827.60 (branches/release_70) | 656 | 657 | ## CMake Variables 658 | 659 | - __`MPARK_PATTERNS_INCLUDE_TESTS`__:`BOOL` (__default__: `OFF`) 660 | 661 | Build tests. 662 | 663 | ## Unit Tests 664 | 665 | Refer to [test/README.md](test/README.md). 666 | 667 | ## License 668 | 669 | Distributed under the [Boost Software License, Version 1.0](LICENSE.md). 670 | 671 | ## Related Work 672 | 673 | - [solodon4/Mach7](https://github.com/solodon4/Mach7) 674 | - [jbandela/simple_match](https://github.com/jbandela/simple_match/) 675 | * [N3449: Open and Efficient Type Switch for C++](https://wg21.link/N3449) 676 | * [P0144: Structured bindings](https://wg21.link/P0144) 677 | - [P1260: Pattern Matching](https://wg21.link/P1260) 678 | - [P1308: Pattern Matching](https://wg21.link/P1308) 679 | - [P1371: Pattern Matching](https://wg21.link/P1371) 680 | - [P2392: Pattern Matching using `is` and `as`](https://wg21.link/P2392) 681 | -------------------------------------------------------------------------------- /cmake/mpark_patterns-config.cmake.in: -------------------------------------------------------------------------------- 1 | # MPark.Patterns 2 | # 3 | # Copyright Michael Park, 2017 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | # Config file for MPark.Patterns 9 | # 10 | # `MPARK_PATTERNS_INCLUDE_DIRS` - include directories 11 | # `MPARK_PATTERNS_LIBRARIES` - libraries to link against 12 | # 13 | # The following `IMPORTED` target is also defined: 14 | # 15 | # `mpark_patterns` 16 | 17 | @PACKAGE_INIT@ 18 | 19 | include("${CMAKE_CURRENT_LIST_DIR}/mpark_patterns-targets.cmake") 20 | 21 | get_target_property( 22 | MPARK_PATTERNS_INCLUDE_DIRS 23 | mpark_patterns INTERFACE_INCLUDE_DIRECTORIES) 24 | 25 | set_and_check(MPARK_PATTERNS_INCLUDE_DIRS "${MPARK_PATTERNS_INCLUDE_DIRS}") 26 | set(MPARK_PATTERNS_LIBRARIES mpark_patterns) 27 | -------------------------------------------------------------------------------- /include/mpark/patterns.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_HPP 9 | #define MPARK_PATTERNS_HPP 10 | 11 | #include "patterns/match.hpp" 12 | 13 | #include "patterns/anyof.hpp" 14 | #include "patterns/as.hpp" 15 | #include "patterns/let.hpp" 16 | #include "patterns/optional.hpp" 17 | #include "patterns/regex.hpp" 18 | #include "patterns/vis.hpp" 19 | #include "patterns/when.hpp" 20 | 21 | #endif // MPARK_PATTERNS_HPP 22 | -------------------------------------------------------------------------------- /include/mpark/patterns/anyof.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_ANYOF_HPP 9 | #define MPARK_PATTERNS_ANYOF_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace mpark::patterns { 16 | 17 | template 18 | struct Anyof { std::tuple patterns; }; 19 | 20 | template 21 | auto anyof(const Patterns &... patterns) noexcept { 22 | return Anyof{std::tie(patterns...)}; 23 | } 24 | 25 | namespace detail { 26 | 27 | template 28 | auto try_match_impl(const Anyof &anyof, 29 | Value &&value, 30 | F &&f, 31 | std::index_sequence) { 32 | return try_match(std::get(anyof.patterns), 33 | std::forward(value), 34 | [&](auto &&... args) { 35 | return match_invoke( 36 | std::forward(f), 37 | std::forward(args)...); 38 | }); 39 | } 40 | 41 | template 47 | auto try_match_impl(const Anyof &anyof, 48 | Value &&value, 49 | F &&f, 50 | std::index_sequence) { 51 | auto result = try_match(std::get(anyof.patterns), 52 | std::forward(value), 53 | [&](auto &&... args) { 54 | return match_invoke( 55 | std::forward(f), 56 | std::forward(args)...); 57 | }); 58 | return result ? std::move(result) 59 | : try_match_impl(anyof, 60 | std::forward(value), 61 | std::forward(f), 62 | std::index_sequence{}); 63 | } 64 | 65 | } // namespace detail 66 | 67 | template 68 | auto try_match(const Anyof &anyof, Value &&value, F &&f) { 69 | return detail::try_match_impl(anyof, 70 | std::forward(value), 71 | std::forward(f), 72 | std::index_sequence_for{}); 73 | } 74 | 75 | } // namespace mpark::patterns 76 | 77 | #endif // MPARK_PATTERNS_ANYOF_HPP 78 | -------------------------------------------------------------------------------- /include/mpark/patterns/as.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_AS_HPP 9 | #define MPARK_PATTERNS_AS_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "lib.hpp" 17 | 18 | namespace mpark::patterns { 19 | 20 | namespace detail { 21 | 22 | using std::get_if; 23 | 24 | template ().template get_if())> 27 | constexpr DetectResult detect_get_if(lib::priority<0>) noexcept { 28 | return DetectResult::Member; 29 | } 30 | 31 | template < 32 | typename T, 33 | typename V, 34 | typename = decltype(get_if(std::addressof(std::declval())))> 35 | constexpr DetectResult detect_get_if(lib::priority<1>) noexcept { 36 | return DetectResult::NonMember; 37 | } 38 | 39 | template 40 | constexpr DetectResult detect_get_if(lib::priority<2>) noexcept { 41 | return DetectResult::None; 42 | } 43 | 44 | template 45 | inline constexpr DetectResult detect_get_if_v = 46 | detect_get_if(lib::priority<>{}); 47 | 48 | using std::any_cast; 49 | 50 | template ().template any_cast())> 53 | constexpr DetectResult detect_any_cast(lib::priority<0>) noexcept { 54 | return DetectResult::Member; 55 | } 56 | 57 | template < 58 | typename T, 59 | typename V, 60 | typename = decltype(any_cast(std::addressof(std::declval())))> 61 | constexpr DetectResult detect_any_cast(lib::priority<1>) noexcept { 62 | return DetectResult::NonMember; 63 | } 64 | 65 | template 66 | constexpr DetectResult detect_any_cast(lib::priority<2>) noexcept { 67 | return DetectResult::None; 68 | } 69 | 70 | template 71 | inline constexpr DetectResult detect_any_cast_v = 72 | detect_any_cast(lib::priority<>{}); 73 | 74 | } // namespace detail 75 | 76 | template 77 | struct As { const Pattern &pattern; }; 78 | 79 | template 80 | auto as(const Pattern &pattern) noexcept { return As{pattern}; } 81 | 82 | template , int> = 0, 84 | std::size_t = sizeof(std::variant_size)> 85 | constexpr bool is_variant_like(lib::priority<0>) noexcept { return true; } 86 | 87 | template 88 | constexpr bool is_variant_like(lib::priority<1>) noexcept { return false; } 89 | 90 | template 91 | inline constexpr bool is_variant_like_v = 92 | is_variant_like(lib::priority<>{}); 93 | 94 | template 95 | auto try_match(const As &as, Value &&value, F &&f) { 96 | auto &&v = [&]() -> decltype(auto) { 97 | if constexpr (std::is_polymorphic_v>) { 98 | return dynamic_cast< 99 | std::add_pointer_t>>( 100 | std::addressof(value)); 101 | } else if constexpr (is_variant_like_v>) { 102 | constexpr auto result = detail::detect_get_if_v; 103 | if constexpr (result == detail::DetectResult::Member) { 104 | return std::forward(value).template get_if(); 105 | } else if constexpr (result == detail::DetectResult::NonMember) { 106 | using std::get_if; 107 | return get_if(std::addressof(value)); 108 | } else { 109 | static_assert(lib::false_v, 110 | "The value attempting to be matched against an `as` " 111 | "pattern has a specialization for `std::variant_size`, " 112 | "but does not have a member nor non-member `get_if` " 113 | "function available."); 114 | } 115 | } else { 116 | constexpr auto result = detail::detect_any_cast_v; 117 | if constexpr (result == detail::DetectResult::Member) { 118 | return std::forward(value).template any_cast(); 119 | } else if constexpr (result == detail::DetectResult::NonMember) { 120 | using std::any_cast; 121 | return any_cast(std::addressof(value)); 122 | } else { 123 | static_assert( 124 | lib::false_v, 125 | "The value attempting to be matched against an `as` " 126 | "pattern is not polymorphic, variant-like, nor any-like."); 127 | } 128 | } 129 | }(); 130 | return v ? try_match(as.pattern, 131 | static_cast(v)), 133 | Value &&>>(*std::forward(v)), 134 | std::forward(f)) 135 | : no_match; 136 | } 137 | 138 | } // namespace mpark::patterns 139 | 140 | #endif // MPARK_PATTERNS_AS_HPP 141 | -------------------------------------------------------------------------------- /include/mpark/patterns/config.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_CONFIG_HPP 9 | #define MPARK_PATTERNS_CONFIG_HPP 10 | 11 | #if __cplusplus < 201703L 12 | #error "MPark.Patterns requires C++17 support." 13 | #endif 14 | 15 | #ifndef __has_feature 16 | #define __has_feature(x) 0 17 | #endif 18 | 19 | #if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \ 20 | (defined(_MSC_VER) && defined(_CPPUNWIND)) 21 | #define MPARK_PATTERNS_EXCEPTIONS 22 | #endif 23 | 24 | #endif // MPARK_PATTERNS_CONFIG_HPP 25 | -------------------------------------------------------------------------------- /include/mpark/patterns/detail/as_tuple.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_DETAIL_AS_TUPLE_HPP 9 | #define MPARK_PATTERNS_DETAIL_AS_TUPLE_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "../lib.hpp" 16 | #include "qualify_as.hpp" 17 | 18 | namespace mpark::patterns::detail { 19 | 20 | struct fill { 21 | template 22 | constexpr operator T &() const noexcept; 23 | }; 24 | 25 | #pragma GCC diagnostic push 26 | #pragma GCC diagnostic ignored "-Wmissing-field-initializers" 27 | template 30 | constexpr bool is_n_constructible(std::index_sequence, 31 | lib::priority<0>) { 32 | return true; 33 | } 34 | #pragma GCC diagnostic pop 35 | 36 | template 37 | constexpr bool is_n_constructible(std::index_sequence, 38 | lib::priority<1>) { 39 | return false; 40 | } 41 | 42 | template 43 | inline constexpr bool is_n_constructible_v = 44 | is_n_constructible(std::make_index_sequence{}, lib::priority<>{}); 45 | 46 | template 47 | struct aggregate_size_impl { 48 | template 49 | static constexpr std::optional impl() { 50 | constexpr std::size_t M = B + ((E - B) / 2); 51 | constexpr bool is_mid_constructible = is_n_constructible_v; 52 | if constexpr (B == M) { 53 | if constexpr (is_mid_constructible) { 54 | return M; 55 | } else { 56 | return std::nullopt; 57 | } 58 | } else if constexpr (is_mid_constructible) { 59 | // We recursve into `[M, E)` rather than `[M + 1, E)` 60 | // since `M` could be the answer. 61 | return impl(); 62 | } else if constexpr (constexpr auto lhs = impl()) { 63 | return lhs; 64 | } else if constexpr (constexpr auto rhs = impl()) { 65 | return rhs; 66 | } 67 | } 68 | }; 69 | 70 | template 71 | inline constexpr std::size_t aggregate_size_v = 72 | *aggregate_size_impl::template impl<0, sizeof(T) + 1>(); 73 | 74 | template 75 | struct aggregate_size : lib::size_constant> {}; 76 | 77 | template 78 | auto as_tuple_impl(Aggregate &&, lib::size_constant<0>) { 79 | return std::forward_as_tuple(); 80 | } 81 | 82 | #define FWD(x) static_cast>(x) 83 | 84 | template 85 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<1>) { 86 | using T = decltype(std::forward(aggregate)); 87 | auto && [x00] = 88 | std::forward(aggregate); 89 | return std::forward_as_tuple(FWD(x00)); 90 | } 91 | 92 | template 93 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<2>) { 94 | using T = decltype(std::forward(aggregate)); 95 | auto && [x00, x01] = 96 | std::forward(aggregate); 97 | return std::forward_as_tuple(FWD(x00), FWD(x01)); 98 | } 99 | 100 | template 101 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<3>) { 102 | using T = decltype(std::forward(aggregate)); 103 | auto && [x00, x01, x02] = 104 | std::forward(aggregate); 105 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02)); 106 | } 107 | 108 | template 109 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<4>) { 110 | using T = decltype(std::forward(aggregate)); 111 | auto && [x00, x01, x02, x03] = 112 | std::forward(aggregate); 113 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03)); 114 | } 115 | 116 | template 117 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<5>) { 118 | using T = decltype(std::forward(aggregate)); 119 | auto && [x00, x01, x02, x03, x04] = 120 | std::forward(aggregate); 121 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 122 | FWD(x04)); 123 | } 124 | 125 | template 126 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<6>) { 127 | using T = decltype(std::forward(aggregate)); 128 | auto && [x00, x01, x02, x03, x04, x05] = 129 | std::forward(aggregate); 130 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 131 | FWD(x04), FWD(x05)); 132 | } 133 | 134 | template 135 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<7>) { 136 | using T = decltype(std::forward(aggregate)); 137 | auto && [x00, x01, x02, x03, x04, x05, x06] = 138 | std::forward(aggregate); 139 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 140 | FWD(x04), FWD(x05), FWD(x06)); 141 | } 142 | 143 | template 144 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<8>) { 145 | using T = decltype(std::forward(aggregate)); 146 | auto && [x00, x01, x02, x03, x04, x05, x06, x07] = 147 | std::forward(aggregate); 148 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 149 | FWD(x04), FWD(x05), FWD(x06), FWD(x07)); 150 | } 151 | 152 | template 153 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<9>) { 154 | using T = decltype(std::forward(aggregate)); 155 | auto && [x00, x01, x02, x03, x04, x05, x06, x07, 156 | x08] = 157 | std::forward(aggregate); 158 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 159 | FWD(x04), FWD(x05), FWD(x06), FWD(x07), 160 | FWD(x08)); 161 | } 162 | 163 | template 164 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<10>) { 165 | using T = decltype(std::forward(aggregate)); 166 | auto && [x00, x01, x02, x03, x04, x05, x06, x07, 167 | x08, x09] = 168 | std::forward(aggregate); 169 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 170 | FWD(x04), FWD(x05), FWD(x06), FWD(x07), 171 | FWD(x08), FWD(x09)); 172 | } 173 | 174 | template 175 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<11>) { 176 | using T = decltype(std::forward(aggregate)); 177 | auto && [x00, x01, x02, x03, x04, x05, x06, x07, 178 | x08, x09, x10] = 179 | std::forward(aggregate); 180 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 181 | FWD(x04), FWD(x05), FWD(x06), FWD(x07), 182 | FWD(x08), FWD(x09), FWD(x10)); 183 | } 184 | 185 | template 186 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<12>) { 187 | using T = decltype(std::forward(aggregate)); 188 | auto && [x00, x01, x02, x03, x04, x05, x06, x07, 189 | x08, x09, x10, x11] = 190 | std::forward(aggregate); 191 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 192 | FWD(x04), FWD(x05), FWD(x06), FWD(x07), 193 | FWD(x08), FWD(x09), FWD(x10), FWD(x11)); 194 | } 195 | 196 | template 197 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<13>) { 198 | using T = decltype(std::forward(aggregate)); 199 | auto && [x00, x01, x02, x03, x04, x05, x06, x07, 200 | x08, x09, x10, x11, x12] = 201 | std::forward(aggregate); 202 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 203 | FWD(x04), FWD(x05), FWD(x06), FWD(x07), 204 | FWD(x08), FWD(x09), FWD(x10), FWD(x11), 205 | FWD(x12)); 206 | } 207 | 208 | template 209 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<14>) { 210 | using T = decltype(std::forward(aggregate)); 211 | auto && [x00, x01, x02, x03, x04, x05, x06, x07, 212 | x08, x09, x10, x11, x12, x13] = 213 | std::forward(aggregate); 214 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 215 | FWD(x04), FWD(x05), FWD(x06), FWD(x07), 216 | FWD(x08), FWD(x09), FWD(x10), FWD(x11), 217 | FWD(x12), FWD(x13)); 218 | } 219 | 220 | template 221 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<15>) { 222 | using T = decltype(std::forward(aggregate)); 223 | auto && [x00, x01, x02, x03, x04, x05, x06, x07, 224 | x08, x09, x10, x11, x12, x13, x14] = 225 | std::forward(aggregate); 226 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 227 | FWD(x04), FWD(x05), FWD(x06), FWD(x07), 228 | FWD(x08), FWD(x09), FWD(x10), FWD(x11), 229 | FWD(x12), FWD(x13), FWD(x14)); 230 | } 231 | 232 | template 233 | auto as_tuple_impl(Aggregate &&aggregate, lib::size_constant<16>) { 234 | using T = decltype(std::forward(aggregate)); 235 | auto && [x00, x01, x02, x03, x04, x05, x06, x07, 236 | x08, x09, x10, x11, x12, x13, x14, x15] = 237 | std::forward(aggregate); 238 | return std::forward_as_tuple(FWD(x00), FWD(x01), FWD(x02), FWD(x03), 239 | FWD(x04), FWD(x05), FWD(x06), FWD(x07), 240 | FWD(x08), FWD(x09), FWD(x10), FWD(x11), 241 | FWD(x12), FWD(x13), FWD(x14), FWD(x15)); 242 | } 243 | 244 | #undef FWD 245 | 246 | template 247 | auto as_tuple(Aggregate &&aggregate) { 248 | return as_tuple_impl(std::forward(aggregate), 249 | aggregate_size>{}); 250 | } 251 | 252 | } // namespace mpark::patterns::detail 253 | 254 | #endif // MPARK_PATTERNS_DETAIL_AS_TUPLE_HPP 255 | -------------------------------------------------------------------------------- /include/mpark/patterns/detail/forwarder.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_DETAIL_FORWARDER_HPP 9 | #define MPARK_PATTERNS_DETAIL_FORWARDER_HPP 10 | 11 | #include 12 | #include 13 | 14 | namespace mpark::patterns::detail { 15 | 16 | struct void_ {}; 17 | 18 | template 19 | class forwarder { 20 | private: 21 | template 22 | static constexpr bool is_enabled() { 23 | return std::is_constructible_v && 24 | !(std::is_constructible_v &> || 25 | std::is_constructible_v &> || 26 | std::is_constructible_v &&> || 27 | std::is_constructible_v &&> || 28 | std::is_convertible_v &, T> || 29 | std::is_convertible_v &, T> || 30 | std::is_convertible_v &&, T> || 31 | std::is_convertible_v &&, T>); 32 | } 33 | 34 | template 35 | static constexpr bool enable_explicit() { 36 | return is_enabled() && !std::is_convertible_v; 37 | } 38 | 39 | template 40 | static constexpr bool enable_implicit() { 41 | return is_enabled() && std::is_convertible_v; 42 | } 43 | 44 | public: 45 | constexpr forwarder(T &&value) : value_(std::forward(value)) {} 46 | 47 | template (), int> = 0> 48 | explicit forwarder(forwarder &&that) 49 | : value_(std::move(that).forward()) {} 50 | 51 | template (), int> = 0> 52 | forwarder(forwarder &&that) : value_(std::move(that).forward()) {} 53 | 54 | forwarder(const forwarder &) = default; 55 | forwarder(forwarder &&) = default; 56 | 57 | forwarder &operator=(const forwarder &) = delete; 58 | forwarder &operator=(forwarder &&) = delete; 59 | 60 | constexpr T forward() && { return std::forward(value_); } 61 | 62 | private: 63 | T value_; 64 | }; 65 | 66 | template <> 67 | class forwarder { 68 | public: 69 | constexpr forwarder(void_) noexcept {} 70 | 71 | template 72 | forwarder(forwarder &&) noexcept {} 73 | 74 | forwarder(const forwarder &) noexcept = default; 75 | forwarder(forwarder &&) noexcept = default; 76 | 77 | forwarder &operator=(const forwarder &) noexcept = delete; 78 | forwarder &operator=(forwarder &&) noexcept = delete; 79 | 80 | constexpr void forward() && noexcept {} 81 | }; 82 | 83 | } // namespace mpark::patterns::detail 84 | 85 | #endif // MPARK_PATTERNS_DETAIL_FORWARDER_HPP 86 | -------------------------------------------------------------------------------- /include/mpark/patterns/detail/qualify_as.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_DETAIL_QUALIFY_AS_HPP 9 | #define MPARK_PATTERNS_DETAIL_QUALIFY_AS_HPP 10 | 11 | namespace mpark::patterns::detail { 12 | 13 | template 14 | struct qualify_as : lib::identity {}; 15 | 16 | template 17 | using qualify_as_t = typename qualify_as::type; 18 | 19 | template 20 | struct qualify_as : lib::identity &> {}; 21 | 22 | template 23 | struct qualify_as : lib::identity &&> {}; 24 | 25 | template 26 | struct qualify_as : lib::identity> {}; 27 | 28 | template 29 | struct qualify_as 30 | : lib::identity volatile> {}; 31 | 32 | template 33 | struct qualify_as 34 | : lib::identity volatile> {}; 35 | 36 | } // namespace mpark::patterns::detail 37 | 38 | #endif // MPARK_PATTERNS_DETAIL_QUALIFY_AS_HPP 39 | -------------------------------------------------------------------------------- /include/mpark/patterns/let.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_LET_HPP 9 | #define MPARK_PATTERNS_LET_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace mpark::patterns { 16 | 17 | template 18 | struct IfLet { 19 | template 20 | void operator=(F &&f) && noexcept { 21 | match(std::move(case_).rhs())( 22 | std::move(case_).pattern = std::forward(f), 23 | pattern(_) = [] {}); 24 | } 25 | 26 | detail::Case &&case_; 27 | }; 28 | 29 | template 30 | auto if_let(detail::Case &&case_) noexcept { 31 | static_assert(Pattern::size <= 1, 32 | "The `if_let` statement cannot have more than 1 pattern " 33 | "since it only matches a single value. If you're trying to " 34 | "match a destructurable type, use the `ds` pattern!"); 35 | return IfLet{std::move(case_)}; 36 | } 37 | 38 | enum ControlFlow { Break, Continue }; 39 | 40 | template 41 | struct ForLet { 42 | template 43 | void operator=(F &&f) && noexcept { 44 | for (auto&& elem : std::move(case_).rhs()) { 45 | ControlFlow control_flow = match(std::forward(elem))( 46 | std::move(case_).pattern = [&](auto &&... args) { 47 | using R = lib::invoke_result_t; 48 | if constexpr (std::is_same_v) { 49 | return std::invoke(std::forward(f), 50 | std::forward(args)...); 51 | } else { 52 | std::invoke(std::forward(f), 53 | std::forward(args)...); 54 | return Continue; 55 | } 56 | }, 57 | pattern(_) = [] { return Continue; }); 58 | if (control_flow == Break) { 59 | break; 60 | } else if (control_flow == Continue) { 61 | continue; 62 | } 63 | } 64 | } 65 | 66 | detail::Case &&case_; 67 | }; 68 | 69 | template 70 | auto for_let(detail::Case &&case_) noexcept { 71 | static_assert(Pattern::size <= 1, 72 | "The `for_let` statement cannot have more than 1 pattern " 73 | "since it only matches a single value. If you're trying to " 74 | "match a destructurable type, use the `ds` pattern!"); 75 | return ForLet{std::move(case_)}; 76 | } 77 | 78 | } // namespace mpark::patterns 79 | 80 | #endif // MPARK_PATTERNS_LET_HPP 81 | -------------------------------------------------------------------------------- /include/mpark/patterns/lib.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_LIB_HPP 9 | #define MPARK_PATTERNS_LIB_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace mpark::patterns::lib { 17 | 18 | template 19 | struct identity { using type = T; }; 20 | 21 | inline namespace cpp17 { 22 | 23 | namespace detail { 24 | 25 | template 26 | struct invoke_result {}; 27 | 28 | template 29 | struct invoke_result(), 30 | std::declval()...))), 31 | F, 32 | Args...> 33 | : identity(), std::declval()...))> {}; 35 | 36 | } // namespace detail 37 | 38 | template 39 | using invoke_result = detail::invoke_result; 40 | 41 | template 42 | using invoke_result_t = typename invoke_result::type; 43 | 44 | namespace detail { 45 | 46 | template 47 | struct is_invocable : std::false_type {}; 48 | 49 | template 50 | struct is_invocable>, 51 | F, 52 | Args...> : std::true_type {}; 53 | 54 | template 55 | struct is_invocable_r : std::false_type {}; 56 | 57 | template 58 | struct is_invocable_r>, 59 | R, 60 | F, 61 | Args...> 62 | : std::is_convertible, R> {}; 63 | 64 | } // namespace detail 65 | 66 | template 67 | using is_invocable = detail::is_invocable; 68 | 69 | template 70 | inline constexpr bool is_invocable_v = is_invocable::value; 71 | 72 | template 73 | using is_invocable_r = detail::is_invocable_r; 74 | 75 | template 76 | inline constexpr bool is_invocable_r_v = is_invocable_r::value; 77 | 78 | } // namespace cpp17 79 | 80 | template 81 | decltype(auto) apply(F &&f, Tuple &&tuple, std::index_sequence) { 82 | return std::invoke(std::forward(f), 83 | std::get(std::forward(tuple))...); 84 | } 85 | 86 | // `string_view::find` is not `constexpr` on GCC 7. 87 | inline constexpr std::size_t find(std::string_view this_, 88 | char ch, 89 | std::size_t begin = 0) { 90 | for (std::size_t i = begin; i < this_.size(); ++i) { 91 | if (this_[i] == ch) { 92 | return i; 93 | } 94 | } 95 | return std::string_view::npos; 96 | } 97 | 98 | // `string_view::find_first_not_of` is not `constexpr` on GCC 7. 99 | inline constexpr std::size_t find_first_not_of(std::string_view this_, 100 | std::string_view sv) { 101 | for (std::size_t i = 0; i < this_.size(); ++i) { 102 | std::size_t index = find(sv, this_[i]); 103 | if (index == std::string_view::npos) { 104 | return i; 105 | } 106 | } 107 | return std::string_view::npos; 108 | } 109 | 110 | template 111 | inline constexpr bool false_v = false; 112 | 113 | template 114 | inline constexpr bool is_rref_v = std::is_rvalue_reference_v; 115 | 116 | template 117 | struct list {}; 118 | 119 | template 120 | struct indexed_type : identity {}; 121 | 122 | template 123 | using size_constant = std::integral_constant; 124 | 125 | template 126 | struct priority : priority {}; 127 | 128 | template <> 129 | struct priority<4> {}; 130 | 131 | } // namespace mpark::patterns::lib 132 | 133 | #endif // MPARK_PATTERNS_LIB_HPP 134 | -------------------------------------------------------------------------------- /include/mpark/patterns/match.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_MATCH_HPP 9 | #define MPARK_PATTERNS_MATCH_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "config.hpp" 22 | #include "detail/as_tuple.hpp" 23 | #include "detail/forwarder.hpp" 24 | #include "lib.hpp" 25 | 26 | namespace mpark::patterns { 27 | 28 | // The type of exception thrown when none of the patterns match. 29 | class match_error : public std::exception { 30 | public: 31 | virtual const char *what() const noexcept { return "match_error"; } 32 | }; 33 | 34 | // Used to indicate a match failure in `try_match` functions. 35 | inline constexpr struct no_match_t {} no_match{}; 36 | 37 | // The return type of `try_match` functions. 38 | 39 | template 40 | struct match_result : std::optional> { 41 | using type = T; 42 | 43 | using super = std::optional>; 44 | using super::super; 45 | 46 | match_result(no_match_t) noexcept {} 47 | match_result(std::nullopt_t) = delete; 48 | 49 | decltype(auto) get() && { 50 | return (*static_cast(*this)).forward(); 51 | } 52 | }; 53 | 54 | template 55 | inline constexpr bool is_match_result_v = false; 56 | 57 | template 58 | inline constexpr bool is_match_result_v> = true; 59 | 60 | // `std::invoke`-like utility for `try_match` functions. 61 | template 62 | auto match_invoke(F &&f, Args &&... args) { 63 | static_assert(lib::is_invocable_v, 64 | "The given handler `F` is not invocable with `Args...`. " 65 | "Inspect the error messages below to determine what " 66 | "`F` and `Args...` are."); 67 | using R = lib::invoke_result_t; 68 | if constexpr (std::is_void_v) { 69 | std::invoke(std::forward(f), std::forward(args)...); 70 | return match_result(detail::void_{}); 71 | } else if constexpr (is_match_result_v) { 72 | return std::invoke(std::forward(f), std::forward(args)...); 73 | } else { 74 | return match_result( 75 | std::invoke(std::forward(f), std::forward(args)...)); 76 | } 77 | } 78 | 79 | // `std::apply`-like utility for `try_match` functions. 80 | 81 | template 82 | auto match_apply(F &&f, Args &&args, std::index_sequence) { 83 | return match_invoke(std::forward(f), 84 | std::get(std::forward(args))...); 85 | } 86 | 87 | template 88 | auto match_apply(F &&f, Args &&args) { 89 | return match_apply( 90 | std::forward(f), 91 | std::forward(args), 92 | std::make_index_sequence>>{}); 93 | } 94 | 95 | inline constexpr std::size_t npos = static_cast(-1); 96 | 97 | // Expression Pattern 98 | 99 | template 100 | auto try_match(const ExprPattern &expr_pattern, Value &&value, F &&f) { 101 | return expr_pattern == std::forward(value) 102 | ? match_invoke(std::forward(f)) 103 | : no_match; 104 | } 105 | 106 | namespace detail { 107 | 108 | template 109 | struct LazyExpr; 110 | 111 | template 112 | inline constexpr bool is_lazy_expr_v = false; 113 | 114 | template 115 | inline constexpr bool is_lazy_expr_v> = true; 116 | 117 | } // namespace detail 118 | 119 | template 120 | struct Identifier; 121 | 122 | template 123 | inline constexpr bool is_identifier_v = false; 124 | 125 | template 126 | inline constexpr bool is_identifier_v> = true; 127 | 128 | // Special indices. 129 | inline constexpr std::size_t wildcard_index = npos; 130 | inline constexpr std::size_t arg_index = npos / 2; 131 | 132 | namespace detail { 133 | 134 | template 135 | struct indexed_forwarder : forwarder { 136 | static constexpr std::size_t index = I; 137 | 138 | using super = forwarder; 139 | using super::super; 140 | }; 141 | 142 | template 143 | inline constexpr bool is_indexed_forwarder_v = false; 144 | 145 | template 146 | inline constexpr bool is_indexed_forwarder_v> = true; 147 | 148 | template 149 | struct set; 150 | 151 | template 152 | struct set, Ts...> 153 | : lib::indexed_type... {}; 154 | 155 | template 156 | struct find_indexed_forwarder; 157 | 158 | template 159 | struct find_indexed_forwarder> { 160 | template 161 | static constexpr std::size_t impl( 162 | lib::indexed_type &&>) { 163 | return Idx; 164 | } 165 | 166 | static constexpr void impl(...) { 167 | static_assert(I == wildcard_index || I == arg_index); 168 | 169 | static_assert(I != wildcard_index, 170 | "Reference to the wildcard pattern (`_`) in the `when` " 171 | "clause is ambiguous. There are multiple instances of " 172 | "them in the source pattern."); 173 | static_assert(I != arg_index, 174 | "Reference to the arg pattern (`arg`) in the `when` " 175 | "clause is ambiguous. There are multiple instances of " 176 | "them in the source pattern."); 177 | } 178 | 179 | static constexpr std::size_t value = 180 | impl(set, Ts...>{}); 181 | }; 182 | 183 | template 184 | inline constexpr std::size_t find_indexed_forwarder_v = 185 | find_indexed_forwarder::value; 186 | 187 | template 188 | decltype(auto) eval(Arg &&arg, 189 | std::tuple &&...> &&ifs) { 190 | using Decayed = std::decay_t; 191 | if constexpr (is_identifier_v) { 192 | if constexpr (Decayed::has_pattern) { 193 | return std::forward(arg).as_lazy_expr().lambda(std::move(ifs)); 194 | } else { 195 | constexpr std::size_t i = find_indexed_forwarder_v< 196 | Decayed::index, 197 | std::tuple &&...>>; 198 | return std::get(std::move(ifs)).forward(); 199 | } 200 | } else if constexpr (is_lazy_expr_v) { 201 | return std::forward(arg).lambda(std::move(ifs)); 202 | } else { 203 | return std::forward(arg); 204 | } 205 | } 206 | 207 | } // namespace detail 208 | 209 | namespace detail { 210 | 211 | template 212 | auto make_lambda(F f, Args &&... args) noexcept { 213 | return [&, f = std::move(f)](auto &&ifs) -> decltype(auto) { 214 | static_assert(lib::is_rref_v); 215 | return f(eval(std::forward(args), std::move(ifs))...); 216 | }; 217 | } 218 | 219 | template 220 | struct LazyExpr { 221 | #define MPARK_PATTERNS_MEMBER_OPERATORS(type) \ 222 | template \ 223 | auto operator=(Arg &&arg) const noexcept { \ 224 | auto lambda = make_lambda( \ 225 | [](auto &&this_, auto &&arg_) -> decltype(auto) { \ 226 | using This_ = decltype(this_); \ 227 | using Arg_ = decltype(arg_); \ 228 | return std::forward(this_) = std::forward(arg_); \ 229 | }, \ 230 | static_cast(*this), \ 231 | std::forward(arg)); \ 232 | return LazyExpr{std::move(lambda)}; \ 233 | } \ 234 | \ 235 | template \ 236 | auto operator()(Args &&... args) const noexcept { \ 237 | auto lambda = make_lambda( \ 238 | [](auto &&this_, auto &&... args_) -> decltype(auto) { \ 239 | using This_ = decltype(this_); \ 240 | return std::forward(this_)( \ 241 | std::forward(args_)...); \ 242 | }, \ 243 | static_cast(*this), \ 244 | std::forward(args)...); \ 245 | return LazyExpr{std::move(lambda)}; \ 246 | } \ 247 | \ 248 | template \ 249 | auto operator[](Arg &&arg) const noexcept { \ 250 | auto lambda = make_lambda( \ 251 | [](auto &&this_, auto &&arg_) -> decltype(auto) { \ 252 | using This_ = decltype(this_); \ 253 | using Arg_ = decltype(arg_); \ 254 | if constexpr (std::is_array_v>) { \ 255 | /* For arrays, we handle the forwarding explicitly because */ \ 256 | /* `std::forward(t)[I]` always yields an lvalue-ref on GCC. */ \ 257 | if constexpr (lib::is_rref_v) { \ 258 | return std::move(this_[std::forward(arg_)]); \ 259 | } else { \ 260 | return this_[std::forward(arg_)]; \ 261 | } \ 262 | } else { \ 263 | return std::forward(this_)[std::forward(arg_)]; \ 264 | } \ 265 | }, \ 266 | static_cast(*this), \ 267 | std::forward(arg)); \ 268 | return LazyExpr{std::move(lambda)}; \ 269 | } 270 | 271 | MPARK_PATTERNS_MEMBER_OPERATORS(LazyExpr) 272 | 273 | Lambda lambda; 274 | }; 275 | 276 | template 277 | auto make_lazy_expr(F f, Args &&... args) noexcept { 278 | auto lambda = make_lambda(std::move(f), std::forward(args)...); 279 | return LazyExpr{std::move(lambda)}; 280 | } 281 | 282 | template 283 | struct IdentifierBase { 284 | using type = Identifier; 285 | 286 | MPARK_PATTERNS_MEMBER_OPERATORS(type) 287 | 288 | static constexpr std::size_t index = I; 289 | static constexpr bool has_pattern = !std::is_void_v; 290 | }; 291 | 292 | } // namespace detail 293 | 294 | #define MPARK_PATTERNS_UNARY_PREFIX_OPERATOR(op) \ 295 | template > || \ 297 | detail::is_lazy_expr_v>), \ 298 | int> = 0> \ 299 | auto operator op(Arg &&arg) noexcept { \ 300 | return detail::make_lazy_expr( \ 301 | [](auto &&arg_) -> decltype(auto) { \ 302 | return op std::forward(arg_); \ 303 | }, \ 304 | std::forward(arg)); \ 305 | } 306 | 307 | #define MPARK_PATTERNS_UNARY_POSTFIX_OPERATOR(op) \ 308 | template > || \ 310 | detail::is_lazy_expr_v>), \ 311 | int> = 0> \ 312 | auto operator op(Arg &&arg, int) noexcept { \ 313 | return detail::make_lazy_expr( \ 314 | [](auto &&arg_) -> decltype(auto) { \ 315 | return std::forward(arg_) op; \ 316 | }, \ 317 | std::forward(arg)); \ 318 | } 319 | 320 | #define MPARK_PATTERNS_BINARY_OPERATOR(op) \ 321 | template > || \ 324 | is_identifier_v> || \ 325 | detail::is_lazy_expr_v> || \ 326 | detail::is_lazy_expr_v>), \ 327 | int> = 0> \ 328 | auto operator op(Lhs &&lhs, Rhs &&rhs) noexcept { \ 329 | return detail::make_lazy_expr( \ 330 | [](auto &&lhs_, auto &&rhs_) -> decltype(auto) { \ 331 | return std::forward(lhs_) \ 332 | op std::forward(rhs_); \ 333 | }, \ 334 | std::forward(lhs), \ 335 | std::forward(rhs)); \ 336 | } 337 | 338 | MPARK_PATTERNS_UNARY_PREFIX_OPERATOR(+) 339 | MPARK_PATTERNS_UNARY_PREFIX_OPERATOR(-) 340 | MPARK_PATTERNS_UNARY_PREFIX_OPERATOR(*) 341 | MPARK_PATTERNS_UNARY_PREFIX_OPERATOR(~) 342 | MPARK_PATTERNS_UNARY_PREFIX_OPERATOR(&) 343 | MPARK_PATTERNS_UNARY_PREFIX_OPERATOR(!) 344 | MPARK_PATTERNS_UNARY_PREFIX_OPERATOR(++) 345 | MPARK_PATTERNS_UNARY_PREFIX_OPERATOR(--) 346 | 347 | MPARK_PATTERNS_UNARY_POSTFIX_OPERATOR(++) 348 | MPARK_PATTERNS_UNARY_POSTFIX_OPERATOR(--) 349 | 350 | MPARK_PATTERNS_BINARY_OPERATOR(<<) 351 | MPARK_PATTERNS_BINARY_OPERATOR(>>) 352 | MPARK_PATTERNS_BINARY_OPERATOR(*) 353 | MPARK_PATTERNS_BINARY_OPERATOR(/) 354 | MPARK_PATTERNS_BINARY_OPERATOR(%) 355 | MPARK_PATTERNS_BINARY_OPERATOR(+) 356 | MPARK_PATTERNS_BINARY_OPERATOR(-) 357 | MPARK_PATTERNS_BINARY_OPERATOR(<) 358 | MPARK_PATTERNS_BINARY_OPERATOR(>) 359 | MPARK_PATTERNS_BINARY_OPERATOR(<=) 360 | MPARK_PATTERNS_BINARY_OPERATOR(>=) 361 | MPARK_PATTERNS_BINARY_OPERATOR(==) 362 | MPARK_PATTERNS_BINARY_OPERATOR(!=) 363 | MPARK_PATTERNS_BINARY_OPERATOR(||) 364 | MPARK_PATTERNS_BINARY_OPERATOR(&&) 365 | MPARK_PATTERNS_BINARY_OPERATOR(&) 366 | MPARK_PATTERNS_BINARY_OPERATOR(|) 367 | MPARK_PATTERNS_BINARY_OPERATOR(^) 368 | MPARK_PATTERNS_BINARY_OPERATOR(->*) 369 | MPARK_PATTERNS_BINARY_OPERATOR(<<=) 370 | MPARK_PATTERNS_BINARY_OPERATOR(>>=) 371 | MPARK_PATTERNS_BINARY_OPERATOR(*=) 372 | MPARK_PATTERNS_BINARY_OPERATOR(/=) 373 | MPARK_PATTERNS_BINARY_OPERATOR(%=) 374 | MPARK_PATTERNS_BINARY_OPERATOR(+=) 375 | MPARK_PATTERNS_BINARY_OPERATOR(-=) 376 | MPARK_PATTERNS_BINARY_OPERATOR(&=) 377 | MPARK_PATTERNS_BINARY_OPERATOR(|=) 378 | MPARK_PATTERNS_BINARY_OPERATOR(^=) 379 | 380 | #define MPARK_PATTERNS_COMMA , 381 | MPARK_PATTERNS_BINARY_OPERATOR(MPARK_PATTERNS_COMMA) 382 | #undef MPARK_PATTERNS_COMMA 383 | 384 | // Identifier Pattern 385 | 386 | template 387 | struct Identifier : detail::IdentifierBase { 388 | using super = detail::IdentifierBase; 389 | 390 | Identifier(const Identifier &) = delete; 391 | Identifier &operator=(const Identifier &) = delete; 392 | 393 | using super::operator=; 394 | using super::operator(); 395 | using super::operator[]; 396 | 397 | // When this type of identifier is found within a `when` clause, 398 | // we convert it to a lazy-expr since it can't mean anything else. 399 | auto as_lazy_expr() const noexcept { 400 | return Identifier{0}.call(std::forward(arg)); 401 | } 402 | 403 | Arg &&arg; 404 | }; 405 | 406 | template 407 | struct Identifier : detail::IdentifierBase { 408 | using super = detail::IdentifierBase; 409 | 410 | constexpr Identifier(int) noexcept {} 411 | 412 | Identifier(const Identifier &) = delete; 413 | Identifier &operator=(const Identifier &) = delete; 414 | 415 | using super::operator=; 416 | using super::operator(); 417 | using super::operator[]; 418 | 419 | template 420 | auto operator()(Pattern &&pattern) const noexcept { 421 | return Identifier{{}, std::forward(pattern)}; 422 | } 423 | 424 | template 425 | auto call(Arg &&arg) const noexcept { 426 | return super::operator()(std::forward(arg)); 427 | } 428 | }; 429 | 430 | // Wildcard Pattern 431 | inline constexpr Identifier _{0}; 432 | 433 | // Arg Pattern 434 | inline constexpr Identifier arg{0}; 435 | 436 | namespace detail { 437 | 438 | template 439 | constexpr auto prepend(std::index_sequence) { 440 | return std::index_sequence{}; 441 | } 442 | 443 | template 444 | constexpr std::tuple...> make_identifiers( 445 | std::index_sequence) { 446 | return {Is...}; 447 | } 448 | 449 | template 451 | constexpr auto parse(StrView str_view) { 452 | constexpr std::string_view sv = str_view(); 453 | if constexpr (I == std::string_view::npos) { 454 | return std::index_sequence<>{}; 455 | } else { 456 | using namespace std::literals; 457 | constexpr std::size_t comma = lib::find(sv, ',', I); 458 | constexpr std::string_view token = sv.substr(I, comma - I); 459 | static_assert(token.size() != 0, "expected identifier"); 460 | constexpr std::size_t first = 461 | lib::find_first_not_of(token, " \f\n\r\t\v"sv); 462 | static_assert(first != std::string_view::npos, "expected identifier"); 463 | constexpr std::size_t next = comma == std::string_view::npos 464 | ? std::string_view::npos 465 | : comma + 1; 466 | if constexpr (token[first] == '_') { 467 | return prepend(parse(str_view)); 468 | } else { 469 | return prepend(parse(str_view)); 470 | } 471 | } 472 | } 473 | 474 | } // namespace detail 475 | 476 | #define IDENTIFIERS(...) \ 477 | auto [__VA_ARGS__] = mpark::patterns::detail::make_identifiers( \ 478 | mpark::patterns::detail::parse<0, arg_index + 1, 0>([] { \ 479 | using namespace std::literals; \ 480 | return #__VA_ARGS__##sv; \ 481 | })) 482 | 483 | template 484 | auto try_match(const Identifier &identifier, 485 | Value &&value, 486 | F &&f) { 487 | auto f_ = [&](auto &&... ifs) { 488 | static_assert((... && lib::is_rref_v)); 489 | return match_invoke( 490 | std::forward(f), 491 | detail::indexed_forwarder{std::forward(value)}, 492 | std::move(ifs)...); 493 | }; 494 | if constexpr (Identifier::has_pattern) { 495 | return try_match( 496 | identifier.arg, std::forward(value), std::move(f_)); 497 | } else { 498 | return f_(); 499 | } 500 | } 501 | 502 | // Variadic Pattern 503 | 504 | template 505 | struct Variadic { const Pattern &pattern; }; 506 | 507 | template 508 | auto variadic(const Pattern &pattern) noexcept { 509 | return Variadic{pattern}; 510 | } 511 | 512 | template 513 | inline constexpr bool is_variadic_v = false; 514 | 515 | template 516 | inline constexpr bool is_variadic_v> = true; 517 | 518 | // Destructure Pattern 519 | 520 | template 521 | struct Ds { std::tuple patterns; }; 522 | 523 | template 524 | auto ds(const Patterns &... patterns) noexcept { 525 | return Ds{std::tie(patterns...)}; 526 | } 527 | 528 | template , int> = 0, 530 | std::size_t = sizeof(std::tuple_size)> 531 | constexpr bool is_tuple_like(lib::priority<0>) noexcept { return true; } 532 | 533 | template 534 | constexpr bool is_tuple_like(lib::priority<1>) noexcept { return false; } 535 | 536 | template 537 | inline constexpr bool is_tuple_like_v = is_tuple_like(lib::priority<>{}); 538 | 539 | namespace detail { 540 | 541 | enum class DetectResult { Member, NonMember, None }; 542 | 543 | using std::get; 544 | 545 | template ().template get())> 548 | constexpr DetectResult detect_get(lib::priority<0>) noexcept { 549 | return DetectResult::Member; 550 | } 551 | 552 | template (std::declval()))> 555 | constexpr DetectResult detect_get(lib::priority<1>) noexcept { 556 | return DetectResult::NonMember; 557 | } 558 | 559 | template 560 | constexpr DetectResult detect_get(lib::priority<2>) noexcept { 561 | return DetectResult::None; 562 | } 563 | 564 | template 565 | inline constexpr DetectResult detect_get_v = 566 | detect_get(lib::priority<>{}); 567 | 568 | template 569 | auto try_match_impl(const Ds &, 570 | Values &&, 571 | F &&f, 572 | std::index_sequence<>) { 573 | return match_invoke(std::forward(f)); 574 | } 575 | 576 | template 581 | auto try_match_impl(const Ds &ds, 582 | Values &&values, 583 | F &&f, 584 | std::index_sequence) { 585 | return try_match( 586 | std::get(ds.patterns), 587 | [&]() -> decltype(auto) { 588 | if constexpr (std::is_array_v>) { 589 | // We handle the forwarding explicitly because 590 | // `std::forward(t)[I]` always yields an lvalue-ref on GCC. 591 | if constexpr (lib::is_rref_v) { 592 | return std::move(values[I]); 593 | } else { 594 | return values[I]; 595 | } 596 | } else if constexpr (is_tuple_like_v>) { 597 | constexpr auto result = detail::detect_get_v; 598 | if constexpr (result == DetectResult::Member) { 599 | return std::forward(values).template get(); 600 | } else if constexpr (result == DetectResult::NonMember) { 601 | using std::get; 602 | return get(std::forward(values)); 603 | } else { 604 | static_assert(lib::false_v, 605 | "The value attempting to be matched against a " 606 | "`ds` pattern has a specialization for " 607 | "`std::tuple_size`, but does not have a member " 608 | "nor non-member `get` function available."); 609 | } 610 | } 611 | }(), 612 | [&](auto &&... head_ifs) { 613 | static_assert((... && lib::is_rref_v)); 614 | return try_match_impl( 615 | ds, 616 | std::forward(values), 617 | [&](auto &&... tail_ifs) { 618 | static_assert((... && lib::is_rref_v)); 619 | return match_invoke(std::forward(f), 620 | std::move(head_ifs)..., 621 | std::move(tail_ifs)...); 622 | }, 623 | std::index_sequence{}); 624 | }); 625 | } 626 | 627 | enum class DsPatternCheckResult { 628 | Success, 629 | TooManyVariadics, 630 | NotEnoughPatterns, 631 | TooManyPatterns 632 | }; 633 | 634 | template 635 | constexpr std::size_t find_variadic() { 636 | constexpr std::size_t size = sizeof...(Patterns); 637 | constexpr std::array bs = {{is_variadic_v...}}; 638 | for (std::size_t i = 0; i < size; ++i) { 639 | if (bs[i]) { 640 | return i; 641 | } 642 | } 643 | return npos; 644 | } 645 | 646 | template 647 | constexpr DsPatternCheckResult ds_pattern_check() noexcept { 648 | constexpr std::size_t size = sizeof...(Patterns); 649 | if constexpr (size == 0) { 650 | return N == 0 ? DsPatternCheckResult::Success 651 | : DsPatternCheckResult::NotEnoughPatterns; 652 | } else { 653 | constexpr std::array bs = {{is_variadic_v...}}; 654 | std::size_t index = npos; 655 | for (std::size_t i = 0; i < size; ++i) { 656 | if (bs[i]) { 657 | if (index == npos) { 658 | index = i; 659 | } else { 660 | return DsPatternCheckResult::TooManyVariadics; 661 | } 662 | } 663 | } 664 | if (index == npos) { // non-variadic 665 | if constexpr (N > size) { 666 | return DsPatternCheckResult::NotEnoughPatterns; 667 | } else if constexpr (N < size) { 668 | return DsPatternCheckResult::TooManyPatterns; 669 | } else { 670 | return DsPatternCheckResult::Success; 671 | } 672 | } else { // variadic 673 | if constexpr (N + 1 < size) { 674 | return DsPatternCheckResult::TooManyPatterns; 675 | } else { 676 | return DsPatternCheckResult::Success; 677 | } 678 | } 679 | } 680 | } 681 | 682 | template 683 | auto expand_variadics(const Ds &p, 684 | std::index_sequence) noexcept { 685 | constexpr std::size_t variadic_index = find_variadic(); 686 | constexpr std::size_t num_values = sizeof...(Is); 687 | constexpr std::size_t num_patterns = sizeof...(Patterns); 688 | if constexpr (variadic_index == npos) { 689 | static_assert(num_values == num_patterns); 690 | return p; 691 | } else { 692 | if constexpr (num_values < num_patterns) { 693 | static_assert(num_values == num_patterns - 1); 694 | return ds( 695 | std::get<(variadic_index <= Is ? Is + 1 : Is)>(p.patterns)...); 696 | } else { 697 | static_assert(num_values >= num_patterns); 698 | constexpr std::size_t diff = num_values - num_patterns; 699 | auto index = [](std::size_t i) constexpr { 700 | if (i < variadic_index) { 701 | return i; 702 | } else if (variadic_index <= i && i <= variadic_index + diff) { 703 | return variadic_index; 704 | } else { 705 | return i - diff; 706 | } 707 | }; 708 | return ds([](auto &&pattern) -> auto && { 709 | if constexpr (is_variadic_v>) { 710 | return pattern.pattern; 711 | } else { 712 | return pattern; 713 | } 714 | }(std::get(p.patterns))...); 715 | } 716 | } 717 | } 718 | 719 | } // namespace detail 720 | 721 | template 722 | auto try_match(const Ds &ds, Values &&values, F &&f) { 723 | constexpr bool is_array = std::is_array_v>; 724 | constexpr bool is_tuple_like = is_tuple_like_v>; 725 | if constexpr (!is_array && !is_tuple_like) { 726 | using Aggregate = std::decay_t; 727 | static_assert(std::is_aggregate_v); 728 | static_assert(std::is_copy_constructible_v); 729 | return try_match(ds, 730 | detail::as_tuple(std::forward(values)), 731 | std::forward(f)); 732 | } else { 733 | constexpr auto size = [] { 734 | if constexpr (is_array) { 735 | return std::extent>{}; 736 | } else if constexpr (is_tuple_like) { 737 | return std::tuple_size>{}; 738 | } else { 739 | static_assert( 740 | lib::false_v, 741 | "The value attempting to be matched against a `ds` " 742 | "pattern is not an array, tuple-like, nor an aggregate."); 743 | } 744 | }(); 745 | constexpr auto result = detail::ds_pattern_check(); 746 | static_assert( 747 | result != detail::DsPatternCheckResult::TooManyVariadics, 748 | "The variadic pattern can only appear once in a `ds` pattern."); 749 | static_assert(result != detail::DsPatternCheckResult::NotEnoughPatterns, 750 | "Not enough patterns are provided to match the values."); 751 | static_assert(result != detail::DsPatternCheckResult::TooManyPatterns, 752 | "More patterns are provided than values. Are you trying to " 753 | "match a destructurable type without a `ds` pattern?"); 754 | using Is = std::make_index_sequence; 755 | return detail::try_match_impl(detail::expand_variadics(ds, Is{}), 756 | std::forward(values), 757 | std::forward(f), 758 | Is{}); 759 | } 760 | } 761 | 762 | // `match` DSL. 763 | 764 | namespace detail { 765 | 766 | struct Deduce; 767 | 768 | // Returns `true` iif the elements at indices `Js...` in the given 769 | // tuple-like argument all compare equal to the element at index `J`. 770 | template 772 | bool equals(std::tuple &&...> &&ifs, 773 | std::index_sequence) { 774 | return (... && (std::get(std::move(ifs)).forward() == 775 | std::get(std::move(ifs)).forward())); 776 | } 777 | 778 | // Returns `true` iif the elements that belong to each group compare 779 | // equal amongst themselves. 780 | template 781 | bool equals(std::tuple &&...> &&ifs, 782 | lib::list) { 783 | return (... && equals(std::move(ifs), GroupedIndices{})); 784 | } 785 | 786 | template 787 | auto prepend(Head, lib::list) { 788 | return lib::list{}; 789 | } 790 | 791 | template 792 | lib::list>> insert(lib::list<>) { 793 | return {}; 794 | } 795 | 796 | template 798 | auto insert( 799 | lib::list>, Tail...>) { 800 | using Head = lib::indexed_type>; 801 | if constexpr (P == wildcard_index || P == arg_index) { 802 | return lib:: 803 | list>>{}; 804 | } else if constexpr (P == Q) { 805 | return lib::list>, 806 | Tail...>{}; 807 | } else { 808 | return prepend(Head{}, insert(lib::list{})); 809 | } 810 | } 811 | 812 | using Pred = bool (*)(std::size_t); 813 | 814 | template 815 | auto grouped_indices(lib::list, 816 | std::index_sequence<>, 817 | std::index_sequence<>) { 818 | return lib::list{}; 819 | } 820 | 821 | template 825 | auto grouped_indices(lib::list result, 826 | std::index_sequence, 827 | std::index_sequence) { 828 | return grouped_indices( 829 | [&] { 830 | if constexpr (pred(P)) { 831 | return insert(result); 832 | } else { 833 | return result; 834 | } 835 | }(), 836 | std::index_sequence{}, 837 | std::index_sequence{}); 838 | } 839 | 840 | // Group the indices of the same placeholders within the arguments. 841 | // 842 | // Example: 843 | // Given placeholders `x`, `y` and 844 | // `match(1, 2, 1, 4)(pattern(x, arg, x, y) = f)`, 845 | // 846 | // The type of arguments passed to the intermediate lambda are 847 | // ``` 848 | // indexed_forwarder<0, int&&> 849 | // indexed_forwarder 850 | // indexed_forwarder<0, int&&> 851 | // indexed_forwarder<1, int&&> 852 | // ``` 853 | // 854 | // We want to take this sequence of types and return `[[0, 2], [1], [3]]`. 855 | // These are the groups of elements (by their indices) that need to 856 | // compare equal in order for the pattern to match. Specifically, 857 | // the values that the `x` placeholder binds to, at index 0 and 2, 858 | // would need to compare equal in order for this pattern to match. 859 | template 860 | using grouped_indices_t = 861 | decltype(grouped_indices(lib::list<>{}, 862 | std::index_sequence{}, 863 | std::index_sequence_for{})); 864 | 865 | template 866 | inline constexpr std::size_t front_v = npos; 867 | 868 | template 869 | inline constexpr std::size_t front_v> = I; 870 | 871 | template 872 | std::index_sequence...> fronts( 873 | lib::list); 874 | 875 | inline constexpr Pred lazy_expr_fn = [](std::size_t) { return true; }; 876 | 877 | template 878 | using lazy_expr_indices_t = 879 | decltype(fronts(grouped_indices_t{})); 880 | 881 | // Get the indices of the arguments to be passed to the final lambda. 882 | // 883 | // Example: 884 | // Given placeholders `x`, `y` and 885 | // `match(1, 2, 1, 4)(pattern(x, arg, x, y) = f)`, 886 | // `grouped_indices_t` returns `[[0, 2], [1], [3]]` (see above). 887 | // 888 | // Given this, the indices of the arguments to be passed to the final 889 | // lambda, are the first element of each of the lists. In this case, 890 | // We want `[0, 1, 3]`, so that we don't pass the value matched by `x` 891 | // (i.e., `1`) multiple times. 892 | inline constexpr Pred args_fn = [](std::size_t i) { 893 | return i <= arg_index; 894 | }; 895 | 896 | template 897 | using args_indices_t = 898 | decltype(fronts(grouped_indices_t{})); 899 | 900 | inline constexpr Pred equals_fn = [](std::size_t i) { 901 | return i != wildcard_index && i != arg_index; 902 | }; 903 | 904 | template 905 | using equals_indices_t = grouped_indices_t; 906 | 907 | template 908 | struct Case { 909 | auto &&rhs() && noexcept { return std::forward(rhs_); } 910 | 911 | Pattern pattern; 912 | Rhs rhs_; 913 | }; 914 | 915 | template 916 | struct Pattern; 917 | 918 | template 919 | struct PatternBase { 920 | using type = Pattern; 921 | 922 | static constexpr bool guarded = Guarded; 923 | 924 | template 925 | auto operator=(Rhs &&rhs) && noexcept { 926 | if constexpr (is_identifier_v>) { 927 | auto lazy_expr = make_lazy_expr( 928 | [](auto &&arg_) -> decltype(auto) { 929 | return std::forward(arg_); 930 | }, 931 | std::forward(rhs)); 932 | return Case{static_cast(*this), 933 | std::move(lazy_expr)}; 934 | } else { 935 | return Case{static_cast(*this), 936 | std::forward(rhs)}; 937 | } 938 | } 939 | }; 940 | 941 | template 942 | struct Pattern 943 | : PatternBase { 944 | static constexpr std::size_t size = sizeof...(Patterns); 945 | 946 | using super = PatternBase; 947 | using super::operator=; 948 | 949 | auto &&guard() && noexcept { return std::forward(guard_); } 950 | 951 | Ds patterns; 952 | Guard guard_; 953 | }; 954 | 955 | template 956 | struct Pattern : PatternBase { 957 | static constexpr std::size_t size = sizeof...(Patterns); 958 | 959 | using super = PatternBase; 960 | using super::operator=; 961 | 962 | template 963 | auto when(Arg &&arg) && noexcept { 964 | if constexpr (is_identifier_v>) { 965 | auto lazy_expr = make_lazy_expr( 966 | [](auto &&arg_) { 967 | bool result = std::forward(arg_); 968 | return result; 969 | }, 970 | std::forward(arg)); 971 | return Pattern{ 972 | {}, std::move(this->patterns), std::move(lazy_expr)}; 973 | } else { 974 | return Pattern{ 975 | {}, std::move(this->patterns), std::forward(arg)}; 976 | } 977 | } 978 | 979 | Ds patterns; 980 | }; 981 | 982 | template 983 | struct Match { 984 | public: 985 | template 986 | decltype(auto) operator()(Case &&case_, 987 | Cases &&... cases) && { 988 | auto result = [&] { 989 | auto result = try_match( 990 | std::move(case_).pattern.patterns, 991 | std::move(values), 992 | [&](auto &&... ifs_) { 993 | // The intermediate function that performs the adjustments for 994 | // placeholder-related functionality and ultimately calls `f` 995 | // with the final arguments. 996 | static_assert((... && lib::is_rref_v)); 997 | auto ifs = std::forward_as_tuple(std::move(ifs_)...); 998 | 999 | bool guard = true; 1000 | if constexpr (decltype(std::move(case_).pattern)::guarded) { 1001 | guard = lib::apply( 1002 | [&](auto &&... ifs_) -> decltype(auto) { 1003 | // We already checked for rvalue-reference above. 1004 | return std::invoke( 1005 | std::move(case_).pattern.guard().lambda, 1006 | std::forward_as_tuple(std::move(ifs_)...)); 1007 | }, 1008 | std::move(ifs), 1009 | lazy_expr_indices_t...>{}); 1010 | } 1011 | 1012 | using EqualsIndices = 1013 | equals_indices_t...>; 1014 | return equals(std::move(ifs), EqualsIndices{}) && guard 1015 | ? lib::apply( 1016 | [&](auto &&... ifs_) -> decltype(auto) { 1017 | // We already checked for rvalue-reference above. 1018 | return match_invoke(std::move(case_).rhs(), 1019 | std::move(ifs_).forward()...); 1020 | }, 1021 | std::move(ifs), 1022 | args_indices_t...>{}) 1023 | : no_match; 1024 | }); 1025 | 1026 | using Result = decltype(result); 1027 | static_assert(is_match_result_v, 1028 | "The function `try_match` is required to return " 1029 | " a `mpark::patterns::match_result` type. " 1030 | "If you're using `std::invoke`, try using " 1031 | "`mpark::patterns::match_invoke` instead."); 1032 | 1033 | if constexpr (std::is_same_v) { 1034 | return result; 1035 | } else if constexpr (std::is_void_v) { 1036 | return match_result(std::move(result)); 1037 | } else if constexpr (std::is_convertible_v) { 1039 | return match_result( 1040 | [&result]() -> R { return std::move(result).get(); }()); 1041 | } 1042 | }(); 1043 | if (result) { 1044 | return std::move(result).get(); 1045 | } 1046 | if constexpr (sizeof...(Cases) == 0) { 1047 | #ifdef MPARK_PATTERNS_EXCEPTIONS 1048 | throw match_error{}; 1049 | #else 1050 | std::terminate(); 1051 | #endif 1052 | } else { 1053 | return std::move(*this)(std::forward(cases)...); 1054 | } 1055 | } 1056 | 1057 | std::tuple values; 1058 | }; 1059 | 1060 | } // namespace detail 1061 | 1062 | template 1063 | auto pattern(const Patterns &... patterns) noexcept { 1064 | return detail::Pattern{{}, ds(patterns...)}; 1065 | } 1066 | 1067 | template 1068 | auto match(Values &&... values) noexcept { 1069 | return detail::Match{ 1070 | std::forward_as_tuple(std::forward(values)...)}; 1071 | } 1072 | 1073 | } // namespace mpark::patterns 1074 | 1075 | #endif // MPARK_PATTERNS_MATCH_HPP 1076 | -------------------------------------------------------------------------------- /include/mpark/patterns/optional.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_OPTIONAL_HPP 9 | #define MPARK_PATTERNS_OPTIONAL_HPP 10 | 11 | #include 12 | 13 | namespace mpark::patterns { 14 | 15 | inline constexpr struct None {} none{}; 16 | 17 | template 18 | auto try_match(None, Value &&value, F &&f) { 19 | return value ? no_match : match_invoke(std::forward(f)); 20 | } 21 | 22 | template 23 | struct Some { const Pattern &pattern; }; 24 | 25 | template 26 | auto some(const Pattern &pattern) { return Some{pattern}; } 27 | 28 | template 29 | auto try_match(const Some &some, Value &&value, F &&f) { 30 | return value ? try_match(some.pattern, 31 | *std::forward(value), 32 | std::forward(f)) 33 | : no_match; 34 | } 35 | 36 | } // namespace mpark::patterns 37 | 38 | #endif // MPARK_PATTERNS_OPTIONAL_HPP 39 | -------------------------------------------------------------------------------- /include/mpark/patterns/regex.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_REGEX_HPP 9 | #define MPARK_PATTERNS_REGEX_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace mpark::patterns { 19 | 20 | struct RegexMatch { 21 | template 22 | bool operator()(Args &&... args) const { 23 | return std::regex_match(std::forward(args)...); 24 | } 25 | }; 26 | 27 | struct RegexSearch { 28 | template 29 | bool operator()(Args &&... args) const { 30 | return std::regex_search(std::forward(args)...); 31 | } 32 | }; 33 | 34 | template 35 | struct Regex { std::regex regex; }; 36 | 37 | struct Re { 38 | auto match(std::regex regex) const { 39 | return Regex{std::move(regex)}; 40 | } 41 | 42 | template 43 | auto match(std::string_view sv, Args &&... args) const { 44 | return match(std::regex(std::begin(sv), std::end(sv)), 45 | std::forward(args)...); 46 | } 47 | 48 | auto search(std::regex regex) const { 49 | return Regex{std::move(regex)}; 50 | } 51 | 52 | template 53 | auto search(std::string_view sv, Args &&... args) const { 54 | return search(std::regex(std::begin(sv), std::end(sv)), 55 | std::forward(args)...); 56 | } 57 | }; 58 | 59 | inline constexpr Re re{}; 60 | 61 | template 62 | auto try_match(const Regex &r, const char *s, F &&f) { 63 | return RegexF{}(s, r.regex) ? match_invoke(std::forward(f)) : no_match; 64 | } 65 | 66 | template , const char *>, 70 | int> = 0> 71 | auto try_match(const Regex &r, Value &&value, F &&f) { 72 | return RegexF{}(std::cbegin(value), std::cend(value), r.regex) 73 | ? match_invoke(std::forward(f)) 74 | : no_match; 75 | } 76 | 77 | } // namespace mpark::patterns 78 | 79 | #endif // MPARK_PATTERNS_REGEX_HPP 80 | -------------------------------------------------------------------------------- /include/mpark/patterns/vis.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_VIS_HPP 9 | #define MPARK_PATTERNS_VIS_HPP 10 | 11 | #include 12 | #include 13 | 14 | namespace mpark::patterns { 15 | 16 | template 17 | struct Vis { const Pattern &pattern; }; 18 | 19 | template 20 | auto vis(const Pattern &pattern) noexcept { return Vis{pattern}; } 21 | 22 | template 23 | auto try_match(const Vis &vis, Value &&value, F &&f) { 24 | using std::visit; 25 | return visit( 26 | [&](auto &&v) { 27 | return try_match( 28 | vis.pattern, std::forward(v), std::forward(f)); 29 | }, 30 | std::forward(value)); 31 | } 32 | 33 | } // namespace mpark::patterns 34 | 35 | #endif // MPARK_PATTERNS_VIS_HPP 36 | -------------------------------------------------------------------------------- /include/mpark/patterns/when.hpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #ifndef MPARK_PATTERNS_WHEN_HPP 9 | #define MPARK_PATTERNS_WHEN_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace mpark::patterns { 16 | 17 | struct When { bool condition; }; 18 | 19 | template 20 | auto operator>>=(When when, F &&f) { 21 | return when.condition ? match_invoke(std::forward(f)) : no_match; 22 | } 23 | 24 | #define WHEN(condition) return mpark::patterns::When{condition} >>= [&] 25 | 26 | } // namespace mpark::patterns 27 | 28 | #endif // MPARK_PATTERNS_WHEN_HPP 29 | -------------------------------------------------------------------------------- /support/single-header.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # MPark.Patterns 4 | # 5 | # Copyright Michael Park, 2017 6 | # 7 | # Distributed under the Boost Software License, Version 1.0. 8 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 9 | 10 | import os.path 11 | import re 12 | import subprocess 13 | import sys 14 | 15 | # Prints a single header version of `include/mpark/patterns.hpp` to stdout. 16 | 17 | processed = [] 18 | 19 | def process(header): 20 | result = '' 21 | with open(header, 'r') as f: 22 | for line in f: 23 | p = re.compile('^#include "(.+)"') 24 | m = p.match(line) 25 | if m is None: 26 | result += line 27 | else: 28 | g = m.group(1) 29 | include = os.path.normpath(os.path.join(os.path.dirname(header), g)) 30 | if include not in processed: 31 | result += process(include) 32 | result += '\n' 33 | processed.append(include) 34 | return result 35 | 36 | root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).strip() 37 | result = process(os.path.join(root, 'include/mpark/patterns.hpp')) 38 | 39 | sys.stdout.write(result) 40 | -------------------------------------------------------------------------------- /support/wandbox.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | 12 | void fizzbuzz() { 13 | using namespace mpark::patterns; 14 | for (int i = 1; i <= 100; ++i) { 15 | match(i % 3, i % 5)( 16 | pattern(0, 0) = [] { std::printf("fizzbuzz\n"); }, 17 | pattern(0, _) = [] { std::printf("fizz\n"); }, 18 | pattern(_, 0) = [] { std::printf("buzz\n"); }, 19 | pattern(_, _) = [i] { std::printf("%d\n", i); }); 20 | } 21 | } 22 | 23 | int main() { 24 | fizzbuzz(); 25 | } 26 | -------------------------------------------------------------------------------- /support/wandbox.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # MPark.Patterns 4 | # 5 | # This script uploads a directory to Wandbox (http://melpon.org/wandbox), 6 | # which is an online compiler environment, and prints a permalink to the 7 | # uploaded code. We use this to provide a "Try it online" version of the 8 | # library to make the barrier to entry as low as possible. 9 | # 10 | # This script was adapted from the script proposed in 11 | # https://github.com/melpon/wandbox/issues/153. 12 | # 13 | # To know how to use this script: ./wandbox.py --help 14 | # 15 | # Copyright Louis Dionne 2015 16 | # 17 | # Distributed under the Boost Software License, Version 1.0. 18 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 19 | # 20 | # Copyright Michael Park, 2017 21 | # 22 | # Distributed under the Boost Software License, Version 1.0. 23 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 24 | 25 | import argparse 26 | import fnmatch 27 | import json 28 | import os 29 | import re 30 | import urllib2 31 | 32 | # Post the given JSON data to Wandbox's API, and return the result 33 | # as a JSON object. 34 | def upload(options): 35 | request = urllib2.Request('http://melpon.org/wandbox/api/compile.json') 36 | request.add_header('Content-Type', 'application/json') 37 | response = urllib2.urlopen(request, json.dumps(options)) 38 | return json.loads(response.read()) 39 | 40 | # Returns a list of the '.hpp' headers in the given directory and in 41 | # subdirectories. 42 | # 43 | # The path must be absolute, and the returned paths are all absolute too. 44 | def headers(path): 45 | return [ 46 | os.path.join(dir, file) 47 | for (dir, _, files) in os.walk(path) 48 | for file in fnmatch.filter(files, "*.hpp") 49 | ] 50 | 51 | def main(): 52 | parser = argparse.ArgumentParser(description= 53 | """Upload a directory to Wandbox (http://melpon.org/wandbox). 54 | 55 | On success, the program prints a permalink to the uploaded 56 | directory on Wandbox and returns 0. On error, it prints the 57 | response from the Wandbox API and returns 1. 58 | 59 | Note that the comments are stripped from all the headers in the 60 | uploaded directory. 61 | """ 62 | ) 63 | parser.add_argument('directory', type=str, help= 64 | """A directory to upload to Wandbox. 65 | 66 | The path may be either absolute or relative to the current directory. 67 | However, the names of the files uploaded to Wandbox will all be 68 | relative to this directory. This way, one can easily specify the 69 | directory to be '/some/project/include', and the uploaded files 70 | will be uploaded as-if they were rooted at '/some/project/include' 71 | """) 72 | parser.add_argument('main', type=str, help= 73 | """The main source file. 74 | 75 | The path may be either absolute or relative to the current directory. 76 | """ 77 | ) 78 | args = parser.parse_args() 79 | directory = os.path.abspath(args.directory) 80 | if not os.path.exists(directory): 81 | raise Exception("'%s' is not a valid directory" % args.directory) 82 | 83 | cpp = os.path.abspath(args.main) 84 | if not os.path.exists(cpp): 85 | raise Exception("'%s' is not a valid file name" % args.main) 86 | 87 | response = upload({ 88 | 'code': open(cpp).read().strip(), 89 | 'codes': [{ 90 | 'file': os.path.relpath(header, directory).replace('\\', '/'), 91 | 'code': open(header).read().strip() 92 | } for header in headers(directory)], 93 | 'options': 'warning,optimize', 94 | 'compiler': 'clang-5.0.0', 95 | 'save': True, 96 | 'compiler-option-raw': '-std=c++17\n-I.' 97 | }) 98 | 99 | if response['status'] == '0': 100 | print response['url'] 101 | return 0 102 | else: 103 | print response 104 | return 1 105 | 106 | exit(main()) 107 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MPark.Patterns 2 | # 3 | # Copyright Michael Park, 2017 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | cmake_minimum_required(VERSION 3.6.3) 9 | 10 | option(MPARK_PATTERNS_EXCEPTIONS 11 | "Build the tests with exceptions support." ON) 12 | 13 | if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR 14 | CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR 15 | CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 16 | add_compile_options("-std=c++17") 17 | 18 | add_compile_options(-Wall) 19 | add_compile_options(-Werror) 20 | add_compile_options(-Wextra) 21 | add_compile_options(-pedantic) 22 | add_compile_options(-Wno-shadow) 23 | endif() 24 | 25 | if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND 26 | CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") 27 | add_compile_options(-Qunused-arguments) 28 | add_compile_options(-Wno-deprecated-declarations) 29 | add_compile_options(-Wno-unknown-argument) 30 | endif() 31 | 32 | add_definitions(-DGTEST_HAS_TR1_TUPLE=0) 33 | add_definitions(-DGTEST_HAS_STD_TUPLE=1) 34 | 35 | add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/googletest/googletest 36 | ${CMAKE_BINARY_DIR}/3rdparty/googletest/googletest) 37 | 38 | config_compiler_and_linker() 39 | 40 | if(MPARK_VARIANT_EXCEPTIONS) 41 | set(compile_flags ${cxx_strict}) 42 | else() 43 | set(compile_flags ${cxx_no_exception}) 44 | endif() 45 | 46 | set(MPARK_TESTS 47 | aggregate 48 | array 49 | as 50 | balance 51 | calc 52 | ds 53 | identifier 54 | intro 55 | let 56 | optional 57 | regex 58 | variadic 59 | when 60 | ) 61 | 62 | foreach(test ${MPARK_TESTS}) 63 | add_executable(${test} ${test}.cpp) 64 | set_target_properties(${test} PROPERTIES COMPILE_FLAGS "${compile_flags}") 65 | target_link_libraries(${test} gtest_main mpark_patterns) 66 | add_test(${test} ${test} --gtest_color=yes) 67 | endforeach() 68 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # MPark.Patterns 2 | 3 | > This is an experimental library that has evolved to [P1371], being proposed for C++23. 4 | 5 | [P1371]: https://wg21.link/p1371 6 | 7 | [![release][badge.release]][release] 8 | [![travis][badge.travis]][travis] 9 | [![license][badge.license]][license] 10 | [![godbolt][badge.godbolt]][godbolt] 11 | [![wandbox][badge.wandbox]][wandbox] 12 | 13 | [badge.release]: https://img.shields.io/github/release/mpark/patterns.svg 14 | [badge.travis]: https://travis-ci.org/mpark/patterns.svg?branch=master 15 | [badge.license]: http://img.shields.io/badge/license-boost-blue.svg 16 | [badge.godbolt]: https://img.shields.io/badge/try%20it-on%20godbolt-222266.svg 17 | [badge.wandbox]: https://img.shields.io/badge/try%20it-on%20wandbox-5cb85c.svg 18 | 19 | [release]: https://github.com/mpark/patterns/releases/latest 20 | [travis]: https://travis-ci.org/mpark/patterns 21 | [license]: https://github.com/mpark/patterns/blob/master/LICENSE.md 22 | [godbolt]: https://godbolt.org/g/b7WTDt 23 | [wandbox]: https://wandbox.org/permlink/bm5oBKAuHoSucEYG 24 | 25 | ## Test 26 | 27 | This directory contains the tests for __MPark.Patterns__. 28 | 29 | ## CMake Variables 30 | 31 | - __`MPARK_PATTERNS_EXCEPTIONS`__:`BOOL` (__default__: `ON`) 32 | 33 | Build the tests with exceptions support. 34 | 35 | ## Build / Run 36 | 37 | Execute the following commands from the top-level directory: 38 | 39 | ```bash 40 | mkdir build 41 | cd build 42 | cmake -DMPARK_PATTERNS_INCLUDE_TESTS=ON .. 43 | cmake --build . 44 | ctest -V 45 | ``` 46 | -------------------------------------------------------------------------------- /test/aggregate.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | struct Aggregate { 17 | double d; 18 | std::string s; 19 | std::vector v; 20 | }; 21 | 22 | TEST(Aggregate, LRef) { 23 | Aggregate aggregate{4.2, "hello", {101, 202}}; 24 | 25 | using namespace mpark::patterns; 26 | double &result = match(aggregate)( 27 | pattern(ds(arg, "world", _)) = [](double &d) -> double & { return d; }, 28 | pattern(ds(arg, "hello", _)) = [](double &d) -> double & { return d; }); 29 | 30 | EXPECT_EQ(aggregate.d, result); 31 | EXPECT_EQ(&aggregate.d, &result); 32 | } 33 | 34 | TEST(Aggregate, ConstLRef) { 35 | const Aggregate aggregate{4.2, "hello", {101, 202}}; 36 | 37 | using namespace mpark::patterns; 38 | const double &result = match(aggregate)( 39 | pattern(ds(arg, "world", _)) = [](const double &d) -> const double & { 40 | return d; 41 | }, 42 | pattern(ds(arg, "hello", _)) = [](const double &d) -> const double & { 43 | return d; 44 | }); 45 | 46 | EXPECT_EQ(aggregate.d, result); 47 | EXPECT_EQ(&aggregate.d, &result); 48 | } 49 | 50 | TEST(Aggregate, RRef) { 51 | auto aggregate = [] { return Aggregate{4.2, "hello", {101, 202}}; }; 52 | 53 | using namespace mpark::patterns; 54 | double result = match(aggregate())( 55 | pattern(ds(arg, "world", _)) = [](double &&d) -> double { 56 | return std::move(d); 57 | }, 58 | pattern(ds(arg, "hello", _)) = [](double &&d) -> double { 59 | return std::move(d); 60 | }); 61 | 62 | EXPECT_EQ(4.2, result); 63 | } 64 | 65 | TEST(Aggregate, Empty) { 66 | struct {} empty; 67 | 68 | using namespace mpark::patterns; 69 | match(empty)(pattern(ds()) = [] {}); 70 | } 71 | 72 | TEST(Aggregate, OneChar) { 73 | struct { char x; } one{'x'}; 74 | 75 | using namespace mpark::patterns; 76 | int result = match(one)(pattern(ds('a')) = [] { return false; }, 77 | pattern(ds('x')) = [] { return true; }); 78 | 79 | EXPECT_TRUE(result); 80 | } 81 | 82 | TEST(Aggregate, OneInt) { 83 | struct { int x; } one{101}; 84 | 85 | using namespace mpark::patterns; 86 | int result = match(one)(pattern(ds(101)) = [] { return true; }, 87 | pattern(ds(1)) = [] { return false; }); 88 | 89 | EXPECT_TRUE(result); 90 | } 91 | 92 | TEST(Aggregate, TwoChars) { 93 | struct { char x; char y; } two{'x', 'y'}; 94 | 95 | using namespace mpark::patterns; 96 | bool result = match(two)(pattern(ds('a', 'b')) = [] { return false; }, 97 | pattern(ds('x', 'y')) = [] { return true; }); 98 | 99 | EXPECT_TRUE(result); 100 | } 101 | 102 | TEST(Aggregate, TwoInts) { 103 | struct { int x; int y; } two{101, 202}; 104 | 105 | using namespace mpark::patterns; 106 | bool result = match(two)(pattern(ds(1, 2)) = [] { return false; }, 107 | pattern(ds(101, 202)) = [] { return true; }); 108 | 109 | EXPECT_TRUE(result); 110 | } 111 | 112 | TEST(Aggregate, ThreeChars) { 113 | struct { char x; char y; char z; } three{'x', 'y', 'z'}; 114 | 115 | using namespace mpark::patterns; 116 | bool result = 117 | match(three)(pattern(ds('x', 'y', 'z')) = [] { return true; }, 118 | pattern(ds('a', 'b', 'c')) = [] { return false; }); 119 | 120 | EXPECT_TRUE(result); 121 | } 122 | 123 | TEST(Aggregate, FourChars) { 124 | struct { char a; char b; char c; char d; } four{'a', 'b', 'c', 'd'}; 125 | 126 | using namespace mpark::patterns; 127 | bool result = 128 | match(four)(pattern(ds('p', 'q', 'r', 's')) = [] { return false; }, 129 | pattern(ds('a', 'b', 'c', 'd')) = [] { return true; }); 130 | 131 | EXPECT_TRUE(result); 132 | } 133 | -------------------------------------------------------------------------------- /test/array.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | TEST(Array, CStyle_LRef) { 17 | int xs[3] = {1, 2, 3}; 18 | 19 | using namespace mpark::patterns; 20 | int &result = 21 | match(xs)(pattern(ds(3, 2, arg)) = [](int &x) -> int & { return x; }, 22 | pattern(ds(arg, 2, 3)) = [](int &x) -> int & { return x; }); 23 | 24 | EXPECT_EQ(xs[0], result); 25 | EXPECT_EQ(&xs[0], &result); 26 | } 27 | 28 | TEST(Array, CStyle_ConstLRef) { 29 | const int xs[3] = {1, 2, 3}; 30 | 31 | using namespace mpark::patterns; 32 | const int &result = match(xs)( 33 | pattern(ds(3, 2, arg)) = [](const int &x) -> const int & { return x; }, 34 | pattern(ds(arg, 2, 3)) = [](const int &x) -> const int & { return x; }); 35 | 36 | EXPECT_EQ(xs[0], result); 37 | EXPECT_EQ(&xs[0], &result); 38 | } 39 | 40 | TEST(Array, CStyle_RRef) { 41 | int xs[3] = {1, 2, 3}; 42 | 43 | using namespace mpark::patterns; 44 | int result = match(std::move(xs))( 45 | pattern(ds(3, 2, arg)) = [](int &&x) -> int { return std::move(x); }, 46 | pattern(ds(arg, 2, 3)) = [](int &&x) -> int { return std::move(x); }); 47 | 48 | EXPECT_EQ(1, result); 49 | } 50 | 51 | TEST(Array, Std_LRef) { 52 | std::array xs = {{"x", "y", "z"}}; 53 | 54 | using namespace mpark::patterns; 55 | std::string &result = match(xs)( 56 | pattern(ds("z", "y", arg)) = [](std::string &s) -> std::string & { 57 | return s; 58 | }, 59 | pattern(ds(arg, _ , "z")) = [](std::string &s) -> std::string & { 60 | return s; 61 | }, 62 | pattern(ds(arg, "y", _ )) = [](std::string &s) -> std::string & { 63 | return s; 64 | }); 65 | 66 | EXPECT_EQ(xs[0], result); 67 | EXPECT_EQ(&xs[0], &result); 68 | } 69 | 70 | TEST(Array, Std_ConstLRef) { 71 | const std::array xs = {{"x", "y", "z"}}; 72 | 73 | using namespace mpark::patterns; 74 | const std::string &result = match(xs)( 75 | pattern(ds("z", "y", arg)) = [](const std::string &s) -> const auto & { 76 | return s; 77 | }, 78 | pattern(ds(arg, _, "z")) = [](const std::string &s) -> const auto & { 79 | return s; 80 | }, 81 | pattern(ds(arg, "y", _)) = [](const std::string &s) -> const auto & { 82 | return s; 83 | }); 84 | 85 | EXPECT_EQ(xs[0], result); 86 | EXPECT_EQ(&xs[0], &result); 87 | } 88 | 89 | TEST(Array, Std_RRef) { 90 | auto xs = [] { return std::array{{"x", "y", "z"}}; }; 91 | 92 | using namespace mpark::patterns; 93 | std::string result = match(xs())( 94 | pattern(ds("z", "y", arg)) = [](std::string &&s) -> std::string { 95 | return std::move(s); 96 | }, 97 | pattern(ds(arg, _ , "z")) = [](std::string &&s) -> std::string { 98 | return std::move(s); 99 | }, 100 | pattern(ds(arg, "y", _ )) = [](std::string &&s) -> std::string { 101 | return std::move(s); 102 | }); 103 | 104 | EXPECT_EQ("x", result); 105 | } 106 | -------------------------------------------------------------------------------- /test/as.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | struct Shape { virtual ~Shape() = default; }; 18 | 19 | struct Circle : Shape {}; 20 | struct Square : Shape {}; 21 | struct Triangle : Shape {}; 22 | 23 | TEST(As, Polymorphic_Reference) { 24 | Circle circle; 25 | const Shape& shape = circle; 26 | using namespace mpark::patterns; 27 | int result = match(shape)(pattern(as(_)) = [] { return 1; }, 28 | pattern(as(_)) = [] { return 2; }, 29 | pattern(as(_)) = [] { return 3; }); 30 | 31 | EXPECT_EQ(1, result); 32 | } 33 | 34 | TEST(As, Polymorphic_Pointer) { 35 | std::unique_ptr shape = std::make_unique(); 36 | using namespace mpark::patterns; 37 | match(shape)(pattern(some(as(_))) = [] { return 1; }, 38 | pattern(some(as(_))) = [] { return 2; }, 39 | pattern(some(as(_))) = [] { return 3; }); 40 | } 41 | 42 | TEST(As, Variant_Unary) { 43 | std::variant v = 42; 44 | using namespace mpark::patterns; 45 | match(v)( 46 | pattern(as(arg)) = [](const auto &n) { EXPECT_EQ(42, n); }, 47 | pattern(as(arg)) = [](const auto &) { EXPECT_FALSE(true); }); 48 | } 49 | 50 | TEST(As, Variant_Binary) { 51 | using str = std::string; 52 | 53 | std::vector> vs = {101, "hello"}; 54 | std::vector> ws = {202, "world"}; 55 | for (const auto &v : vs) { 56 | for (const auto &w : ws) { 57 | using namespace mpark::patterns; 58 | match(v, w)( 59 | pattern(as(arg), as(arg)) = [](auto x, auto y) { 60 | EXPECT_EQ(101, x); 61 | EXPECT_EQ(202, y); 62 | }, 63 | pattern(as(arg), as(arg)) = [](auto x, auto y) { 64 | EXPECT_EQ(101, x); 65 | EXPECT_EQ("world", y); 66 | }, 67 | pattern(as(arg), as(arg)) = [](auto x, auto y) { 68 | EXPECT_EQ("hello", x); 69 | EXPECT_EQ(202, y); 70 | }, 71 | pattern(as(arg), as(arg)) = [](auto x, auto y) { 72 | EXPECT_EQ("hello", x); 73 | EXPECT_EQ("world", y); 74 | }); 75 | } 76 | } 77 | } 78 | 79 | TEST(As, Any) { 80 | using str = std::string; 81 | 82 | std::any a = str("hello"); 83 | 84 | using namespace mpark::patterns; 85 | match(a)( 86 | pattern(as(arg)) = [](const auto &) { EXPECT_FALSE(true); }, 87 | pattern(as(arg)) = [](const auto &s) { EXPECT_EQ("hello", s); }); 88 | } 89 | -------------------------------------------------------------------------------- /test/balance.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | namespace memory { 22 | 23 | template 24 | struct part_ptr : std::unique_ptr { 25 | using super = std::unique_ptr; 26 | using super::super; 27 | 28 | T &operator*() & { return *this->get(); } 29 | const T &operator*() const & { return *this->get(); } 30 | T &&operator*() && { return std::move(*this->get()); } 31 | const T &&operator*() const && { return std::move(*this->get()); } 32 | }; 33 | 34 | template 35 | auto make_part(Args&&... args) { 36 | return part_ptr(new T(std::forward(args)...)); 37 | } 38 | 39 | } // namespace memory 40 | 41 | namespace utility { 42 | 43 | template 44 | std::string stringify(Arg &&arg) { 45 | std::ostringstream strm; 46 | strm << std::forward(arg); 47 | return strm.str(); 48 | } 49 | 50 | } // namespace utility 51 | 52 | namespace rb { 53 | 54 | enum Color { Red, Black }; 55 | 56 | template class Node; 57 | 58 | template struct is_node : std::false_type {}; 59 | template struct is_node> : std::true_type {}; 60 | 61 | template 62 | class Node { 63 | public: 64 | explicit Node(Color color, 65 | memory::part_ptr &&lhs, 66 | T t, 67 | memory::part_ptr &&rhs) 68 | : color_(color), 69 | lhs_(std::move(lhs)), 70 | t_(std::move(t)), 71 | rhs_(std::move(rhs)) {} 72 | 73 | void balance(); 74 | 75 | private: 76 | Color color_; 77 | memory::part_ptr lhs_; 78 | T t_; 79 | memory::part_ptr rhs_; 80 | 81 | friend std::ostream &operator<<(std::ostream &strm, const Node &node) { 82 | const auto & [color, lhs, value, rhs] = node; 83 | strm << '{' << (color ? "Black" : "Red") << ','; 84 | if (lhs) { 85 | strm << *lhs; 86 | } else { 87 | strm << "null"; 88 | } 89 | strm << ',' << value << ','; 90 | if (rhs) { 91 | strm << *rhs; 92 | } else { 93 | strm << "null"; 94 | } 95 | strm << '}'; 96 | return strm; 97 | } 98 | 99 | template >::value, int> = 0> 102 | friend auto &&get(FwdNode &&node) noexcept { 103 | if constexpr (I == 0) return std::forward(node).color_; 104 | else if constexpr (I == 1) return std::forward(node).lhs_; 105 | else if constexpr (I == 2) return std::forward(node).t_; 106 | else if constexpr (I == 3) return std::forward(node).rhs_; 107 | } 108 | }; 109 | 110 | template 111 | void Node::balance() { 112 | using namespace mpark::patterns; 113 | IDENTIFIERS(a, b, c, d, x, y, z); 114 | match(std::move(*this))( 115 | pattern( 116 | anyof(ds(Black, some(ds(Red, some(ds(Red, a, x, b)), y, c)), z, d), 117 | ds(Black, some(ds(Red, a, x, some(ds(Red, b, y, c)))), z, d), 118 | ds(Black, a, x, some(ds(Red, some(ds(Red, b, y, c)), z, d))), 119 | ds(Black, a, x, some(ds(Red, b, y, some(ds(Red, c, z, d))))))) = 120 | [&](auto &&a, auto &&x, auto &&b, auto &&y, auto &&c, auto &&z, auto &&d) { 121 | #define FWD(x) std::forward(x) 122 | 123 | *this = Node(Red, 124 | memory::make_part(Black, FWD(a), FWD(x), FWD(b)), 125 | y, 126 | memory::make_part(Black, FWD(c), FWD(z), FWD(d))); 127 | #undef FWD 128 | }, 129 | pattern(_) = [] {}); 130 | } 131 | 132 | } // namespace rb 133 | 134 | namespace std { 135 | 136 | template 137 | class tuple_size> : public integral_constant {}; 138 | 139 | template 140 | class tuple_element<0, rb::Node> { public: using type = rb::Color; }; 141 | 142 | template 143 | class tuple_element<1, rb::Node> { 144 | public: 145 | using type = memory::part_ptr>; 146 | }; 147 | 148 | template 149 | class tuple_element<2, rb::Node> { public: using type = T; }; 150 | 151 | template 152 | class tuple_element<3, rb::Node> { 153 | public: 154 | using type = memory::part_ptr>; 155 | }; 156 | 157 | } // namespace std 158 | 159 | TEST(Balance, Typical) { 160 | auto tree = memory::make_part>( 161 | rb::Black, 162 | memory::make_part>( 163 | rb::Red, 164 | memory::make_part>(rb::Red, nullptr, 101, nullptr), 165 | 202, 166 | nullptr), 167 | 303, 168 | nullptr); 169 | 170 | std::string before = "{Black,{Red,{Red,null,101,null},202,null},303,null}"; 171 | EXPECT_EQ(before, utility::stringify(*tree)); 172 | 173 | tree->balance(); 174 | 175 | std::string after = "{Red,{Black,null,101,null},202,{Black,null,303,null}}"; 176 | EXPECT_EQ(after, utility::stringify(*tree)); 177 | } 178 | -------------------------------------------------------------------------------- /test/calc.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | namespace memory { 21 | 22 | template 23 | struct part_ptr : std::unique_ptr { 24 | using super = std::unique_ptr; 25 | using super::super; 26 | 27 | T &operator*() & { return *this->get(); } 28 | const T &operator*() const & { return *this->get(); } 29 | T &&operator*() && { return std::move(*this->get()); } 30 | const T &&operator*() const && { return std::move(*this->get()); } 31 | }; 32 | 33 | template 34 | auto make_part(Args&&... args) { 35 | return part_ptr(new T(std::forward(args)...)); 36 | } 37 | 38 | } // namespace memory 39 | 40 | namespace utility { 41 | 42 | template 43 | std::string stringify(Arg &&arg) { 44 | std::ostringstream strm; 45 | strm << std::forward(arg); 46 | return strm.str(); 47 | } 48 | 49 | } // namespace utility 50 | 51 | namespace calc { 52 | 53 | class Expr; 54 | 55 | class Unary { 56 | public: 57 | explicit Unary(Expr &&expr); 58 | 59 | private: 60 | memory::part_ptr expr_; 61 | 62 | template >, 65 | int> = 0> 66 | friend auto &&get(FwdUnary &&unary) noexcept { 67 | if constexpr (I == 0) return *std::forward(unary).expr_; 68 | } 69 | }; 70 | 71 | class Binary { 72 | public: 73 | explicit Binary(Expr &&lhs, Expr &&rhs); 74 | 75 | private: 76 | memory::part_ptr lhs_, rhs_; 77 | 78 | template < 79 | std::size_t I, 80 | typename FwdBinary, 81 | std::enable_if_t>, 82 | int> = 0> 83 | friend auto &&get(FwdBinary &&binary) noexcept { 84 | if constexpr (I == 0) return *std::forward(binary).lhs_; 85 | if constexpr (I == 1) return *std::forward(binary).rhs_; 86 | } 87 | }; 88 | 89 | class Func : public Unary { public: using Unary::Unary; }; 90 | class Plus : public Binary { public: using Binary::Binary; }; 91 | class Mult : public Binary { public: using Binary::Binary; }; 92 | 93 | class Expr : public std::variant { 94 | public: 95 | using variant::variant; 96 | }; 97 | 98 | Unary::Unary(Expr &&expr) : expr_(memory::make_part(std::move(expr))) {} 99 | 100 | Binary::Binary(Expr &&lhs, Expr &&rhs) 101 | : lhs_(memory::make_part(std::move(lhs))), 102 | rhs_(memory::make_part(std::move(rhs))) {} 103 | 104 | } // namespace calc 105 | 106 | namespace std { 107 | 108 | template <> 109 | class tuple_size : public integral_constant {}; 110 | 111 | template <> 112 | class tuple_size : public integral_constant {}; 113 | 114 | template <> class tuple_size : public tuple_size {}; 115 | template <> class tuple_size : public tuple_size {}; 116 | template <> class tuple_size : public tuple_size {}; 117 | 118 | template <> 119 | struct variant_size : public integral_constant {}; 120 | 121 | } // namespace std 122 | 123 | namespace calc { 124 | 125 | std::ostream &operator<<(std::ostream &strm, const Expr &expr) { 126 | using namespace mpark::patterns; 127 | IDENTIFIERS(lhs, rhs); 128 | match(expr)( 129 | pattern(as(arg)) = [&](auto x) { strm << x; }, 130 | pattern(as(ds(lhs, rhs))) = [&](auto &&lhs, auto &&rhs) { 131 | strm << "(+ " << lhs << ' ' << rhs << ')'; 132 | }, 133 | pattern(as(ds(lhs, rhs))) = [&](auto &&lhs, auto &&rhs) { 134 | strm << "(* " << lhs << ' ' << rhs << ')'; 135 | }, 136 | pattern(as(ds(arg))) = [&](auto &&body) { 137 | strm << "(fn [] " << body << ')'; 138 | }); 139 | return strm; 140 | } 141 | 142 | int eval(const Expr &expr) { 143 | using namespace mpark::patterns; 144 | IDENTIFIERS(lhs, rhs); 145 | return match(expr)( 146 | pattern(as(arg)) = [](auto x) { return x; }, 147 | pattern(as(ds(lhs, rhs))) = [](auto &&lhs, auto &&rhs) { 148 | return eval(lhs) + eval(rhs); 149 | }, 150 | pattern(as(ds(lhs, rhs))) = [](auto &&lhs, auto &&rhs) { 151 | return eval(lhs) * eval(rhs); 152 | }, 153 | pattern(as(ds(arg))) = [](auto &&body) { return eval(body); }); 154 | } 155 | 156 | } // namespace calc 157 | 158 | TEST(Calc, Int) { 159 | using namespace calc; 160 | Expr expr = 101; 161 | EXPECT_EQ("101", utility::stringify(expr)); 162 | EXPECT_EQ(101, eval(expr)); 163 | } 164 | 165 | TEST(Calc, MultPlus) { 166 | using namespace calc; 167 | Expr expr = Mult(Plus(Expr(101), Expr(202)), 303); 168 | EXPECT_EQ("(* (+ 101 202) 303)", utility::stringify(expr)); 169 | EXPECT_EQ(91809, eval(expr)); 170 | } 171 | 172 | TEST(Calc, FuncPlus) { 173 | using namespace calc; 174 | Expr expr = Func(Plus(Expr(101), Expr(202))); 175 | EXPECT_EQ("(fn [] (+ 101 202))", utility::stringify(expr)); 176 | EXPECT_EQ(303, eval(expr)); 177 | } 178 | -------------------------------------------------------------------------------- /test/ds.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | namespace N { 19 | 20 | struct S { 21 | S(int x_, std::string y_) : x(x_), y(std::move(y_)) {} 22 | 23 | int x; 24 | std::string y; 25 | }; 26 | 27 | template 28 | auto &&get(const S &s) { 29 | if constexpr (I == 0) return s.x; 30 | else if constexpr (I == 1) return s.y; 31 | } 32 | 33 | } // namespace N 34 | 35 | namespace std { 36 | 37 | template <> class tuple_size : public integral_constant {}; 38 | template <> class tuple_element<0, N::S> { public: using type = int; }; 39 | template <> class tuple_element<1, N::S> { public: using type = string; }; 40 | 41 | } // namespace std 42 | 43 | TEST(Destructure, Pair) { 44 | std::vector> points = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; 45 | 46 | int origin = 0; 47 | int y_zero = 0; 48 | int otherwise = 0; 49 | for (const auto &point : points) { 50 | using namespace mpark::patterns; 51 | match(point)(pattern(ds(0, 0)) = [&origin] { ++origin; }, 52 | pattern(ds(_, 0)) = [&y_zero] { ++y_zero; }, 53 | pattern(ds(_, _)) = [&otherwise] { ++otherwise; }); 54 | } 55 | 56 | EXPECT_EQ(1, origin); 57 | EXPECT_EQ(2, y_zero); 58 | EXPECT_EQ(1, otherwise); 59 | } 60 | 61 | TEST(Destructure, Custom) { 62 | N::S s(101, "world"); 63 | 64 | using namespace mpark::patterns; 65 | int result = match(s)( 66 | pattern(ds(0, "")) = [] { return 0; }, 67 | pattern(arg(ds(101, arg))) = [](const auto &x, const auto &y) { 68 | static_assert(std::is_same>::value, ""); 69 | static_assert( 70 | std::is_same>::value, ""); 71 | return 1; 72 | }); 73 | 74 | EXPECT_EQ(1, result); 75 | } 76 | -------------------------------------------------------------------------------- /test/identifier.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | TEST(Identifier, Simple) { 16 | std::tuple t = {101, 202, 101}; 17 | std::optional o = 202; 18 | 19 | using namespace mpark::patterns; 20 | IDENTIFIERS(x, y); 21 | int actual = match(t, o)( 22 | pattern(ds(x, x, x), some(x)) = [](auto &&) { return 1; }, 23 | pattern(ds(x, y, x), some(y)) = [](auto &&, auto &&) { return 2; }, 24 | pattern(_, _) = [] { return 3; }); 25 | 26 | EXPECT_EQ(2, actual); 27 | } 28 | 29 | TEST(Identifier, Complex) { 30 | std::tuple, int> t = {{101}, 202}; 31 | std::optional o = 101; 32 | 33 | using namespace mpark::patterns; 34 | IDENTIFIERS(x, y); 35 | int actual = match(t, o)( 36 | pattern(ds(x(some(202)), y), x) = [](auto &&, auto &&) { return 1; }, 37 | pattern(ds(x(some(101)), y), x) = [](auto &&, auto &&) { return 2; }, 38 | pattern(ds(x(some(101)), y), y) = [](auto &&, auto &&) { return 3; }); 39 | 40 | EXPECT_EQ(2, actual); 41 | } 42 | 43 | TEST(Identifier, Discards) { 44 | std::tuple t = {101, 202, 101}; 45 | std::optional o = 202; 46 | 47 | using namespace mpark::patterns; 48 | IDENTIFIERS(x, _y, z); 49 | int actual = match(t, o)( 50 | pattern(ds(x , x , x ), some(x )) = [](auto &&) { return 1; }, 51 | pattern(ds(x , _y, _y), some(x )) = [](auto &&) { return 2; }, 52 | pattern(ds(x , _y, x ), some(_y)) = [](auto &&) { return 3; }, 53 | pattern(ds(_y, _y, x ), some(z )) = [](auto &&, auto &&) { return 4; }); 54 | 55 | EXPECT_EQ(3, actual); 56 | } 57 | -------------------------------------------------------------------------------- /test/intro.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | auto fizzbuzz_v1() { 22 | std::vector> result; 23 | 24 | using namespace mpark::patterns; 25 | for (int i = 1; i <= 100; ++i) { 26 | match(i % 3, i % 5)(pattern(0, 0) = [&] { result.push_back("fizzbuzz"); }, 27 | pattern(0, _) = [&] { result.push_back("fizz"); }, 28 | pattern(_, 0) = [&] { result.push_back("buzz"); }, 29 | pattern(_, _) = [&] { result.push_back(i); }); 30 | } 31 | 32 | return result; 33 | } 34 | 35 | auto fizzbuzz_v2() { 36 | std::vector> result; 37 | 38 | using namespace mpark::patterns; 39 | for (int i = 1; i <= 100; ++i) { 40 | IDENTIFIERS(x); 41 | match(i)( 42 | pattern(_).when(_ % 15 == 0) = [&] { result.push_back("fizzbuzz"); }, 43 | pattern(arg).when(arg % 3 == 0) = [&](auto) { result.push_back("fizz"); }, 44 | pattern(x).when(x % 5 == 0) = [&](auto) { result.push_back("buzz"); }, 45 | pattern(x) = [&](auto x) { result.push_back(x); }); 46 | } 47 | 48 | return result; 49 | } 50 | 51 | int factorial(int n) { 52 | using namespace mpark::patterns; 53 | return match(n)(pattern(0) = [] { return 1; }, 54 | pattern(arg) = [](int n) { return n * factorial(n - 1); }); 55 | } 56 | 57 | int fib_v0(int n) { 58 | using namespace mpark::patterns; 59 | assert(n >= 0); 60 | return match(n)( 61 | pattern(0) = [] { return 0; }, 62 | pattern(1) = [] { return 1; }, 63 | pattern(arg) = [](int n) { return fib_v0(n - 1) + fib_v0(n - 2); }); 64 | } 65 | 66 | int fib_v1(int n) { 67 | using namespace mpark::patterns; 68 | IDENTIFIERS(x); 69 | return match(n)( 70 | pattern(x).when(x <= 0) = [](int) { return 0; }, 71 | pattern(1) = [] { return 1; }, 72 | pattern(x) = [](int x) { return fib_v1(x - 1) + fib_v1(x - 2); }); 73 | } 74 | 75 | int fib_v2(int n) { 76 | using namespace mpark::patterns; 77 | return match(n)( 78 | pattern(arg).when(arg < 0) = [](int) { return 0; }, 79 | pattern(arg(anyof(0, 1))) = [](int n) { return n; }, 80 | pattern(arg) = [](int n) { return fib_v2(n - 1) + fib_v2(n - 2); }); 81 | } 82 | 83 | TEST(Intro, Fizzbuzz) { 84 | EXPECT_EQ(fizzbuzz_v1(), fizzbuzz_v2()); 85 | } 86 | 87 | TEST(Intro, Factorial) { 88 | EXPECT_EQ(120, factorial(5)); 89 | EXPECT_EQ(3628800, factorial(10)); 90 | } 91 | 92 | TEST(Intro, Fibonacci) { 93 | EXPECT_EQ(55, fib_v0(10)); 94 | EXPECT_EQ(55, fib_v1(10)); 95 | EXPECT_EQ(55, fib_v2(10)); 96 | } 97 | 98 | TEST(Intro, ExplicitReturnType) { 99 | std::optional o(4.2); 100 | 101 | using namespace mpark::patterns; 102 | auto x = match(o)( 103 | pattern(some(arg)) = [](double v) { return v; }, 104 | pattern(none) = [] { return 'A'; }); 105 | 106 | static_assert(std::is_same::value, ""); 107 | EXPECT_EQ(4, x); 108 | } 109 | 110 | TEST(Intro, ExplicitReturnVoid) { 111 | std::optional o(4.2); 112 | 113 | using namespace mpark::patterns; 114 | match(o)( 115 | pattern(some(arg)) = [](double v) { return v; }, 116 | pattern(none) = [] { return 'A'; }); 117 | } 118 | 119 | TEST(Intro, ExplicitReturnImplicitConversion) { 120 | struct S { 121 | S(long) { EXPECT_TRUE(true); } 122 | explicit S(int) { EXPECT_TRUE(false); } 123 | }; 124 | 125 | using namespace mpark::patterns; 126 | match(0)(pattern(_) = [] { return 42; }); 127 | } 128 | -------------------------------------------------------------------------------- /test/let.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | TEST(Let, If) { 15 | std::optional expected = 42; 16 | std::optional actual; 17 | 18 | using namespace mpark::patterns; 19 | if_let (pattern(some(arg)) = expected) = [&](auto x) { actual = x; }; 20 | 21 | EXPECT_EQ(expected, actual); 22 | } 23 | 24 | TEST(Let, For) { 25 | std::vector> pairs = {{1, 0}, {1, 1}, {0, 0}, {1, 2}}; 26 | 27 | std::vector expected = {0, 1, 2}; 28 | std::vector actual; 29 | 30 | using namespace mpark::patterns; 31 | for_let (pattern(ds(1, arg)) = pairs) = [&](auto x) { actual.push_back(x); }; 32 | 33 | EXPECT_EQ(expected, actual); 34 | } 35 | 36 | TEST(Let, ForBreak) { 37 | std::vector> pairs = {{1, 0}, {1, 1}, {0, 0}, {1, 2}}; 38 | 39 | std::vector expected = {0}; 40 | std::vector actual; 41 | 42 | using namespace mpark::patterns; 43 | for_let (pattern(ds(1, arg)) = pairs) = [&](auto x) { 44 | if (x == 1) { return Break; } 45 | actual.push_back(x); 46 | return Continue; 47 | }; 48 | 49 | EXPECT_EQ(expected, actual); 50 | } 51 | 52 | TEST(Let, ForContinue) { 53 | std::vector> pairs = {{1, 0}, {1, 1}, {0, 0}, {1, 2}}; 54 | 55 | std::vector expected = {1, 2}; 56 | std::vector actual; 57 | 58 | using namespace mpark::patterns; 59 | for_let (pattern(ds(1, arg)) = pairs) = [&](auto x) { 60 | if (x == 0) { return Continue; } 61 | actual.push_back(x); 62 | return Continue; 63 | }; 64 | 65 | EXPECT_EQ(expected, actual); 66 | } 67 | -------------------------------------------------------------------------------- /test/optional.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | TEST(Optional, Pointer) { 15 | auto holds = [](int *p) { 16 | using namespace mpark::patterns; 17 | return match(p)(pattern(some(_)) = [] { return true; }, 18 | pattern(none) = [] { return false; }); 19 | }; 20 | 21 | int x = 42; 22 | EXPECT_TRUE(holds(&x)); 23 | EXPECT_FALSE(holds(nullptr)); 24 | } 25 | 26 | TEST(Optional, Std) { 27 | auto test_optional = [](const std::optional> &oo) { 28 | using namespace mpark::patterns; 29 | return match(oo)(pattern(some(some(_))) = [] { return 0; }, 30 | pattern(some(none)) = [] { return 1; }, 31 | pattern(none) = [] { return 2; }); 32 | }; 33 | 34 | std::optional> oo1(42); 35 | std::optional> oo2(std::optional{}); 36 | std::optional> oo3; 37 | 38 | EXPECT_EQ(0, test_optional(oo1)); 39 | EXPECT_EQ(1, test_optional(oo2)); 40 | EXPECT_EQ(2, test_optional(oo3)); 41 | } 42 | -------------------------------------------------------------------------------- /test/regex.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | struct Token { 21 | enum Kind { ID, NUM, OP, WS }; 22 | 23 | Token(Kind kind_, std::string lexeme_) 24 | : kind(kind_), lexeme(std::move(lexeme_)) {} 25 | 26 | Kind kind; 27 | std::string lexeme; 28 | }; 29 | 30 | bool operator==(const Token &lhs, const Token &rhs) { 31 | return std::tie(lhs.kind, lhs.lexeme) == std::tie(rhs.kind, rhs.lexeme); 32 | } 33 | 34 | bool operator!=(const Token &lhs, const Token &rhs) { return !(lhs == rhs); } 35 | 36 | TEST(Regex, String) { 37 | auto lex = [](std::string_view sv) { 38 | using namespace mpark::patterns; 39 | return match(sv)( 40 | pattern(arg(re.match(R"~([_a-zA-Z]\w*)~"))) = [](auto lexeme) { 41 | return Token(Token::ID, std::string(lexeme)); 42 | }, 43 | pattern(arg(re.match(R"~(-?\d+)~"))) = [](auto lexeme) { 44 | return Token(Token::NUM, std::string(lexeme)); 45 | }, 46 | pattern(arg(re.match(R"~([*|/|+|-])~"))) = [](auto lexeme) { 47 | return Token(Token::OP, std::string(lexeme)); 48 | }); 49 | }; 50 | 51 | EXPECT_EQ(lex("foobar"), Token(Token::ID, "foobar")); 52 | EXPECT_EQ(lex("x"), Token(Token::ID, "x")); 53 | EXPECT_EQ(lex("x0"), Token(Token::ID, "x0")); 54 | 55 | EXPECT_EQ(lex("42"), Token(Token::NUM, "42")); 56 | EXPECT_EQ(lex("101"), Token(Token::NUM, "101")); 57 | EXPECT_EQ(lex("-202"), Token(Token::NUM, "-202")); 58 | 59 | EXPECT_EQ(lex("*"), Token(Token::OP, "*")); 60 | EXPECT_EQ(lex("/"), Token(Token::OP, "/")); 61 | EXPECT_EQ(lex("+"), Token(Token::OP, "+")); 62 | EXPECT_EQ(lex("-"), Token(Token::OP, "-")); 63 | } 64 | 65 | TEST(Regex, Stream) { 66 | std::regex id(R"~([_a-zA-Z]\w*)~"); 67 | std::regex num(R"~(-?\d+)~"); 68 | std::regex op(R"~([*|/|+|-])~"); 69 | 70 | std::istringstream strm("foo + -42 - x * 101 / bar"); 71 | 72 | std::vector expected = {{Token::ID, "foo"}, 73 | {Token::OP, "+"}, 74 | {Token::NUM, "-42"}, 75 | {Token::OP, "-"}, 76 | {Token::ID, "x"}, 77 | {Token::OP, "*"}, 78 | {Token::NUM, "101"}, 79 | {Token::OP, "/"}, 80 | {Token::ID, "bar"}}; 81 | 82 | std::vector actual; 83 | for (std::string token; strm >> token; ) { 84 | using namespace mpark::patterns; 85 | actual.push_back(match(token)( 86 | pattern(arg(re.match(id))) = [](const auto &lexeme) { 87 | return Token(Token::ID, lexeme); 88 | }, 89 | pattern(arg(re.match(num))) = [](const auto &lexeme) { 90 | return Token(Token::NUM, lexeme); 91 | }, 92 | pattern(arg(re.match(op))) = [](const auto &lexeme) { 93 | return Token(Token::OP, lexeme); 94 | })); 95 | } 96 | 97 | EXPECT_EQ(expected, actual); 98 | } 99 | -------------------------------------------------------------------------------- /test/variadic.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace utility { 18 | 19 | template 20 | decltype(auto) apply(F &&f, Tuple &&t) { 21 | using namespace mpark::patterns; 22 | return match(std::forward(t))( 23 | pattern(ds(variadic(arg))) = std::forward(f)); 24 | } 25 | 26 | } // namespace utility 27 | 28 | TEST(Variadic, Apply) { 29 | std::tuple x = {42, "hello"}; 30 | utility::apply( 31 | [](const auto &lhs, const auto &rhs) { 32 | EXPECT_EQ(42, lhs); 33 | EXPECT_EQ("hello", rhs); 34 | }, 35 | x); 36 | } 37 | 38 | namespace utility { 39 | 40 | template 41 | decltype(auto) visit(F &&f, Vs &&... vs) { 42 | using namespace mpark::patterns; 43 | return match(std::forward(vs)...)( 44 | pattern(variadic(vis(arg))) = std::forward(f)); 45 | } 46 | 47 | } // namespace utility 48 | 49 | struct Visitor { 50 | void operator()(int lhs, const std::string &rhs) const { 51 | EXPECT_EQ(42, lhs); 52 | EXPECT_EQ("hello", rhs); 53 | } 54 | template 55 | void operator()(const T &, const U &) const { 56 | EXPECT_TRUE(false); 57 | } 58 | }; 59 | 60 | TEST(Variadic, Visit) { 61 | std::variant x = 42, y = "hello"; 62 | utility::visit(Visitor{}, x, y); 63 | } 64 | 65 | TEST(Variadic, Middle) { 66 | std::tuple tuple = {42, 101, 101, 42}; 67 | 68 | using namespace mpark::patterns; 69 | IDENTIFIERS(x, y); 70 | int result = match(tuple)( 71 | pattern(ds(variadic(x))) = [](auto) { return 1; }, 72 | pattern(ds(x, variadic(arg), x)) = [](auto, auto... args) { 73 | static_assert(sizeof...(args) == 2); 74 | return 2; 75 | }, 76 | pattern(ds(x, y, y, variadic(arg), x)) = [](auto, auto, auto... args) { 77 | static_assert(sizeof...(args) == 0); 78 | return 3; 79 | }); 80 | 81 | EXPECT_EQ(2, result); 82 | } 83 | -------------------------------------------------------------------------------- /test/when.cpp: -------------------------------------------------------------------------------- 1 | // MPark.Patterns 2 | // 3 | // Copyright Michael Park, 2017 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | TEST(When, SubscriptOperatorToBool) { 15 | std::array xs = {{false, false, true}}; 16 | 17 | using namespace mpark::patterns; 18 | IDENTIFIERS(_x); 19 | int result = match(xs)(pattern(ds(_x, _, _)).when(_x) = [] { return 0; }, 20 | pattern(ds(_, _x, _)).when(_x) = [] { return 1; }, 21 | pattern(ds(_, _, _x)).when(_x) = [] { return 2; }); 22 | 23 | EXPECT_EQ(2, result); 24 | } 25 | 26 | TEST(When, SubscriptOperator) { 27 | std::array xs = {{1, 2, 3}}; 28 | 29 | using namespace mpark::patterns; 30 | int result = match(xs)(pattern(_).when(_[0] == 1) = [] { return 0; }, 31 | pattern(_).when(_[1] == 1) = [] { return 1; }, 32 | pattern(_).when(_[2] == 1) = [] { return 2; }); 33 | 34 | EXPECT_EQ(0, result); 35 | } 36 | 37 | TEST(When, CallOperator_Guard) { 38 | std::array xs = { 39 | {[] { return 0; }, [] { return 1; }, [] { return 2; }}}; 40 | 41 | using namespace mpark::patterns; 42 | int result = match(xs)(pattern(_).when(_[0]() == 1) = [] { return 0; }, 43 | pattern(_).when(_[1]() == 1) = [] { return 1; }, 44 | pattern(_).when(_[2]() == 1) = [] { return 2; }); 45 | 46 | EXPECT_EQ(1, result); 47 | } 48 | 49 | TEST(When, CallOperator_Identifier) { 50 | std::array xs = { 51 | {[] { return 0; }, [] { return 1; }, [] { return 2; }}}; 52 | 53 | using namespace mpark::patterns; 54 | IDENTIFIERS(x, y, z); 55 | int result = match(xs)( 56 | pattern(ds(x, y, z)).when(x() == 2) = [](auto...) { return 0; }, 57 | pattern(ds(x, y, z)).when(y() == 2) = [](auto...) { return 1; }, 58 | pattern(ds(x, y, z)).when(z() == 2) = [](auto...) { return 2; }); 59 | 60 | EXPECT_EQ(2, result); 61 | } 62 | 63 | TEST(When, UnaryCallOperator_Identifier) { 64 | std::array xs = {{[](int x) { return x; }, 65 | [](int x) { return x + 1; }, 66 | [](int x) { return x + 2; }}}; 67 | 68 | using namespace mpark::patterns; 69 | IDENTIFIERS(x, y, z); 70 | int result = match(xs)( 71 | pattern(ds(x, y, z)).when(x(0) == 2) = [](auto...) { return 0; }, 72 | pattern(ds(x, y, z)).when(y(0) == 2) = [](auto...) { return 1; }, 73 | pattern(ds(x, y, z)).when(z(0) == 2) = [](auto...) { return 2; }); 74 | 75 | EXPECT_EQ(2, result); 76 | } 77 | 78 | TEST(When, CallTwice_Identifier) { 79 | using IntToInt = int (*)(int); 80 | std::array xs = { 81 | {[](int) -> IntToInt { return [](int x) { return x; }; }, 82 | [](int) -> IntToInt { return [](int x) { return x + 1; }; }, 83 | [](int) -> IntToInt { return [](int x) { return x + 2; }; }}}; 84 | 85 | using namespace mpark::patterns; 86 | IDENTIFIERS(x, y, z); 87 | int result = match(xs)( 88 | pattern(ds(x, y, z)).when(x(0)(0) == 1) = [](auto...) { return 0; }, 89 | pattern(ds(x, y, z)).when(y(0)(0) == 1) = [](auto...) { return 1; }, 90 | pattern(ds(x, y, z)).when(z(0)(0) == 1) = [](auto...) { return 2; }); 91 | 92 | EXPECT_EQ(1, result); 93 | } 94 | --------------------------------------------------------------------------------