├── LICENSE ├── README.md └── uninttp ├── fmt_support.cppm ├── uni_auto.cppm └── uni_auto.hpp /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-... reacfen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # uninttp 3 | 4 | A universal type for non-type template parameters for C++20 or later. 5 | 6 | ## Installation: 7 | 8 | uninttp (***Uni***versal ***N***on-***T***ype ***T***emplate ***P***arameters) is a header-only library. Simply clone this repository and you're ready to go. 9 | 10 | Once that's done, you can include the necessary header(s) and start using uninttp in your project: 11 | 12 | ```cpp 13 | #include 14 | ``` 15 | 16 | uninttp also has a C++ module version, so, if your compiler supports [C++20 modules](https://en.cppreference.com/w/cpp/language/modules), you can do something like this instead of including the whole header file into your project: 17 | 18 | ```cpp 19 | import uninttp.uni_auto; // Improves compilation speed 20 | 21 | // Uncomment the lines below for fmtlib support 22 | // import fmt; 23 | // import uninttp.fmt_support; 24 | ``` 25 | 26 | ## Usage: 27 | 28 | Using uninttp's `uninttp::uni_auto` is pretty straightforward and is synonymous to `auto` in *most* of the cases: [Demo](https://godbolt.org/z/fhWhaf5hx) 29 | 30 | ```cpp 31 | #include 32 | 33 | using namespace uninttp; 34 | 35 | template 36 | constexpr auto add20() { 37 | return Value + 20; 38 | } 39 | 40 | int main() { 41 | static_assert(add20<20>() == 40); // OK 42 | } 43 | ``` 44 | 45 | And if you thought, "Can't I just use something like `template ` instead?", then you'd be absolutely correct. One can safely replace `uni_auto` with `auto`, at least for *this* example. 46 | 47 | However, a template parameter declared with `uni_auto` can do much more than a template parameter declared with `auto` in the sense that you can also pass string literals, `constexpr`-marked arrays, arrays of static storage duration, etc., through it: [Demo](https://godbolt.org/z/vzdTnvK4j) 48 | 49 | ```cpp 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | using namespace uninttp; 57 | 58 | template 59 | constexpr auto shift() { 60 | return Value + X; 61 | } 62 | 63 | template 64 | void print_array() { 65 | // Using a range-based `for`-loop 66 | for (const auto& elem : Array) 67 | std::cout << elem << ' '; 68 | 69 | std::cout << '\n'; 70 | 71 | // Using iterators 72 | for (auto it = std::begin(Array); it != std::end(Array); ++it) 73 | std::cout << *it << ' '; 74 | 75 | std::cout << '\n'; 76 | 77 | // Using an index-based `for`-loop 78 | for (std::size_t i = 0; i < std::size(Array); i++) 79 | std::cout << Array[i] << ' '; 80 | 81 | std::cout << '\n'; 82 | } 83 | 84 | int main() { 85 | // Passing a string literal 86 | static_assert(std::string_view(shift<"foobar", 3>()) == "bar"); // OK 87 | 88 | // Passing an array marked as `constexpr` 89 | constexpr int arr1[] { 1, 8, 9, 20 }; 90 | // `arr1` can only be passed by value 91 | print_array(); // 1 8 9 20 92 | 93 | // Passing a `constexpr` array of static storage duration 94 | static constexpr int arr2[] { 1, 6, 10, 23 }; 95 | // Passing `arr2` by value 96 | print_array(); // 1 6 10 23 97 | // Passing `arr2` by reference 98 | print_array>(); // 1 6 10 23 99 | 100 | // Passing a non-`const` array of static storage duration 101 | static int arr3[] { 1, 2, 4, 8 }; 102 | // `arr3` can only be passed by reference 103 | print_array>(); // 1 2 4 8 104 | 105 | // Passing a `const` array of static storage duration 106 | static const int arr4[] { 1, 2, 8, 9 }; 107 | // `arr4` can only be passed by reference 108 | print_array>(); // 1 2 8 9 109 | 110 | // Passing an `std::array` object 111 | print_array(); // 1 4 6 9 112 | } 113 | ``` 114 | 115 | You can also use it with parameter packs, obviously: [Demo](https://godbolt.org/z/Wf67xnahc) 116 | 117 | ```cpp 118 | #include 119 | #include 120 | 121 | using namespace uninttp; 122 | 123 | template 124 | void print() { 125 | ((std::cout << Values << ' '), ...) << '\n'; 126 | } 127 | 128 | int main() { 129 | print<1, 3.14159, 6.3f, "foo">(); // 1 3.14159 6.3 foo 130 | } 131 | ``` 132 | 133 | You can also enforce a type by adding a constraint: [Demo](https://godbolt.org/z/M55an6zGP) 134 | 135 | ```cpp 136 | #include 137 | #include 138 | 139 | using namespace uninttp; 140 | 141 | template 142 | // `uni_auto_simplify_t` gives you the simplified type for type-checking convenience 143 | requires std::same_as, const char*> 144 | void only_accepts_strings() {} 145 | 146 | int main() { 147 | only_accepts_strings<"foobar">(); // OK 148 | // only_accepts_strings<123>(); // Error! Constraint not satisfied! 149 | } 150 | ``` 151 | 152 | > **Note**: One can also use the above combination of constraints and `uni_auto` to achieve a sort of "*function overloading through template parameters*" mechanism: [Demo](https://godbolt.org/z/YxMnqc699) 153 | > 154 | > ```cpp 155 | > #include 156 | > #include 157 | > #include 158 | > 159 | > using namespace uninttp; 160 | > 161 | > template 162 | > requires std::same_as, const char*> 163 | > void do_something() { 164 | > std::cout << "A string was passed\n"; 165 | > } 166 | > 167 | > template 168 | > requires std::same_as, int> 169 | > void do_something() { 170 | > std::cout << "An integer was passed\n"; 171 | > } 172 | > 173 | > int main() { 174 | > do_something<"foobar">(); // A string was passed 175 | > do_something<123>(); // An integer was passed 176 | > // do_something<12.3>(); // Error! 177 | > } 178 | > ``` 179 | 180 | Example using class types: [Demo](https://godbolt.org/z/n5rqYdd95) 181 | 182 | ```cpp 183 | #include 184 | 185 | using namespace uninttp; 186 | 187 | struct X { 188 | int val = 6; 189 | }; 190 | 191 | struct Y { 192 | int val = 7; 193 | }; 194 | 195 | template 196 | constexpr auto mul() { 197 | return A.val * B.val; 198 | } 199 | 200 | int main() { 201 | static_assert(mul() == 42); // OK 202 | } 203 | ``` 204 | 205 | Example using lambdas and functors: [Demo](https://godbolt.org/z/nbPxTjKz5) 206 | 207 | ```cpp 208 | #include 209 | 210 | using namespace uninttp; 211 | 212 | template 213 | constexpr auto call() { 214 | return F(); 215 | } 216 | 217 | struct Funct { 218 | constexpr auto operator()() const { 219 | return 86; 220 | } 221 | }; 222 | 223 | int main() { 224 | static_assert(call<[] { return 69; }>() == 69); // OK 225 | static_assert(call() == 86); // OK 226 | } 227 | ``` 228 | 229 | Example using pointers to objects: [Demo](https://godbolt.org/z/E5Yo3z5qq) 230 | 231 | ```cpp 232 | #include 233 | #include 234 | 235 | using namespace uninttp; 236 | 237 | template 238 | void modify_pointer_value() { 239 | *P = 42; 240 | } 241 | 242 | template 243 | void print_pointer_value() { 244 | std::cout << *P << '\n'; 245 | } 246 | 247 | int main() { 248 | static constexpr int x = 2; 249 | static int y = 3; 250 | print_pointer_value<&x>(); // 2 251 | modify_pointer_value<&y>(); // Modifies the value of `y` indirectly through pointer access 252 | print_pointer_value<&y>(); // 42 253 | } 254 | ``` 255 | 256 | Example using function pointers: [Demo](https://godbolt.org/z/vKP7shjvc) 257 | 258 | ```cpp 259 | #include 260 | 261 | using namespace uninttp; 262 | 263 | constexpr auto some_fun() { 264 | return 42; 265 | } 266 | 267 | template 268 | constexpr auto call_fun() { 269 | return Func(); 270 | } 271 | 272 | int main() { 273 | // Passing `some_fun` by reference 274 | static_assert(call_fun>() == 42); // OK 275 | // Passing `some_fun` as a function pointer 276 | static_assert(call_fun<&some_fun>() == 42); // OK 277 | } 278 | ``` 279 | 280 | Example using pointers to members: [Demo](https://godbolt.org/z/e7oY8EGhs) 281 | 282 | ```cpp 283 | #include 284 | #include 285 | 286 | using namespace uninttp; 287 | 288 | struct some_class { 289 | int some_member_var = 0; 290 | void some_member_fun(int& p) const { 291 | p = 2; 292 | } 293 | }; 294 | 295 | template 296 | void call_member_fun(const some_class& x, int& y) { 297 | // `uni_auto_v` is used to extract the underlying value out of a `uni_auto` object 298 | (x.*uni_auto_v)(y); 299 | } 300 | 301 | template 302 | void modify_member_var(some_class& x, const int new_val) { 303 | x.*uni_auto_v = new_val; 304 | } 305 | 306 | int main() { 307 | static some_class x; 308 | int y; 309 | 310 | // Calling a member function 311 | call_member_fun<&some_class::some_member_fun>(x, y); 312 | std::cout << y << '\n'; // 2 313 | 314 | // Modifying a member variable 315 | modify_member_var<&some_class::some_member_var>(x, 3); 316 | std::cout << x.some_member_var << '\n'; // 3 317 | } 318 | ``` 319 | 320 | Example using lvalue references: [Demo](https://godbolt.org/z/z5xsxEcjx) 321 | 322 | ```cpp 323 | #include 324 | #include 325 | #include 326 | 327 | using namespace uninttp; 328 | 329 | struct X { 330 | int n = 0; 331 | 332 | friend void swap(X& a, X& b) { 333 | std::cout << "`swap(X&, X&)` was called\n"; 334 | std::ranges::swap(a.n, b.n); 335 | } 336 | 337 | friend std::ostream& operator<<(std::ostream& os, const X& x) { 338 | return os << x.n; 339 | } 340 | }; 341 | 342 | template 343 | void swap_vars() { 344 | std::ranges::swap(A, B); 345 | /* Alternatives: `uninttp::swap(A, B);`, 346 | `A.swap(B);` */ 347 | } 348 | 349 | int main() { 350 | { 351 | static X x{ 42 }, y{ 69 }; 352 | 353 | std::cout << x << ' ' << y << '\n'; // 42 69 354 | swap_vars, promote_to_ref>(); // `swap(X&, X&)` was called 355 | std::cout << x << ' ' << y << '\n'; // 69 42 356 | } 357 | 358 | ///////////////////////////////////////// 359 | 360 | { 361 | static int x = 86, y = 420; 362 | 363 | std::cout << x << ' ' << y << '\n'; // 86 420 364 | swap_vars, promote_to_ref>(); // Swaps the values of `x` and `y` 365 | std::cout << x << ' ' << y << '\n'; // 420 86 366 | } 367 | } 368 | ``` 369 | 370 | Formatting using [`std::format()`](https://en.cppreference.com/w/cpp/utility/format/format)/[`fmt::format()`](https://fmt.dev/latest/api.html#_CPPv4IDpEN3fmt6formatENSt6stringE13format_stringIDp1TEDpRR1T) is also supported: [Demo](https://godbolt.org/z/cedh9M5hP) 371 | 372 | ```cpp 373 | // All the fmtlib headers have to be included BEFORE including `uni_auto.hpp`! 374 | #include 375 | #include 376 | 377 | #include 378 | #include 379 | 380 | using namespace uninttp; 381 | 382 | template 383 | void print() { 384 | std::cout << std::format("{}\n", Value); // Using `std::format()` 385 | std::cout << fmt::format("{}\n", Value); // Using `fmt::format()` 386 | } 387 | 388 | int main() { 389 | print<"foo">(); // foo 390 | } 391 | ``` 392 | 393 | All the examples shown above have used function templates to demonstrate the capability of `uni_auto`. However, it can readily be used in any context. 394 | 395 | ## Test suite: 396 | 397 | An exhaustive test on uninttp's `uninttp::uni_auto` has been done to ensure that it consistently works for almost every non-type template argument allowed. 398 | 399 | The test suite can be found [here](https://godbolt.org/z/fvfWqjGPP). 400 | 401 | (*P.S.*: For reference, one can look up [this](https://en.cppreference.com/w/cpp/language/template_parameters) link.) 402 | 403 | ## Cheat sheet: 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 |
Description
uninttp::uni_auto_t<uni_auto Value>Gives the type of the underlying value held by Value.
uninttp::uni_auto_simplify_t<uni_auto Value>

Gives the simplified type of the underlying value held by Value.

If Value holds an array or a reference to a function, it condenses it into a pointer and returns the pointer as the type. It also removes any references from the type returned.

This feature is often useful for doing compile-time type-checking, SFINAE and/or for defining certain constraints on the types held by Value.

uninttp::uni_auto_v<uni_auto Value>Extracts the underlying value held by Value.
uninttp::uni_auto_simplify_v<uni_auto Value>

Converts the underlying value of Value into its simplest form.

If Value holds an array or a reference to a function, it converts it into a pointer and also casts away any and all references.

uninttp::promote_to_ref<auto& Value>

Pre-constructs a uni_auto object after binding an lvalue to a reference.

In simple terms, it's used to tell the compiler to pass by reference through uni_auto.

Here you can find a live example to see this feature in action.

uninttp::promote_to_cref<const auto& Value>

Pre-constructs a uni_auto object after binding an lvalue to a const reference.

439 | 440 | ## Limitations: 441 | 442 | 1) The datatype of the value held by a `uni_auto` object cannot be fetched using `decltype(X)` as is done with `auto`-template parameters. Instead, one would have to use `uni_auto_t` or `uni_auto_simplify_t` to fetch the type: [Demo](https://godbolt.org/z/bh69M4Y6n) 443 | ```cpp 444 | #include 445 | #include 446 | 447 | using namespace uninttp; 448 | 449 | template 450 | void fun() { 451 | // This doesn't work for obvious reasons: 452 | // static_assert(std::same_as); // Error 453 | 454 | // Using `uni_auto_t`: 455 | static_assert(std::is_same_v, double>); // OK 456 | 457 | /* Using `uni_auto_v` and then using `decltype()` and then removing the `const` 458 | * specifier from the type returned: */ 459 | static_assert( 460 | std::is_same_v)>, double> 461 | ); // OK 462 | 463 | // Using `uni_auto_simplify_t`: 464 | static_assert(std::is_same_v, double>); // OK 465 | 466 | /* Using `uni_auto_simplify_v` and then using `decltype()` and then removing the 467 | * `const` specifier from the type returned: */ 468 | static_assert( 469 | std::is_same_v)>, double> 470 | ); // OK 471 | } 472 | 473 | int main() { 474 | fun<1.89>(); 475 | } 476 | ``` 477 | 2) There may be some cases where the conversion operator of the `uni_auto` object doesn't get invoked. In such a scenario, one would need to explicitly notify the compiler to extract the value out of the `uni_auto` object using `uni_auto_v` or `uni_auto_simplify_v`: 478 | - During type inference: [Demo](https://godbolt.org/z/sdP6vT67c) 479 | ```cpp 480 | #include 481 | #include 482 | 483 | using namespace uninttp; 484 | 485 | template 486 | void fun() { 487 | // The conversion operator doesn't get invoked in this case: 488 | // constexpr auto a = X; 489 | 490 | // Using an explicit conversion statement: 491 | constexpr int b = X; 492 | 493 | // Using `uni_auto_v`: 494 | constexpr auto c = uni_auto_v; 495 | 496 | // Using `uni_auto_simplify_v`: 497 | constexpr auto d = uni_auto_simplify_v; 498 | 499 | // static_assert(std::is_same_v); // Error 500 | static_assert(std::is_same_v); // OK 501 | static_assert(std::is_same_v); // OK 502 | static_assert(std::is_same_v); // OK 503 | } 504 | 505 | int main() { 506 | fun<42>(); 507 | } 508 | ``` 509 | - When accessing an object's members through a reference: [Demo](https://godbolt.org/z/5dGzYfPv8) 510 | ```cpp 511 | #include 512 | #include 513 | 514 | using namespace uninttp; 515 | 516 | struct some_class { 517 | int p = 0; 518 | some_class& operator=(const int rhs) { 519 | p = rhs; 520 | return *this; 521 | } 522 | }; 523 | 524 | template 525 | void fun() { 526 | // Assignment operator works as expected 527 | X = 2; 528 | 529 | // const auto a = X.p; // This will NOT work since the C++ Standard does not allow 530 | // std::cout << a << '\n'; // overloading the dot operator (yet) 531 | // For more info, see the 'P0416R1' proposal 532 | 533 | /* Extract the value out of `X` beforehand and bind it to another reference which can 534 | * now be used to access the member `p`: */ 535 | auto& ref = uni_auto_v; 536 | const auto b = ref.p; 537 | std::cout << b << '\n'; 538 | 539 | /* Or if you want to access the member `p` directly, you would have to call `uni_auto_v` 540 | * explicitly: */ 541 | const auto c = uni_auto_v.p; 542 | std::cout << c << '\n'; 543 | } 544 | 545 | int main() { 546 | static some_class some_obj; 547 | fun>(); // 2 548 | } 549 | ``` 550 | - When the function parameter is a reference to an array: [Demo](https://godbolt.org/z/dc699a7Wc) 551 | ```cpp 552 | #include 553 | #include 554 | #include 555 | 556 | using namespace uninttp; 557 | 558 | template 559 | void print_array(T(&arr)[N]) { 560 | for (const auto& elem : arr) 561 | std::cout << elem << ' '; 562 | 563 | std::cout << '\n'; 564 | } 565 | 566 | template 567 | void fun() { 568 | // print_array(X); // Error! `X`'s conversion operator is not invoked 569 | // during the call! 570 | print_array(uni_auto_v); // OK 571 | } 572 | 573 | int main() { 574 | constexpr int arr[] { 1, 2, 3 }; 575 | fun(); // 1 2 3 576 | } 577 | ``` 578 | - When using `std::to_array()`: [Demo](https://godbolt.org/z/Y77e77rjc) 579 | ```cpp 580 | #include 581 | #include 582 | 583 | using namespace uninttp; 584 | 585 | template 586 | constexpr auto convert_to_array() { 587 | return uninttp::to_array(X); 588 | // Alternative: `return std::to_array(uni_auto_v);` 589 | } 590 | 591 | int main() { 592 | constexpr int arr[] { 1, 2, 3 }; 593 | static_assert(convert_to_array() == std::array { 1, 2, 3 }); // OK 594 | } 595 | ``` 596 | 597 | ## Playground: 598 | 599 | If you'd like to play around with `uni_auto` yourself, [here](https://godbolt.org/z/eKz8GsMrb) you go! 600 | -------------------------------------------------------------------------------- /uninttp/fmt_support.cppm: -------------------------------------------------------------------------------- 1 | /* 2 | * _ _ _ 3 | * (_) | | | | 4 | * _ _ _ __ _ _ __ | |_| |_ _ __ 5 | * | | | | '_ \| | '_ \| __| __| '_ \ 6 | * | |_| | | | | | | | | |_| |_| |_) | 7 | * \__,_|_| |_|_|_| |_|\__|\__| .__/ 8 | * | | 9 | * |_| 10 | * 11 | * uninttp (Universal Non-Type Template Parameters) 12 | * 13 | * Version: v4.2.9 14 | * 15 | * Copyright (c) 2021-... reacfen 16 | * 17 | * Permission is hereby granted, free of charge, to any person obtaining a copy 18 | * of this software and associated documentation files (the "Software"), to deal 19 | * in the Software without restriction, including without limitation the rights 20 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | * copies of the Software, and to permit persons to whom the Software is 22 | * furnished to do so, subject to the following conditions: 23 | * 24 | * The above copyright notice and this permission notice shall be included in all 25 | * copies or substantial portions of the Software. 26 | * 27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 | * SOFTWARE. 34 | */ 35 | 36 | export module uninttp.fmt_support; 37 | 38 | import fmt; 39 | import uninttp.uni_auto; 40 | import ; 41 | 42 | export template 43 | struct fmt::formatter> : formatter> { 44 | template 45 | auto format(const uninttp::uni_auto& a, FormatContext& c) const { 46 | return formatter>::format(a.operator typename uninttp::uni_auto::type(), c); 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /uninttp/uni_auto.cppm: -------------------------------------------------------------------------------- 1 | /* 2 | * _ _ _ 3 | * (_) | | | | 4 | * _ _ _ __ _ _ __ | |_| |_ _ __ 5 | * | | | | '_ \| | '_ \| __| __| '_ \ 6 | * | |_| | | | | | | | | |_| |_| |_) | 7 | * \__,_|_| |_|_|_| |_|\__|\__| .__/ 8 | * | | 9 | * |_| 10 | * 11 | * uninttp (Universal Non-Type Template Parameters) 12 | * 13 | * Version: v4.2.9 14 | * 15 | * Copyright (c) 2021-... reacfen 16 | * 17 | * Permission is hereby granted, free of charge, to any person obtaining a copy 18 | * of this software and associated documentation files (the "Software"), to deal 19 | * in the Software without restriction, including without limitation the rights 20 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | * copies of the Software, and to permit persons to whom the Software is 22 | * furnished to do so, subject to the following conditions: 23 | * 24 | * The above copyright notice and this permission notice shall be included in all 25 | * copies or substantial portions of the Software. 26 | * 27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 | * SOFTWARE. 34 | */ 35 | 36 | export module uninttp.uni_auto; 37 | 38 | import ; 39 | import ; 40 | import ; 41 | import ; 42 | import ; 43 | import ; 44 | 45 | export namespace uninttp { 46 | /** 47 | * @brief The `uni_auto` class type implementation 48 | */ 49 | template 50 | struct uni_auto; 51 | } 52 | 53 | template 54 | struct is_uni_auto final : std::false_type {}; 55 | 56 | template 57 | struct is_uni_auto> final : std::true_type {}; 58 | 59 | export namespace uninttp { 60 | template 61 | struct uni_auto final { 62 | using type = const T(&)[N]; 63 | std::remove_reference_t value; 64 | 65 | template 66 | constexpr uni_auto(type v, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{ v[Indices]... } {} 67 | 68 | constexpr uni_auto(type v) noexcept(noexcept(uni_auto{ v, std::make_index_sequence() })) : uni_auto{ v, std::make_index_sequence() } {} 69 | 70 | constexpr operator type() const noexcept { 71 | return value; 72 | } 73 | 74 | static constexpr auto size() noexcept { 75 | return N; 76 | } 77 | 78 | constexpr auto data() const noexcept { 79 | return std::data(value); 80 | } 81 | 82 | constexpr auto empty() const noexcept { 83 | return std::empty(value); 84 | } 85 | 86 | constexpr auto begin() const noexcept { 87 | return std::begin(value); 88 | } 89 | 90 | constexpr auto end() const noexcept { 91 | return std::end(value); 92 | } 93 | 94 | constexpr auto cbegin() const noexcept { 95 | return std::cbegin(value); 96 | } 97 | 98 | constexpr auto cend() const noexcept { 99 | return std::cend(value); 100 | } 101 | 102 | constexpr auto rbegin() const noexcept { 103 | return std::rbegin(value); 104 | } 105 | 106 | constexpr auto rend() const noexcept { 107 | return std::rend(value); 108 | } 109 | 110 | constexpr auto crbegin() const noexcept { 111 | return std::crbegin(value); 112 | } 113 | 114 | constexpr auto crend() const noexcept { 115 | return std::crend(value); 116 | } 117 | }; 118 | 119 | template 120 | struct uni_auto final { 121 | using type = T&; 122 | type value; 123 | 124 | constexpr uni_auto(type v) noexcept : value{ v } {} 125 | 126 | constexpr operator type() const noexcept { 127 | return value; 128 | } 129 | 130 | static constexpr auto size() noexcept 131 | requires std::is_array_v { 132 | return std::extent_v; 133 | } 134 | 135 | constexpr auto size() const noexcept 136 | requires (!std::is_array_v) { 137 | return std::size(value); 138 | } 139 | 140 | constexpr auto swap(const uni_auto& other) const noexcept { 141 | using std::swap; 142 | swap(value, other.value); 143 | } 144 | 145 | constexpr auto data() const noexcept { 146 | return std::data(value); 147 | } 148 | 149 | constexpr auto empty() const noexcept { 150 | return std::empty(value); 151 | } 152 | 153 | constexpr auto begin() const noexcept { 154 | return std::begin(value); 155 | } 156 | 157 | constexpr auto end() const noexcept { 158 | return std::end(value); 159 | } 160 | 161 | constexpr auto cbegin() const noexcept { 162 | return std::cbegin(value); 163 | } 164 | 165 | constexpr auto cend() const noexcept { 166 | return std::cend(value); 167 | } 168 | 169 | constexpr auto rbegin() const noexcept { 170 | return std::rbegin(value); 171 | } 172 | 173 | constexpr auto rend() const noexcept { 174 | return std::rend(value); 175 | } 176 | 177 | constexpr auto crbegin() const noexcept { 178 | return std::crbegin(value); 179 | } 180 | 181 | constexpr auto crend() const noexcept { 182 | return std::crend(value); 183 | } 184 | 185 | template 186 | constexpr decltype(auto) operator=(U&& b) const noexcept(noexcept(value = std::forward(b))) 187 | requires requires { value = std::forward(b); } { 188 | return value = std::forward(b); 189 | } 190 | 191 | template 192 | constexpr decltype(auto) operator[](Args&&... args) const noexcept(noexcept(value.operator[](std::forward(args)...))) 193 | requires requires { value.operator[](std::forward(args)...); } { 194 | return value.operator[](std::forward(args)...); 195 | } 196 | 197 | constexpr decltype(auto) operator->() const noexcept(noexcept(value.operator->())) 198 | requires requires { value.operator->(); } { 199 | return value.operator->(); 200 | } 201 | 202 | template 203 | constexpr decltype(auto) operator()(Args&&... args) const noexcept(noexcept(value(std::forward(args)...))) 204 | requires requires { value(std::forward(args)...); } { 205 | return value(std::forward(args)...); 206 | } 207 | }; 208 | 209 | template 210 | requires (!std::is_class_v) 211 | struct uni_auto final { 212 | using type = T; 213 | type value; 214 | 215 | constexpr uni_auto(type v) noexcept : value{ v } {} 216 | 217 | constexpr operator type() const noexcept { 218 | return value; 219 | } 220 | 221 | constexpr decltype(auto) operator->() const noexcept 222 | requires std::is_pointer_v { 223 | return value; 224 | } 225 | }; 226 | 227 | template 228 | requires std::is_class_v 229 | struct uni_auto final : T { 230 | using type = T; 231 | 232 | constexpr uni_auto(const type& v) noexcept(std::is_nothrow_constructible_v) : type{ v } {} 233 | 234 | constexpr operator const type&() const noexcept { 235 | return *this; 236 | } 237 | }; 238 | 239 | /* Deals with `constexpr`-marked built-in arrays and fixed-size C-Strings/string literals */ 240 | template 241 | uni_auto(const T(&)[N]) -> uni_auto; 242 | 243 | /* Deals with built-in arrays, function pointers, integral and enumeration types, pointers to objects, pointers to member functions and objects, nullptr, etc. (Basically everything else) */ 244 | template 245 | uni_auto(const T&) -> uni_auto; 246 | 247 | template 248 | constexpr decltype(auto) operator++(const uni_auto& a) noexcept(noexcept(++a.operator typename uni_auto::type())) 249 | requires requires { ++a.operator typename uni_auto::type(); } { 250 | return ++a.operator typename uni_auto::type(); 251 | } 252 | 253 | template 254 | constexpr decltype(auto) operator--(const uni_auto& a) noexcept(noexcept(--a.operator typename uni_auto::type())) 255 | requires requires { --a.operator typename uni_auto::type(); } { 256 | return --a.operator typename uni_auto::type(); 257 | } 258 | 259 | template 260 | constexpr decltype(auto) operator++(const uni_auto& a, int) noexcept(noexcept(a.operator typename uni_auto::type()++)) 261 | requires requires { a.operator typename uni_auto::type()++; } { 262 | return a.operator typename uni_auto::type()++; 263 | } 264 | 265 | template 266 | constexpr decltype(auto) operator--(const uni_auto& a, int) noexcept(noexcept(a.operator typename uni_auto::type()--)) 267 | requires requires { a.operator typename uni_auto::type()--; } { 268 | return a.operator typename uni_auto::type()--; 269 | } 270 | 271 | template 272 | constexpr decltype(auto) operator+(const uni_auto& a) noexcept(noexcept(+a.operator typename uni_auto::type())) 273 | requires requires { +a.operator typename uni_auto::type(); } { 274 | return +a.operator typename uni_auto::type(); 275 | } 276 | 277 | template 278 | constexpr decltype(auto) operator-(const uni_auto& a) noexcept(noexcept(-a.operator typename uni_auto::type())) 279 | requires requires { -a.operator typename uni_auto::type(); } { 280 | return -a.operator typename uni_auto::type(); 281 | } 282 | 283 | template 284 | constexpr decltype(auto) operator~(const uni_auto& a) noexcept(noexcept(~a.operator typename uni_auto::type())) 285 | requires requires { ~a.operator typename uni_auto::type(); } { 286 | return ~a.operator typename uni_auto::type(); 287 | } 288 | 289 | template 290 | constexpr decltype(auto) operator!(const uni_auto& a) noexcept(noexcept(!a.operator typename uni_auto::type())) 291 | requires requires { !a.operator typename uni_auto::type(); } { 292 | return !a.operator typename uni_auto::type(); 293 | } 294 | 295 | template 296 | constexpr decltype(auto) operator*(const uni_auto& a) noexcept(noexcept(*a.operator typename uni_auto::type())) 297 | requires requires { *a.operator typename uni_auto::type(); } { 298 | return *a.operator typename uni_auto::type(); 299 | } 300 | 301 | template 302 | constexpr decltype(auto) operator&(const uni_auto& a) noexcept(noexcept(&a.operator typename uni_auto::type())) { 303 | return &a.operator typename uni_auto::type(); 304 | } 305 | 306 | template 307 | requires std::is_class_v 308 | constexpr decltype(auto) operator&(const uni_auto& a) noexcept { 309 | return &a.operator const typename uni_auto::type&(); 310 | } 311 | 312 | template 313 | requires (!std::is_class_v) 314 | constexpr decltype(auto) operator+=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() += b.operator typename uni_auto::type())) 315 | requires requires { a.operator typename uni_auto::type() += b.operator typename uni_auto::type(); } { 316 | return a.operator typename uni_auto::type() += b.operator typename uni_auto::type(); 317 | } 318 | 319 | template 320 | requires (!std::is_class_v) 321 | constexpr decltype(auto) operator-=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() -= b.operator typename uni_auto::type())) 322 | requires requires { a.operator typename uni_auto::type() -= b.operator typename uni_auto::type(); } { 323 | return a.operator typename uni_auto::type() -= b.operator typename uni_auto::type(); 324 | } 325 | 326 | template 327 | requires (!std::is_class_v) 328 | constexpr decltype(auto) operator*=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() *= b.operator typename uni_auto::type())) 329 | requires requires { a.operator typename uni_auto::type() *= b.operator typename uni_auto::type(); } { 330 | return a.operator typename uni_auto::type() *= b.operator typename uni_auto::type(); 331 | } 332 | 333 | template 334 | requires (!std::is_class_v) 335 | constexpr decltype(auto) operator/=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() /= b.operator typename uni_auto::type())) 336 | requires requires { a.operator typename uni_auto::type() /= b.operator typename uni_auto::type(); } { 337 | return a.operator typename uni_auto::type() /= b.operator typename uni_auto::type(); 338 | } 339 | 340 | template 341 | requires (!std::is_class_v) 342 | constexpr decltype(auto) operator%=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() %= b.operator typename uni_auto::type())) 343 | requires requires { a.operator typename uni_auto::type() %= b.operator typename uni_auto::type(); } { 344 | return a.operator typename uni_auto::type() %= b.operator typename uni_auto::type(); 345 | } 346 | 347 | template 348 | requires (!std::is_class_v) 349 | constexpr decltype(auto) operator&=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() &= b.operator typename uni_auto::type())) 350 | requires requires { a.operator typename uni_auto::type() &= b.operator typename uni_auto::type(); } { 351 | return a.operator typename uni_auto::type() &= b.operator typename uni_auto::type(); 352 | } 353 | 354 | template 355 | requires (!std::is_class_v) 356 | constexpr decltype(auto) operator|=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() |= b.operator typename uni_auto::type())) 357 | requires requires { a.operator typename uni_auto::type() |= b.operator typename uni_auto::type(); } { 358 | return a.operator typename uni_auto::type() |= b.operator typename uni_auto::type(); 359 | } 360 | 361 | template 362 | requires (!std::is_class_v) 363 | constexpr decltype(auto) operator^=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() ^= b.operator typename uni_auto::type())) 364 | requires requires { a.operator typename uni_auto::type() ^= b.operator typename uni_auto::type(); } { 365 | return a.operator typename uni_auto::type() ^= b.operator typename uni_auto::type(); 366 | } 367 | 368 | template 369 | requires (!std::is_class_v) 370 | constexpr decltype(auto) operator<<=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() <<= b.operator typename uni_auto::type())) 371 | requires requires { a.operator typename uni_auto::type() <<= b.operator typename uni_auto::type(); } { 372 | return a.operator typename uni_auto::type() <<= b.operator typename uni_auto::type(); 373 | } 374 | 375 | template 376 | requires (!std::is_class_v) 377 | constexpr decltype(auto) operator>>=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() >>= b.operator typename uni_auto::type())) 378 | requires requires { a.operator typename uni_auto::type() >>= b.operator typename uni_auto::type(); } { 379 | return a.operator typename uni_auto::type() >>= b.operator typename uni_auto::type(); 380 | } 381 | 382 | template 383 | requires (!std::is_class_v) 384 | constexpr decltype(auto) operator+(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() + b.operator typename uni_auto::type())) 385 | requires requires { a.operator typename uni_auto::type() + b.operator typename uni_auto::type(); } { 386 | return a.operator typename uni_auto::type() + b.operator typename uni_auto::type(); 387 | } 388 | 389 | template 390 | requires (!std::is_class_v) 391 | constexpr decltype(auto) operator-(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() - b.operator typename uni_auto::type())) 392 | requires requires { a.operator typename uni_auto::type() - b.operator typename uni_auto::type(); } { 393 | return a.operator typename uni_auto::type() - b.operator typename uni_auto::type(); 394 | } 395 | 396 | template 397 | requires (!std::is_class_v) 398 | constexpr decltype(auto) operator*(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() * b.operator typename uni_auto::type())) 399 | requires requires { a.operator typename uni_auto::type() * b.operator typename uni_auto::type(); } { 400 | return a.operator typename uni_auto::type() * b.operator typename uni_auto::type(); 401 | } 402 | 403 | template 404 | requires (!std::is_class_v) 405 | constexpr decltype(auto) operator/(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() / b.operator typename uni_auto::type())) 406 | requires requires { a.operator typename uni_auto::type() / b.operator typename uni_auto::type(); } { 407 | return a.operator typename uni_auto::type() / b.operator typename uni_auto::type(); 408 | } 409 | 410 | template 411 | requires (!std::is_class_v) 412 | constexpr decltype(auto) operator%(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() % b.operator typename uni_auto::type())) 413 | requires requires { a.operator typename uni_auto::type() % b.operator typename uni_auto::type(); } { 414 | return a.operator typename uni_auto::type() % b.operator typename uni_auto::type(); 415 | } 416 | 417 | template 418 | requires (!std::is_class_v) 419 | constexpr decltype(auto) operator&(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() & b.operator typename uni_auto::type())) 420 | requires requires { a.operator typename uni_auto::type() & b.operator typename uni_auto::type(); } { 421 | return a.operator typename uni_auto::type() & b.operator typename uni_auto::type(); 422 | } 423 | 424 | template 425 | requires (!std::is_class_v) 426 | constexpr decltype(auto) operator|(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() | b.operator typename uni_auto::type())) 427 | requires requires { a.operator typename uni_auto::type() | b.operator typename uni_auto::type(); } { 428 | return a.operator typename uni_auto::type() | b.operator typename uni_auto::type(); 429 | } 430 | 431 | template 432 | requires (!std::is_class_v) 433 | constexpr decltype(auto) operator^(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() ^ b.operator typename uni_auto::type())) 434 | requires requires { a.operator typename uni_auto::type() ^ b.operator typename uni_auto::type(); } { 435 | return a.operator typename uni_auto::type() ^ b.operator typename uni_auto::type(); 436 | } 437 | 438 | template 439 | requires (!std::is_class_v) 440 | constexpr decltype(auto) operator<<(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() << b.operator typename uni_auto::type())) 441 | requires requires { a.operator typename uni_auto::type() << b.operator typename uni_auto::type(); } { 442 | return a.operator typename uni_auto::type() << b.operator typename uni_auto::type(); 443 | } 444 | 445 | template 446 | requires (!std::is_class_v) 447 | constexpr decltype(auto) operator>>(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() >> b.operator typename uni_auto::type())) 448 | requires requires { a.operator typename uni_auto::type() >> b.operator typename uni_auto::type(); } { 449 | return a.operator typename uni_auto::type() >> b.operator typename uni_auto::type(); 450 | } 451 | 452 | template 453 | requires (!std::is_class_v) 454 | constexpr decltype(auto) operator&&(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() && b.operator typename uni_auto::type())) 455 | requires requires { a.operator typename uni_auto::type() && b.operator typename uni_auto::type(); } { 456 | return a.operator typename uni_auto::type() && b.operator typename uni_auto::type(); 457 | } 458 | 459 | template 460 | requires (!std::is_class_v) 461 | constexpr decltype(auto) operator||(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() || b.operator typename uni_auto::type())) 462 | requires requires { a.operator typename uni_auto::type() || b.operator typename uni_auto::type(); } { 463 | return a.operator typename uni_auto::type() || b.operator typename uni_auto::type(); 464 | } 465 | 466 | template 467 | requires (!std::is_class_v) 468 | constexpr decltype(auto) operator==(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() == b.operator typename uni_auto::type())) 469 | requires requires { a.operator typename uni_auto::type() == b.operator typename uni_auto::type(); } { 470 | return a.operator typename uni_auto::type() == b.operator typename uni_auto::type(); 471 | } 472 | 473 | template 474 | requires (!std::is_class_v) 475 | constexpr decltype(auto) operator<=>(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() <=> b.operator typename uni_auto::type())) 476 | requires requires { a.operator typename uni_auto::type() <=> b.operator typename uni_auto::type(); } { 477 | return a.operator typename uni_auto::type() <=> b.operator typename uni_auto::type(); 478 | } 479 | 480 | template 481 | requires (!std::is_class_v) 482 | constexpr decltype(auto) operator->*(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() ->* b.operator typename uni_auto::type())) 483 | requires requires { a.operator typename uni_auto::type() ->* b.operator typename uni_auto::type(); } { 484 | return a.operator typename uni_auto::type() ->* b.operator typename uni_auto::type(); 485 | } 486 | 487 | template 488 | requires (!std::is_class_v) 489 | constexpr decltype(auto) operator,(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type(), b.operator typename uni_auto::type())) 490 | requires requires { a.operator typename uni_auto::type(), b.operator typename uni_auto::type(); } { 491 | return a.operator typename uni_auto::type(), b.operator typename uni_auto::type(); 492 | } 493 | 494 | template 495 | requires (!is_uni_auto>::value) 496 | constexpr decltype(auto) operator+=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) += b.operator typename uni_auto::type())) 497 | requires requires { std::forward(a) += b.operator typename uni_auto::type(); } { 498 | return std::forward(a) += b.operator typename uni_auto::type(); 499 | } 500 | 501 | template 502 | requires (!is_uni_auto>::value) 503 | constexpr decltype(auto) operator-=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) -= b.operator typename uni_auto::type())) 504 | requires requires { std::forward(a) -= b.operator typename uni_auto::type(); } { 505 | return std::forward(a) -= b.operator typename uni_auto::type(); 506 | } 507 | 508 | template 509 | requires (!is_uni_auto>::value) 510 | constexpr decltype(auto) operator*=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) *= b.operator typename uni_auto::type())) 511 | requires requires { std::forward(a) *= b.operator typename uni_auto::type(); } { 512 | return std::forward(a) *= b.operator typename uni_auto::type(); 513 | } 514 | 515 | template 516 | requires (!is_uni_auto>::value) 517 | constexpr decltype(auto) operator/=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) /= b.operator typename uni_auto::type())) 518 | requires requires { std::forward(a) /= b.operator typename uni_auto::type(); } { 519 | return std::forward(a) /= b.operator typename uni_auto::type(); 520 | } 521 | 522 | template 523 | requires (!is_uni_auto>::value) 524 | constexpr decltype(auto) operator%=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) %= b.operator typename uni_auto::type())) 525 | requires requires { std::forward(a) %= b.operator typename uni_auto::type(); } { 526 | return std::forward(a) %= b.operator typename uni_auto::type(); 527 | } 528 | 529 | template 530 | requires (!is_uni_auto>::value) 531 | constexpr decltype(auto) operator&=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) &= b.operator typename uni_auto::type())) 532 | requires requires { std::forward(a) &= b.operator typename uni_auto::type(); } { 533 | return std::forward(a) &= b.operator typename uni_auto::type(); 534 | } 535 | 536 | template 537 | requires (!is_uni_auto>::value) 538 | constexpr decltype(auto) operator|=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) |= b.operator typename uni_auto::type())) 539 | requires requires { std::forward(a) |= b.operator typename uni_auto::type(); } { 540 | return std::forward(a) |= b.operator typename uni_auto::type(); 541 | } 542 | 543 | template 544 | requires (!is_uni_auto>::value) 545 | constexpr decltype(auto) operator^=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) ^= b.operator typename uni_auto::type())) 546 | requires requires { std::forward(a) ^= b.operator typename uni_auto::type(); } { 547 | return std::forward(a) ^= b.operator typename uni_auto::type(); 548 | } 549 | 550 | template 551 | requires (!is_uni_auto>::value) 552 | constexpr decltype(auto) operator<<=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) <<= b.operator typename uni_auto::type())) 553 | requires requires { std::forward(a) <<= b.operator typename uni_auto::type(); } { 554 | return std::forward(a) <<= b.operator typename uni_auto::type(); 555 | } 556 | 557 | template 558 | requires (!is_uni_auto>::value) 559 | constexpr decltype(auto) operator>>=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) >>= b.operator typename uni_auto::type())) 560 | requires requires { std::forward(a) >>= b.operator typename uni_auto::type(); } { 561 | return std::forward(a) >>= b.operator typename uni_auto::type(); 562 | } 563 | 564 | template 565 | requires (!is_uni_auto>::value) 566 | constexpr decltype(auto) operator+(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) + b.operator typename uni_auto::type())) 567 | requires requires { std::forward(a) + b.operator typename uni_auto::type(); } { 568 | return std::forward(a) + b.operator typename uni_auto::type(); 569 | } 570 | 571 | template 572 | requires (!is_uni_auto>::value) 573 | constexpr decltype(auto) operator-(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) - b.operator typename uni_auto::type())) 574 | requires requires { std::forward(a) - b.operator typename uni_auto::type(); } { 575 | return std::forward(a) - b.operator typename uni_auto::type(); 576 | } 577 | 578 | template 579 | requires (!is_uni_auto>::value) 580 | constexpr decltype(auto) operator*(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) * b.operator typename uni_auto::type())) 581 | requires requires { std::forward(a) * b.operator typename uni_auto::type(); } { 582 | return std::forward(a) * b.operator typename uni_auto::type(); 583 | } 584 | 585 | template 586 | requires (!is_uni_auto>::value) 587 | constexpr decltype(auto) operator/(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) / b.operator typename uni_auto::type())) 588 | requires requires { std::forward(a) / b.operator typename uni_auto::type(); } { 589 | return std::forward(a) / b.operator typename uni_auto::type(); 590 | } 591 | 592 | template 593 | requires (!is_uni_auto>::value) 594 | constexpr decltype(auto) operator%(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) % b.operator typename uni_auto::type())) 595 | requires requires { std::forward(a) % b.operator typename uni_auto::type(); } { 596 | return std::forward(a) % b.operator typename uni_auto::type(); 597 | } 598 | 599 | template 600 | requires (!is_uni_auto>::value) 601 | constexpr decltype(auto) operator&(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) & b.operator typename uni_auto::type())) 602 | requires requires { std::forward(a) & b.operator typename uni_auto::type(); } { 603 | return std::forward(a) & b.operator typename uni_auto::type(); 604 | } 605 | 606 | template 607 | requires (!is_uni_auto>::value) 608 | constexpr decltype(auto) operator|(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) | b.operator typename uni_auto::type())) 609 | requires requires { std::forward(a) | b.operator typename uni_auto::type(); } { 610 | return std::forward(a) | b.operator typename uni_auto::type(); 611 | } 612 | 613 | template 614 | requires (!is_uni_auto>::value) 615 | constexpr decltype(auto) operator^(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) ^ b.operator typename uni_auto::type())) 616 | requires requires { std::forward(a) ^ b.operator typename uni_auto::type(); } { 617 | return std::forward(a) ^ b.operator typename uni_auto::type(); 618 | } 619 | 620 | template 621 | requires (!is_uni_auto>::value) 622 | constexpr decltype(auto) operator<<(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) << b.operator typename uni_auto::type())) 623 | requires requires { std::forward(a) << b.operator typename uni_auto::type(); } { 624 | return std::forward(a) << b.operator typename uni_auto::type(); 625 | } 626 | 627 | template 628 | requires (!is_uni_auto>::value) 629 | constexpr decltype(auto) operator>>(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) >> b.operator typename uni_auto::type())) 630 | requires requires { std::forward(a) >> b.operator typename uni_auto::type(); } { 631 | return std::forward(a) >> b.operator typename uni_auto::type(); 632 | } 633 | 634 | template 635 | requires (!is_uni_auto>::value) 636 | constexpr decltype(auto) operator&&(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) && b.operator typename uni_auto::type())) 637 | requires requires { std::forward(a) && b.operator typename uni_auto::type(); } { 638 | return std::forward(a) && b.operator typename uni_auto::type(); 639 | } 640 | 641 | template 642 | requires (!is_uni_auto>::value) 643 | constexpr decltype(auto) operator||(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) || b.operator typename uni_auto::type())) 644 | requires requires { std::forward(a) || b.operator typename uni_auto::type(); } { 645 | return std::forward(a) || b.operator typename uni_auto::type(); 646 | } 647 | 648 | template 649 | requires (!is_uni_auto>::value) 650 | constexpr decltype(auto) operator==(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) == b.operator typename uni_auto::type())) 651 | requires requires { std::forward(a) == b.operator typename uni_auto::type(); } { 652 | return std::forward(a) == b.operator typename uni_auto::type(); 653 | } 654 | 655 | template 656 | requires (!is_uni_auto>::value) 657 | constexpr decltype(auto) operator<=>(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) <=> b.operator typename uni_auto::type())) 658 | requires requires { std::forward(a) <=> b.operator typename uni_auto::type(); } { 659 | return std::forward(a) <=> b.operator typename uni_auto::type(); 660 | } 661 | 662 | template 663 | requires (!is_uni_auto>::value) 664 | constexpr decltype(auto) operator->*(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) ->* b.operator typename uni_auto::type())) 665 | requires requires { std::forward(a) ->* b.operator typename uni_auto::type(); } { 666 | return std::forward(a) ->* b.operator typename uni_auto::type(); 667 | } 668 | 669 | template 670 | requires (!is_uni_auto>::value) 671 | constexpr decltype(auto) operator,(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a), b.operator typename uni_auto::type())) 672 | requires requires { std::forward(a), b.operator typename uni_auto::type(); } { 673 | return std::forward(a), b.operator typename uni_auto::type(); 674 | } 675 | 676 | template 677 | requires (!is_uni_auto>::value) 678 | constexpr decltype(auto) operator+=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() += std::forward(b))) 679 | requires requires { a.operator typename uni_auto::type() += std::forward(b); } { 680 | return a.operator typename uni_auto::type() += std::forward(b); 681 | } 682 | 683 | template 684 | requires (!is_uni_auto>::value) 685 | constexpr decltype(auto) operator-=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() -= std::forward(b))) 686 | requires requires { a.operator typename uni_auto::type() -= std::forward(b); } { 687 | return a.operator typename uni_auto::type() -= std::forward(b); 688 | } 689 | 690 | template 691 | requires (!is_uni_auto>::value) 692 | constexpr decltype(auto) operator*=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() *= std::forward(b))) 693 | requires requires { a.operator typename uni_auto::type() *= std::forward(b); } { 694 | return a.operator typename uni_auto::type() *= std::forward(b); 695 | } 696 | 697 | template 698 | requires (!is_uni_auto>::value) 699 | constexpr decltype(auto) operator/=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() /= std::forward(b))) 700 | requires requires { a.operator typename uni_auto::type() /= std::forward(b); } { 701 | return a.operator typename uni_auto::type() /= std::forward(b); 702 | } 703 | 704 | template 705 | requires (!is_uni_auto>::value) 706 | constexpr decltype(auto) operator%=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() %= std::forward(b))) 707 | requires requires { a.operator typename uni_auto::type() %= std::forward(b); } { 708 | return a.operator typename uni_auto::type() %= std::forward(b); 709 | } 710 | 711 | template 712 | requires (!is_uni_auto>::value) 713 | constexpr decltype(auto) operator&=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() &= std::forward(b))) 714 | requires requires { a.operator typename uni_auto::type() &= std::forward(b); } { 715 | return a.operator typename uni_auto::type() &= std::forward(b); 716 | } 717 | 718 | template 719 | requires (!is_uni_auto>::value) 720 | constexpr decltype(auto) operator|=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() |= std::forward(b))) 721 | requires requires { a.operator typename uni_auto::type() |= std::forward(b); } { 722 | return a.operator typename uni_auto::type() |= std::forward(b); 723 | } 724 | 725 | template 726 | requires (!is_uni_auto>::value) 727 | constexpr decltype(auto) operator^=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() ^= std::forward(b))) 728 | requires requires { a.operator typename uni_auto::type() ^= std::forward(b); } { 729 | return a.operator typename uni_auto::type() ^= std::forward(b); 730 | } 731 | 732 | template 733 | requires (!is_uni_auto>::value) 734 | constexpr decltype(auto) operator<<=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() <<= std::forward(b))) 735 | requires requires { a.operator typename uni_auto::type() <<= std::forward(b); } { 736 | return a.operator typename uni_auto::type() <<= std::forward(b); 737 | } 738 | 739 | template 740 | requires (!is_uni_auto>::value) 741 | constexpr decltype(auto) operator>>=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() >>= std::forward(b))) 742 | requires requires { a.operator typename uni_auto::type() >>= std::forward(b); } { 743 | return a.operator typename uni_auto::type() >>= std::forward(b); 744 | } 745 | 746 | template 747 | requires (!is_uni_auto>::value) 748 | constexpr decltype(auto) operator+(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() + std::forward(b))) 749 | requires requires { a.operator typename uni_auto::type() + std::forward(b); } { 750 | return a.operator typename uni_auto::type() + std::forward(b); 751 | } 752 | 753 | template 754 | requires (!is_uni_auto>::value) 755 | constexpr decltype(auto) operator-(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() - std::forward(b))) 756 | requires requires { a.operator typename uni_auto::type() - std::forward(b); } { 757 | return a.operator typename uni_auto::type() - std::forward(b); 758 | } 759 | 760 | template 761 | requires (!is_uni_auto>::value) 762 | constexpr decltype(auto) operator*(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() * std::forward(b))) 763 | requires requires { a.operator typename uni_auto::type() * std::forward(b); } { 764 | return a.operator typename uni_auto::type() * std::forward(b); 765 | } 766 | 767 | template 768 | requires (!is_uni_auto>::value) 769 | constexpr decltype(auto) operator/(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() / std::forward(b))) 770 | requires requires { a.operator typename uni_auto::type() / std::forward(b); } { 771 | return a.operator typename uni_auto::type() / std::forward(b); 772 | } 773 | 774 | template 775 | requires (!is_uni_auto>::value) 776 | constexpr decltype(auto) operator%(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() % std::forward(b))) 777 | requires requires { a.operator typename uni_auto::type() % std::forward(b); } { 778 | return a.operator typename uni_auto::type() % std::forward(b); 779 | } 780 | 781 | template 782 | requires (!is_uni_auto>::value) 783 | constexpr decltype(auto) operator&(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() & std::forward(b))) 784 | requires requires { a.operator typename uni_auto::type() & std::forward(b); } { 785 | return a.operator typename uni_auto::type() & std::forward(b); 786 | } 787 | 788 | template 789 | requires (!is_uni_auto>::value) 790 | constexpr decltype(auto) operator|(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() | std::forward(b))) 791 | requires requires { a.operator typename uni_auto::type() | std::forward(b); } { 792 | return a.operator typename uni_auto::type() | std::forward(b); 793 | } 794 | 795 | template 796 | requires (!is_uni_auto>::value) 797 | constexpr decltype(auto) operator^(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() ^ std::forward(b))) 798 | requires requires { a.operator typename uni_auto::type() ^ std::forward(b); } { 799 | return a.operator typename uni_auto::type() ^ std::forward(b); 800 | } 801 | 802 | template 803 | requires (!is_uni_auto>::value) 804 | constexpr decltype(auto) operator<<(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() << std::forward(b))) 805 | requires requires { a.operator typename uni_auto::type() << std::forward(b); } { 806 | return a.operator typename uni_auto::type() << std::forward(b); 807 | } 808 | 809 | template 810 | requires (!is_uni_auto>::value) 811 | constexpr decltype(auto) operator>>(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() >> std::forward(b))) 812 | requires requires { a.operator typename uni_auto::type() >> std::forward(b); } { 813 | return a.operator typename uni_auto::type() >> std::forward(b); 814 | } 815 | 816 | template 817 | requires (!is_uni_auto>::value) 818 | constexpr decltype(auto) operator&&(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() && std::forward(b))) 819 | requires requires { a.operator typename uni_auto::type() && std::forward(b); } { 820 | return a.operator typename uni_auto::type() && std::forward(b); 821 | } 822 | 823 | template 824 | requires (!is_uni_auto>::value) 825 | constexpr decltype(auto) operator||(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() || std::forward(b))) 826 | requires requires { a.operator typename uni_auto::type() || std::forward(b); } { 827 | return a.operator typename uni_auto::type() || std::forward(b); 828 | } 829 | 830 | template 831 | requires (!is_uni_auto>::value) 832 | constexpr decltype(auto) operator==(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() == std::forward(b))) 833 | requires requires { a.operator typename uni_auto::type() == std::forward(b); } { 834 | return a.operator typename uni_auto::type() == std::forward(b); 835 | } 836 | 837 | template 838 | requires (!is_uni_auto>::value) 839 | constexpr decltype(auto) operator<=>(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() <=> std::forward(b))) 840 | requires requires { a.operator typename uni_auto::type() <=> std::forward(b); } { 841 | return a.operator typename uni_auto::type() <=> std::forward(b); 842 | } 843 | 844 | template 845 | requires (!is_uni_auto>::value) 846 | constexpr decltype(auto) operator->*(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() ->* std::forward(b))) 847 | requires requires { a.operator typename uni_auto::type() ->* std::forward(b); } { 848 | return a.operator typename uni_auto::type() ->* std::forward(b); 849 | } 850 | 851 | template 852 | requires (!is_uni_auto>::value) 853 | constexpr decltype(auto) operator,(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type(), std::forward(b))) 854 | requires requires { a.operator typename uni_auto::type(), std::forward(b); } { 855 | return a.operator typename uni_auto::type(), std::forward(b); 856 | } 857 | 858 | /** 859 | * @brief Fetches the type of the underlying value held by a `uni_auto` object. 860 | * @tparam Value The `uni_auto` object 861 | */ 862 | template 863 | using uni_auto_t = typename decltype(Value)::type; 864 | 865 | /** 866 | * @brief Fetches the underlying value held by a `uni_auto` object. 867 | * @tparam Value The `uni_auto` object 868 | */ 869 | template 870 | constexpr uni_auto_t uni_auto_v = Value; 871 | 872 | /** 873 | * @brief Similar to `uni_auto_t` but condenses array types and function types to pointers and removes any references from the type returned. 874 | * @tparam Value The `uni_auto` object 875 | */ 876 | template 877 | using uni_auto_simplify_t = std::decay_t; 878 | 879 | /** 880 | * @brief Similar to `uni_auto_v` but converts array types and function types to pointers and casts away any and all references. 881 | * @tparam Value The `uni_auto` object 882 | */ 883 | template 884 | constexpr uni_auto_simplify_t uni_auto_simplify_v = Value; 885 | 886 | /** 887 | * @brief Pre-constructs a `uni_auto` object after binding an lvalue to a reference. 888 | * @tparam Value The lvalue that the reference will bind to 889 | */ 890 | template 891 | requires (!is_uni_auto>::value) 892 | constexpr uni_auto promote_to_ref = Value; 893 | 894 | /** 895 | * @brief Pre-constructs a `uni_auto` object after binding an lvalue to a const reference. 896 | * @tparam Value The lvalue that the const reference will bind to 897 | */ 898 | template 899 | requires (!is_uni_auto>::value) 900 | constexpr uni_auto promote_to_cref = Value; 901 | 902 | /** 903 | * @brief Exchanges the values held by `a` and `b`. 904 | */ 905 | template 906 | constexpr auto swap(const uni_auto& a, const uni_auto& b) noexcept { 907 | using std::swap; 908 | swap(a.operator typename uni_auto::type(), b.operator typename uni_auto::type()); 909 | } 910 | 911 | /** 912 | * @brief Exchanges the values held by `a` and `b`. 913 | */ 914 | template 915 | constexpr auto swap(const uni_auto& a, T2& b) noexcept { 916 | using std::swap; 917 | swap(a.operator typename uni_auto::type(), b); 918 | } 919 | 920 | /** 921 | * @brief Exchanges the values held by `a` and `b`. 922 | */ 923 | template 924 | constexpr auto swap(T1& a, const uni_auto& b) noexcept { 925 | using std::swap; 926 | swap(a, b.operator typename uni_auto::type()); 927 | } 928 | 929 | /** 930 | * @brief Creates an `std::array` object using `a`. 931 | */ 932 | template 933 | requires std::is_array_v> 934 | constexpr auto to_array(const uni_auto& a) noexcept { 935 | return std::to_array(a.value); 936 | } 937 | } 938 | 939 | export template 940 | struct std::formatter> : formatter> { 941 | template 942 | auto format(const uninttp::uni_auto& a, FormatContext& c) const { 943 | return formatter>::format(a.operator typename uninttp::uni_auto::type(), c); 944 | } 945 | }; 946 | -------------------------------------------------------------------------------- /uninttp/uni_auto.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * _ _ _ 3 | * (_) | | | | 4 | * _ _ _ __ _ _ __ | |_| |_ _ __ 5 | * | | | | '_ \| | '_ \| __| __| '_ \ 6 | * | |_| | | | | | | | | |_| |_| |_) | 7 | * \__,_|_| |_|_|_| |_|\__|\__| .__/ 8 | * | | 9 | * |_| 10 | * 11 | * uninttp (Universal Non-Type Template Parameters) 12 | * 13 | * Version: v4.2.9 14 | * 15 | * Copyright (c) 2021-... reacfen 16 | * 17 | * Permission is hereby granted, free of charge, to any person obtaining a copy 18 | * of this software and associated documentation files (the "Software"), to deal 19 | * in the Software without restriction, including without limitation the rights 20 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | * copies of the Software, and to permit persons to whom the Software is 22 | * furnished to do so, subject to the following conditions: 23 | * 24 | * The above copyright notice and this permission notice shall be included in all 25 | * copies or substantial portions of the Software. 26 | * 27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 | * SOFTWARE. 34 | */ 35 | 36 | #ifndef UNINTTP_UNI_AUTO_HPP 37 | #define UNINTTP_UNI_AUTO_HPP 38 | 39 | #if __cplusplus < 202002L 40 | #error uninttp can only be used with C++20 or later. 41 | #endif 42 | 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | namespace uninttp { 51 | /** 52 | * @brief The `uni_auto` class type implementation 53 | */ 54 | template 55 | struct uni_auto; 56 | 57 | namespace uninttp_internals { 58 | template 59 | struct is_uni_auto final : std::false_type {}; 60 | 61 | template 62 | struct is_uni_auto> final : std::true_type {}; 63 | } 64 | 65 | template 66 | struct uni_auto final { 67 | using type = const T(&)[N]; 68 | std::remove_reference_t value; 69 | 70 | template 71 | constexpr uni_auto(type v, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{ v[Indices]... } {} 72 | 73 | constexpr uni_auto(type v) noexcept(noexcept(uni_auto{ v, std::make_index_sequence() })) : uni_auto{ v, std::make_index_sequence() } {} 74 | 75 | constexpr operator type() const noexcept { 76 | return value; 77 | } 78 | 79 | static constexpr auto size() noexcept { 80 | return N; 81 | } 82 | 83 | constexpr auto data() const noexcept { 84 | return std::data(value); 85 | } 86 | 87 | constexpr auto empty() const noexcept { 88 | return std::empty(value); 89 | } 90 | 91 | constexpr auto begin() const noexcept { 92 | return std::begin(value); 93 | } 94 | 95 | constexpr auto end() const noexcept { 96 | return std::end(value); 97 | } 98 | 99 | constexpr auto cbegin() const noexcept { 100 | return std::cbegin(value); 101 | } 102 | 103 | constexpr auto cend() const noexcept { 104 | return std::cend(value); 105 | } 106 | 107 | constexpr auto rbegin() const noexcept { 108 | return std::rbegin(value); 109 | } 110 | 111 | constexpr auto rend() const noexcept { 112 | return std::rend(value); 113 | } 114 | 115 | constexpr auto crbegin() const noexcept { 116 | return std::crbegin(value); 117 | } 118 | 119 | constexpr auto crend() const noexcept { 120 | return std::crend(value); 121 | } 122 | }; 123 | 124 | template 125 | struct uni_auto final { 126 | using type = T&; 127 | type value; 128 | 129 | constexpr uni_auto(type v) noexcept : value{ v } {} 130 | 131 | constexpr operator type() const noexcept { 132 | return value; 133 | } 134 | 135 | static constexpr auto size() noexcept 136 | requires std::is_array_v { 137 | return std::extent_v; 138 | } 139 | 140 | constexpr auto size() const noexcept 141 | requires (!std::is_array_v) { 142 | return std::size(value); 143 | } 144 | 145 | constexpr auto swap(const uni_auto& other) const noexcept { 146 | using std::swap; 147 | swap(value, other.value); 148 | } 149 | 150 | constexpr auto data() const noexcept { 151 | return std::data(value); 152 | } 153 | 154 | constexpr auto empty() const noexcept { 155 | return std::empty(value); 156 | } 157 | 158 | constexpr auto begin() const noexcept { 159 | return std::begin(value); 160 | } 161 | 162 | constexpr auto end() const noexcept { 163 | return std::end(value); 164 | } 165 | 166 | constexpr auto cbegin() const noexcept { 167 | return std::cbegin(value); 168 | } 169 | 170 | constexpr auto cend() const noexcept { 171 | return std::cend(value); 172 | } 173 | 174 | constexpr auto rbegin() const noexcept { 175 | return std::rbegin(value); 176 | } 177 | 178 | constexpr auto rend() const noexcept { 179 | return std::rend(value); 180 | } 181 | 182 | constexpr auto crbegin() const noexcept { 183 | return std::crbegin(value); 184 | } 185 | 186 | constexpr auto crend() const noexcept { 187 | return std::crend(value); 188 | } 189 | 190 | template 191 | constexpr decltype(auto) operator=(U&& b) const noexcept(noexcept(value = std::forward(b))) 192 | requires requires { value = std::forward(b); } { 193 | return value = std::forward(b); 194 | } 195 | 196 | template 197 | constexpr decltype(auto) operator[](Args&&... args) const noexcept(noexcept(value.operator[](std::forward(args)...))) 198 | requires requires { value.operator[](std::forward(args)...); } { 199 | return value.operator[](std::forward(args)...); 200 | } 201 | 202 | constexpr decltype(auto) operator->() const noexcept(noexcept(value.operator->())) 203 | requires requires { value.operator->(); } { 204 | return value.operator->(); 205 | } 206 | 207 | template 208 | constexpr decltype(auto) operator()(Args&&... args) const noexcept(noexcept(value(std::forward(args)...))) 209 | requires requires { value(std::forward(args)...); } { 210 | return value(std::forward(args)...); 211 | } 212 | }; 213 | 214 | template 215 | requires (!std::is_class_v) 216 | struct uni_auto final { 217 | using type = T; 218 | type value; 219 | 220 | constexpr uni_auto(type v) noexcept : value{ v } {} 221 | 222 | constexpr operator type() const noexcept { 223 | return value; 224 | } 225 | 226 | constexpr decltype(auto) operator->() const noexcept 227 | requires std::is_pointer_v { 228 | return value; 229 | } 230 | }; 231 | 232 | template 233 | requires std::is_class_v 234 | struct uni_auto final : T { 235 | using type = T; 236 | 237 | constexpr uni_auto(const type& v) noexcept(std::is_nothrow_constructible_v) : type{ v } {} 238 | 239 | constexpr operator const type&() const noexcept { 240 | return *this; 241 | } 242 | }; 243 | 244 | /* Deals with `constexpr`-marked built-in arrays and fixed-size C-Strings/string literals */ 245 | template 246 | uni_auto(const T(&)[N]) -> uni_auto; 247 | 248 | /* Deals with built-in arrays, function pointers, integral and enumeration types, pointers to objects, pointers to member functions and objects, nullptr, etc. (Basically everything else) */ 249 | template 250 | uni_auto(const T&) -> uni_auto; 251 | 252 | template 253 | constexpr decltype(auto) operator++(const uni_auto& a) noexcept(noexcept(++a.operator typename uni_auto::type())) 254 | requires requires { ++a.operator typename uni_auto::type(); } { 255 | return ++a.operator typename uni_auto::type(); 256 | } 257 | 258 | template 259 | constexpr decltype(auto) operator--(const uni_auto& a) noexcept(noexcept(--a.operator typename uni_auto::type())) 260 | requires requires { --a.operator typename uni_auto::type(); } { 261 | return --a.operator typename uni_auto::type(); 262 | } 263 | 264 | template 265 | constexpr decltype(auto) operator++(const uni_auto& a, int) noexcept(noexcept(a.operator typename uni_auto::type()++)) 266 | requires requires { a.operator typename uni_auto::type()++; } { 267 | return a.operator typename uni_auto::type()++; 268 | } 269 | 270 | template 271 | constexpr decltype(auto) operator--(const uni_auto& a, int) noexcept(noexcept(a.operator typename uni_auto::type()--)) 272 | requires requires { a.operator typename uni_auto::type()--; } { 273 | return a.operator typename uni_auto::type()--; 274 | } 275 | 276 | template 277 | constexpr decltype(auto) operator+(const uni_auto& a) noexcept(noexcept(+a.operator typename uni_auto::type())) 278 | requires requires { +a.operator typename uni_auto::type(); } { 279 | return +a.operator typename uni_auto::type(); 280 | } 281 | 282 | template 283 | constexpr decltype(auto) operator-(const uni_auto& a) noexcept(noexcept(-a.operator typename uni_auto::type())) 284 | requires requires { -a.operator typename uni_auto::type(); } { 285 | return -a.operator typename uni_auto::type(); 286 | } 287 | 288 | template 289 | constexpr decltype(auto) operator~(const uni_auto& a) noexcept(noexcept(~a.operator typename uni_auto::type())) 290 | requires requires { ~a.operator typename uni_auto::type(); } { 291 | return ~a.operator typename uni_auto::type(); 292 | } 293 | 294 | template 295 | constexpr decltype(auto) operator!(const uni_auto& a) noexcept(noexcept(!a.operator typename uni_auto::type())) 296 | requires requires { !a.operator typename uni_auto::type(); } { 297 | return !a.operator typename uni_auto::type(); 298 | } 299 | 300 | template 301 | constexpr decltype(auto) operator*(const uni_auto& a) noexcept(noexcept(*a.operator typename uni_auto::type())) 302 | requires requires { *a.operator typename uni_auto::type(); } { 303 | return *a.operator typename uni_auto::type(); 304 | } 305 | 306 | template 307 | constexpr decltype(auto) operator&(const uni_auto& a) noexcept(noexcept(&a.operator typename uni_auto::type())) { 308 | return &a.operator typename uni_auto::type(); 309 | } 310 | 311 | template 312 | requires std::is_class_v 313 | constexpr decltype(auto) operator&(const uni_auto& a) noexcept { 314 | return &a.operator const typename uni_auto::type&(); 315 | } 316 | 317 | template 318 | requires (!std::is_class_v) 319 | constexpr decltype(auto) operator+=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() += b.operator typename uni_auto::type())) 320 | requires requires { a.operator typename uni_auto::type() += b.operator typename uni_auto::type(); } { 321 | return a.operator typename uni_auto::type() += b.operator typename uni_auto::type(); 322 | } 323 | 324 | template 325 | requires (!std::is_class_v) 326 | constexpr decltype(auto) operator-=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() -= b.operator typename uni_auto::type())) 327 | requires requires { a.operator typename uni_auto::type() -= b.operator typename uni_auto::type(); } { 328 | return a.operator typename uni_auto::type() -= b.operator typename uni_auto::type(); 329 | } 330 | 331 | template 332 | requires (!std::is_class_v) 333 | constexpr decltype(auto) operator*=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() *= b.operator typename uni_auto::type())) 334 | requires requires { a.operator typename uni_auto::type() *= b.operator typename uni_auto::type(); } { 335 | return a.operator typename uni_auto::type() *= b.operator typename uni_auto::type(); 336 | } 337 | 338 | template 339 | requires (!std::is_class_v) 340 | constexpr decltype(auto) operator/=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() /= b.operator typename uni_auto::type())) 341 | requires requires { a.operator typename uni_auto::type() /= b.operator typename uni_auto::type(); } { 342 | return a.operator typename uni_auto::type() /= b.operator typename uni_auto::type(); 343 | } 344 | 345 | template 346 | requires (!std::is_class_v) 347 | constexpr decltype(auto) operator%=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() %= b.operator typename uni_auto::type())) 348 | requires requires { a.operator typename uni_auto::type() %= b.operator typename uni_auto::type(); } { 349 | return a.operator typename uni_auto::type() %= b.operator typename uni_auto::type(); 350 | } 351 | 352 | template 353 | requires (!std::is_class_v) 354 | constexpr decltype(auto) operator&=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() &= b.operator typename uni_auto::type())) 355 | requires requires { a.operator typename uni_auto::type() &= b.operator typename uni_auto::type(); } { 356 | return a.operator typename uni_auto::type() &= b.operator typename uni_auto::type(); 357 | } 358 | 359 | template 360 | requires (!std::is_class_v) 361 | constexpr decltype(auto) operator|=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() |= b.operator typename uni_auto::type())) 362 | requires requires { a.operator typename uni_auto::type() |= b.operator typename uni_auto::type(); } { 363 | return a.operator typename uni_auto::type() |= b.operator typename uni_auto::type(); 364 | } 365 | 366 | template 367 | requires (!std::is_class_v) 368 | constexpr decltype(auto) operator^=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() ^= b.operator typename uni_auto::type())) 369 | requires requires { a.operator typename uni_auto::type() ^= b.operator typename uni_auto::type(); } { 370 | return a.operator typename uni_auto::type() ^= b.operator typename uni_auto::type(); 371 | } 372 | 373 | template 374 | requires (!std::is_class_v) 375 | constexpr decltype(auto) operator<<=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() <<= b.operator typename uni_auto::type())) 376 | requires requires { a.operator typename uni_auto::type() <<= b.operator typename uni_auto::type(); } { 377 | return a.operator typename uni_auto::type() <<= b.operator typename uni_auto::type(); 378 | } 379 | 380 | template 381 | requires (!std::is_class_v) 382 | constexpr decltype(auto) operator>>=(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() >>= b.operator typename uni_auto::type())) 383 | requires requires { a.operator typename uni_auto::type() >>= b.operator typename uni_auto::type(); } { 384 | return a.operator typename uni_auto::type() >>= b.operator typename uni_auto::type(); 385 | } 386 | 387 | template 388 | requires (!std::is_class_v) 389 | constexpr decltype(auto) operator+(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() + b.operator typename uni_auto::type())) 390 | requires requires { a.operator typename uni_auto::type() + b.operator typename uni_auto::type(); } { 391 | return a.operator typename uni_auto::type() + b.operator typename uni_auto::type(); 392 | } 393 | 394 | template 395 | requires (!std::is_class_v) 396 | constexpr decltype(auto) operator-(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() - b.operator typename uni_auto::type())) 397 | requires requires { a.operator typename uni_auto::type() - b.operator typename uni_auto::type(); } { 398 | return a.operator typename uni_auto::type() - b.operator typename uni_auto::type(); 399 | } 400 | 401 | template 402 | requires (!std::is_class_v) 403 | constexpr decltype(auto) operator*(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() * b.operator typename uni_auto::type())) 404 | requires requires { a.operator typename uni_auto::type() * b.operator typename uni_auto::type(); } { 405 | return a.operator typename uni_auto::type() * b.operator typename uni_auto::type(); 406 | } 407 | 408 | template 409 | requires (!std::is_class_v) 410 | constexpr decltype(auto) operator/(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() / b.operator typename uni_auto::type())) 411 | requires requires { a.operator typename uni_auto::type() / b.operator typename uni_auto::type(); } { 412 | return a.operator typename uni_auto::type() / b.operator typename uni_auto::type(); 413 | } 414 | 415 | template 416 | requires (!std::is_class_v) 417 | constexpr decltype(auto) operator%(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() % b.operator typename uni_auto::type())) 418 | requires requires { a.operator typename uni_auto::type() % b.operator typename uni_auto::type(); } { 419 | return a.operator typename uni_auto::type() % b.operator typename uni_auto::type(); 420 | } 421 | 422 | template 423 | requires (!std::is_class_v) 424 | constexpr decltype(auto) operator&(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() & b.operator typename uni_auto::type())) 425 | requires requires { a.operator typename uni_auto::type() & b.operator typename uni_auto::type(); } { 426 | return a.operator typename uni_auto::type() & b.operator typename uni_auto::type(); 427 | } 428 | 429 | template 430 | requires (!std::is_class_v) 431 | constexpr decltype(auto) operator|(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() | b.operator typename uni_auto::type())) 432 | requires requires { a.operator typename uni_auto::type() | b.operator typename uni_auto::type(); } { 433 | return a.operator typename uni_auto::type() | b.operator typename uni_auto::type(); 434 | } 435 | 436 | template 437 | requires (!std::is_class_v) 438 | constexpr decltype(auto) operator^(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() ^ b.operator typename uni_auto::type())) 439 | requires requires { a.operator typename uni_auto::type() ^ b.operator typename uni_auto::type(); } { 440 | return a.operator typename uni_auto::type() ^ b.operator typename uni_auto::type(); 441 | } 442 | 443 | template 444 | requires (!std::is_class_v) 445 | constexpr decltype(auto) operator<<(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() << b.operator typename uni_auto::type())) 446 | requires requires { a.operator typename uni_auto::type() << b.operator typename uni_auto::type(); } { 447 | return a.operator typename uni_auto::type() << b.operator typename uni_auto::type(); 448 | } 449 | 450 | template 451 | requires (!std::is_class_v) 452 | constexpr decltype(auto) operator>>(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() >> b.operator typename uni_auto::type())) 453 | requires requires { a.operator typename uni_auto::type() >> b.operator typename uni_auto::type(); } { 454 | return a.operator typename uni_auto::type() >> b.operator typename uni_auto::type(); 455 | } 456 | 457 | template 458 | requires (!std::is_class_v) 459 | constexpr decltype(auto) operator&&(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() && b.operator typename uni_auto::type())) 460 | requires requires { a.operator typename uni_auto::type() && b.operator typename uni_auto::type(); } { 461 | return a.operator typename uni_auto::type() && b.operator typename uni_auto::type(); 462 | } 463 | 464 | template 465 | requires (!std::is_class_v) 466 | constexpr decltype(auto) operator||(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() || b.operator typename uni_auto::type())) 467 | requires requires { a.operator typename uni_auto::type() || b.operator typename uni_auto::type(); } { 468 | return a.operator typename uni_auto::type() || b.operator typename uni_auto::type(); 469 | } 470 | 471 | template 472 | requires (!std::is_class_v) 473 | constexpr decltype(auto) operator==(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() == b.operator typename uni_auto::type())) 474 | requires requires { a.operator typename uni_auto::type() == b.operator typename uni_auto::type(); } { 475 | return a.operator typename uni_auto::type() == b.operator typename uni_auto::type(); 476 | } 477 | 478 | template 479 | requires (!std::is_class_v) 480 | constexpr decltype(auto) operator<=>(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() <=> b.operator typename uni_auto::type())) 481 | requires requires { a.operator typename uni_auto::type() <=> b.operator typename uni_auto::type(); } { 482 | return a.operator typename uni_auto::type() <=> b.operator typename uni_auto::type(); 483 | } 484 | 485 | template 486 | requires (!std::is_class_v) 487 | constexpr decltype(auto) operator->*(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type() ->* b.operator typename uni_auto::type())) 488 | requires requires { a.operator typename uni_auto::type() ->* b.operator typename uni_auto::type(); } { 489 | return a.operator typename uni_auto::type() ->* b.operator typename uni_auto::type(); 490 | } 491 | 492 | template 493 | requires (!std::is_class_v) 494 | constexpr decltype(auto) operator,(const uni_auto& a, const uni_auto& b) noexcept(noexcept(a.operator typename uni_auto::type(), b.operator typename uni_auto::type())) 495 | requires requires { a.operator typename uni_auto::type(), b.operator typename uni_auto::type(); } { 496 | return a.operator typename uni_auto::type(), b.operator typename uni_auto::type(); 497 | } 498 | 499 | template 500 | requires (!uninttp_internals::is_uni_auto>::value) 501 | constexpr decltype(auto) operator+=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) += b.operator typename uni_auto::type())) 502 | requires requires { std::forward(a) += b.operator typename uni_auto::type(); } { 503 | return std::forward(a) += b.operator typename uni_auto::type(); 504 | } 505 | 506 | template 507 | requires (!uninttp_internals::is_uni_auto>::value) 508 | constexpr decltype(auto) operator-=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) -= b.operator typename uni_auto::type())) 509 | requires requires { std::forward(a) -= b.operator typename uni_auto::type(); } { 510 | return std::forward(a) -= b.operator typename uni_auto::type(); 511 | } 512 | 513 | template 514 | requires (!uninttp_internals::is_uni_auto>::value) 515 | constexpr decltype(auto) operator*=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) *= b.operator typename uni_auto::type())) 516 | requires requires { std::forward(a) *= b.operator typename uni_auto::type(); } { 517 | return std::forward(a) *= b.operator typename uni_auto::type(); 518 | } 519 | 520 | template 521 | requires (!uninttp_internals::is_uni_auto>::value) 522 | constexpr decltype(auto) operator/=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) /= b.operator typename uni_auto::type())) 523 | requires requires { std::forward(a) /= b.operator typename uni_auto::type(); } { 524 | return std::forward(a) /= b.operator typename uni_auto::type(); 525 | } 526 | 527 | template 528 | requires (!uninttp_internals::is_uni_auto>::value) 529 | constexpr decltype(auto) operator%=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) %= b.operator typename uni_auto::type())) 530 | requires requires { std::forward(a) %= b.operator typename uni_auto::type(); } { 531 | return std::forward(a) %= b.operator typename uni_auto::type(); 532 | } 533 | 534 | template 535 | requires (!uninttp_internals::is_uni_auto>::value) 536 | constexpr decltype(auto) operator&=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) &= b.operator typename uni_auto::type())) 537 | requires requires { std::forward(a) &= b.operator typename uni_auto::type(); } { 538 | return std::forward(a) &= b.operator typename uni_auto::type(); 539 | } 540 | 541 | template 542 | requires (!uninttp_internals::is_uni_auto>::value) 543 | constexpr decltype(auto) operator|=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) |= b.operator typename uni_auto::type())) 544 | requires requires { std::forward(a) |= b.operator typename uni_auto::type(); } { 545 | return std::forward(a) |= b.operator typename uni_auto::type(); 546 | } 547 | 548 | template 549 | requires (!uninttp_internals::is_uni_auto>::value) 550 | constexpr decltype(auto) operator^=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) ^= b.operator typename uni_auto::type())) 551 | requires requires { std::forward(a) ^= b.operator typename uni_auto::type(); } { 552 | return std::forward(a) ^= b.operator typename uni_auto::type(); 553 | } 554 | 555 | template 556 | requires (!uninttp_internals::is_uni_auto>::value) 557 | constexpr decltype(auto) operator<<=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) <<= b.operator typename uni_auto::type())) 558 | requires requires { std::forward(a) <<= b.operator typename uni_auto::type(); } { 559 | return std::forward(a) <<= b.operator typename uni_auto::type(); 560 | } 561 | 562 | template 563 | requires (!uninttp_internals::is_uni_auto>::value) 564 | constexpr decltype(auto) operator>>=(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) >>= b.operator typename uni_auto::type())) 565 | requires requires { std::forward(a) >>= b.operator typename uni_auto::type(); } { 566 | return std::forward(a) >>= b.operator typename uni_auto::type(); 567 | } 568 | 569 | template 570 | requires (!uninttp_internals::is_uni_auto>::value) 571 | constexpr decltype(auto) operator+(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) + b.operator typename uni_auto::type())) 572 | requires requires { std::forward(a) + b.operator typename uni_auto::type(); } { 573 | return std::forward(a) + b.operator typename uni_auto::type(); 574 | } 575 | 576 | template 577 | requires (!uninttp_internals::is_uni_auto>::value) 578 | constexpr decltype(auto) operator-(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) - b.operator typename uni_auto::type())) 579 | requires requires { std::forward(a) - b.operator typename uni_auto::type(); } { 580 | return std::forward(a) - b.operator typename uni_auto::type(); 581 | } 582 | 583 | template 584 | requires (!uninttp_internals::is_uni_auto>::value) 585 | constexpr decltype(auto) operator*(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) * b.operator typename uni_auto::type())) 586 | requires requires { std::forward(a) * b.operator typename uni_auto::type(); } { 587 | return std::forward(a) * b.operator typename uni_auto::type(); 588 | } 589 | 590 | template 591 | requires (!uninttp_internals::is_uni_auto>::value) 592 | constexpr decltype(auto) operator/(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) / b.operator typename uni_auto::type())) 593 | requires requires { std::forward(a) / b.operator typename uni_auto::type(); } { 594 | return std::forward(a) / b.operator typename uni_auto::type(); 595 | } 596 | 597 | template 598 | requires (!uninttp_internals::is_uni_auto>::value) 599 | constexpr decltype(auto) operator%(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) % b.operator typename uni_auto::type())) 600 | requires requires { std::forward(a) % b.operator typename uni_auto::type(); } { 601 | return std::forward(a) % b.operator typename uni_auto::type(); 602 | } 603 | 604 | template 605 | requires (!uninttp_internals::is_uni_auto>::value) 606 | constexpr decltype(auto) operator&(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) & b.operator typename uni_auto::type())) 607 | requires requires { std::forward(a) & b.operator typename uni_auto::type(); } { 608 | return std::forward(a) & b.operator typename uni_auto::type(); 609 | } 610 | 611 | template 612 | requires (!uninttp_internals::is_uni_auto>::value) 613 | constexpr decltype(auto) operator|(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) | b.operator typename uni_auto::type())) 614 | requires requires { std::forward(a) | b.operator typename uni_auto::type(); } { 615 | return std::forward(a) | b.operator typename uni_auto::type(); 616 | } 617 | 618 | template 619 | requires (!uninttp_internals::is_uni_auto>::value) 620 | constexpr decltype(auto) operator^(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) ^ b.operator typename uni_auto::type())) 621 | requires requires { std::forward(a) ^ b.operator typename uni_auto::type(); } { 622 | return std::forward(a) ^ b.operator typename uni_auto::type(); 623 | } 624 | 625 | template 626 | requires (!uninttp_internals::is_uni_auto>::value) 627 | constexpr decltype(auto) operator<<(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) << b.operator typename uni_auto::type())) 628 | requires requires { std::forward(a) << b.operator typename uni_auto::type(); } { 629 | return std::forward(a) << b.operator typename uni_auto::type(); 630 | } 631 | 632 | template 633 | requires (!uninttp_internals::is_uni_auto>::value) 634 | constexpr decltype(auto) operator>>(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) >> b.operator typename uni_auto::type())) 635 | requires requires { std::forward(a) >> b.operator typename uni_auto::type(); } { 636 | return std::forward(a) >> b.operator typename uni_auto::type(); 637 | } 638 | 639 | template 640 | requires (!uninttp_internals::is_uni_auto>::value) 641 | constexpr decltype(auto) operator&&(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) && b.operator typename uni_auto::type())) 642 | requires requires { std::forward(a) && b.operator typename uni_auto::type(); } { 643 | return std::forward(a) && b.operator typename uni_auto::type(); 644 | } 645 | 646 | template 647 | requires (!uninttp_internals::is_uni_auto>::value) 648 | constexpr decltype(auto) operator||(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) || b.operator typename uni_auto::type())) 649 | requires requires { std::forward(a) || b.operator typename uni_auto::type(); } { 650 | return std::forward(a) || b.operator typename uni_auto::type(); 651 | } 652 | 653 | template 654 | requires (!uninttp_internals::is_uni_auto>::value) 655 | constexpr decltype(auto) operator==(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) == b.operator typename uni_auto::type())) 656 | requires requires { std::forward(a) == b.operator typename uni_auto::type(); } { 657 | return std::forward(a) == b.operator typename uni_auto::type(); 658 | } 659 | 660 | template 661 | requires (!uninttp_internals::is_uni_auto>::value) 662 | constexpr decltype(auto) operator<=>(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) <=> b.operator typename uni_auto::type())) 663 | requires requires { std::forward(a) <=> b.operator typename uni_auto::type(); } { 664 | return std::forward(a) <=> b.operator typename uni_auto::type(); 665 | } 666 | 667 | template 668 | requires (!uninttp_internals::is_uni_auto>::value) 669 | constexpr decltype(auto) operator->*(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a) ->* b.operator typename uni_auto::type())) 670 | requires requires { std::forward(a) ->* b.operator typename uni_auto::type(); } { 671 | return std::forward(a) ->* b.operator typename uni_auto::type(); 672 | } 673 | 674 | template 675 | requires (!uninttp_internals::is_uni_auto>::value) 676 | constexpr decltype(auto) operator,(T&& a, const uni_auto& b) noexcept(noexcept(std::forward(a), b.operator typename uni_auto::type())) 677 | requires requires { std::forward(a), b.operator typename uni_auto::type(); } { 678 | return std::forward(a), b.operator typename uni_auto::type(); 679 | } 680 | 681 | template 682 | requires (!uninttp_internals::is_uni_auto>::value) 683 | constexpr decltype(auto) operator+=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() += std::forward(b))) 684 | requires requires { a.operator typename uni_auto::type() += std::forward(b); } { 685 | return a.operator typename uni_auto::type() += std::forward(b); 686 | } 687 | 688 | template 689 | requires (!uninttp_internals::is_uni_auto>::value) 690 | constexpr decltype(auto) operator-=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() -= std::forward(b))) 691 | requires requires { a.operator typename uni_auto::type() -= std::forward(b); } { 692 | return a.operator typename uni_auto::type() -= std::forward(b); 693 | } 694 | 695 | template 696 | requires (!uninttp_internals::is_uni_auto>::value) 697 | constexpr decltype(auto) operator*=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() *= std::forward(b))) 698 | requires requires { a.operator typename uni_auto::type() *= std::forward(b); } { 699 | return a.operator typename uni_auto::type() *= std::forward(b); 700 | } 701 | 702 | template 703 | requires (!uninttp_internals::is_uni_auto>::value) 704 | constexpr decltype(auto) operator/=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() /= std::forward(b))) 705 | requires requires { a.operator typename uni_auto::type() /= std::forward(b); } { 706 | return a.operator typename uni_auto::type() /= std::forward(b); 707 | } 708 | 709 | template 710 | requires (!uninttp_internals::is_uni_auto>::value) 711 | constexpr decltype(auto) operator%=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() %= std::forward(b))) 712 | requires requires { a.operator typename uni_auto::type() %= std::forward(b); } { 713 | return a.operator typename uni_auto::type() %= std::forward(b); 714 | } 715 | 716 | template 717 | requires (!uninttp_internals::is_uni_auto>::value) 718 | constexpr decltype(auto) operator&=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() &= std::forward(b))) 719 | requires requires { a.operator typename uni_auto::type() &= std::forward(b); } { 720 | return a.operator typename uni_auto::type() &= std::forward(b); 721 | } 722 | 723 | template 724 | requires (!uninttp_internals::is_uni_auto>::value) 725 | constexpr decltype(auto) operator|=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() |= std::forward(b))) 726 | requires requires { a.operator typename uni_auto::type() |= std::forward(b); } { 727 | return a.operator typename uni_auto::type() |= std::forward(b); 728 | } 729 | 730 | template 731 | requires (!uninttp_internals::is_uni_auto>::value) 732 | constexpr decltype(auto) operator^=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() ^= std::forward(b))) 733 | requires requires { a.operator typename uni_auto::type() ^= std::forward(b); } { 734 | return a.operator typename uni_auto::type() ^= std::forward(b); 735 | } 736 | 737 | template 738 | requires (!uninttp_internals::is_uni_auto>::value) 739 | constexpr decltype(auto) operator<<=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() <<= std::forward(b))) 740 | requires requires { a.operator typename uni_auto::type() <<= std::forward(b); } { 741 | return a.operator typename uni_auto::type() <<= std::forward(b); 742 | } 743 | 744 | template 745 | requires (!uninttp_internals::is_uni_auto>::value) 746 | constexpr decltype(auto) operator>>=(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() >>= std::forward(b))) 747 | requires requires { a.operator typename uni_auto::type() >>= std::forward(b); } { 748 | return a.operator typename uni_auto::type() >>= std::forward(b); 749 | } 750 | 751 | template 752 | requires (!uninttp_internals::is_uni_auto>::value) 753 | constexpr decltype(auto) operator+(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() + std::forward(b))) 754 | requires requires { a.operator typename uni_auto::type() + std::forward(b); } { 755 | return a.operator typename uni_auto::type() + std::forward(b); 756 | } 757 | 758 | template 759 | requires (!uninttp_internals::is_uni_auto>::value) 760 | constexpr decltype(auto) operator-(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() - std::forward(b))) 761 | requires requires { a.operator typename uni_auto::type() - std::forward(b); } { 762 | return a.operator typename uni_auto::type() - std::forward(b); 763 | } 764 | 765 | template 766 | requires (!uninttp_internals::is_uni_auto>::value) 767 | constexpr decltype(auto) operator*(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() * std::forward(b))) 768 | requires requires { a.operator typename uni_auto::type() * std::forward(b); } { 769 | return a.operator typename uni_auto::type() * std::forward(b); 770 | } 771 | 772 | template 773 | requires (!uninttp_internals::is_uni_auto>::value) 774 | constexpr decltype(auto) operator/(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() / std::forward(b))) 775 | requires requires { a.operator typename uni_auto::type() / std::forward(b); } { 776 | return a.operator typename uni_auto::type() / std::forward(b); 777 | } 778 | 779 | template 780 | requires (!uninttp_internals::is_uni_auto>::value) 781 | constexpr decltype(auto) operator%(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() % std::forward(b))) 782 | requires requires { a.operator typename uni_auto::type() % std::forward(b); } { 783 | return a.operator typename uni_auto::type() % std::forward(b); 784 | } 785 | 786 | template 787 | requires (!uninttp_internals::is_uni_auto>::value) 788 | constexpr decltype(auto) operator&(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() & std::forward(b))) 789 | requires requires { a.operator typename uni_auto::type() & std::forward(b); } { 790 | return a.operator typename uni_auto::type() & std::forward(b); 791 | } 792 | 793 | template 794 | requires (!uninttp_internals::is_uni_auto>::value) 795 | constexpr decltype(auto) operator|(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() | std::forward(b))) 796 | requires requires { a.operator typename uni_auto::type() | std::forward(b); } { 797 | return a.operator typename uni_auto::type() | std::forward(b); 798 | } 799 | 800 | template 801 | requires (!uninttp_internals::is_uni_auto>::value) 802 | constexpr decltype(auto) operator^(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() ^ std::forward(b))) 803 | requires requires { a.operator typename uni_auto::type() ^ std::forward(b); } { 804 | return a.operator typename uni_auto::type() ^ std::forward(b); 805 | } 806 | 807 | template 808 | requires (!uninttp_internals::is_uni_auto>::value) 809 | constexpr decltype(auto) operator<<(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() << std::forward(b))) 810 | requires requires { a.operator typename uni_auto::type() << std::forward(b); } { 811 | return a.operator typename uni_auto::type() << std::forward(b); 812 | } 813 | 814 | template 815 | requires (!uninttp_internals::is_uni_auto>::value) 816 | constexpr decltype(auto) operator>>(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() >> std::forward(b))) 817 | requires requires { a.operator typename uni_auto::type() >> std::forward(b); } { 818 | return a.operator typename uni_auto::type() >> std::forward(b); 819 | } 820 | 821 | template 822 | requires (!uninttp_internals::is_uni_auto>::value) 823 | constexpr decltype(auto) operator&&(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() && std::forward(b))) 824 | requires requires { a.operator typename uni_auto::type() && std::forward(b); } { 825 | return a.operator typename uni_auto::type() && std::forward(b); 826 | } 827 | 828 | template 829 | requires (!uninttp_internals::is_uni_auto>::value) 830 | constexpr decltype(auto) operator||(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() || std::forward(b))) 831 | requires requires { a.operator typename uni_auto::type() || std::forward(b); } { 832 | return a.operator typename uni_auto::type() || std::forward(b); 833 | } 834 | 835 | template 836 | requires (!uninttp_internals::is_uni_auto>::value) 837 | constexpr decltype(auto) operator==(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() == std::forward(b))) 838 | requires requires { a.operator typename uni_auto::type() == std::forward(b); } { 839 | return a.operator typename uni_auto::type() == std::forward(b); 840 | } 841 | 842 | template 843 | requires (!uninttp_internals::is_uni_auto>::value) 844 | constexpr decltype(auto) operator<=>(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() <=> std::forward(b))) 845 | requires requires { a.operator typename uni_auto::type() <=> std::forward(b); } { 846 | return a.operator typename uni_auto::type() <=> std::forward(b); 847 | } 848 | 849 | template 850 | requires (!uninttp_internals::is_uni_auto>::value) 851 | constexpr decltype(auto) operator->*(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type() ->* std::forward(b))) 852 | requires requires { a.operator typename uni_auto::type() ->* std::forward(b); } { 853 | return a.operator typename uni_auto::type() ->* std::forward(b); 854 | } 855 | 856 | template 857 | requires (!uninttp_internals::is_uni_auto>::value) 858 | constexpr decltype(auto) operator,(const uni_auto& a, U&& b) noexcept(noexcept(a.operator typename uni_auto::type(), std::forward(b))) 859 | requires requires { a.operator typename uni_auto::type(), std::forward(b); } { 860 | return a.operator typename uni_auto::type(), std::forward(b); 861 | } 862 | 863 | /** 864 | * @brief Fetches the type of the underlying value held by a `uni_auto` object. 865 | * @tparam Value The `uni_auto` object 866 | */ 867 | template 868 | using uni_auto_t = typename decltype(Value)::type; 869 | 870 | /** 871 | * @brief Fetches the underlying value held by a `uni_auto` object. 872 | * @tparam Value The `uni_auto` object 873 | */ 874 | template 875 | constexpr uni_auto_t uni_auto_v = Value; 876 | 877 | /** 878 | * @brief Similar to `uni_auto_t` but condenses array types and function types to pointers and removes any references from the type returned. 879 | * @tparam Value The `uni_auto` object 880 | */ 881 | template 882 | using uni_auto_simplify_t = std::decay_t; 883 | 884 | /** 885 | * @brief Similar to `uni_auto_v` but converts array types and function types to pointers and casts away any and all references. 886 | * @tparam Value The `uni_auto` object 887 | */ 888 | template 889 | constexpr uni_auto_simplify_t uni_auto_simplify_v = Value; 890 | 891 | /** 892 | * @brief Pre-constructs a `uni_auto` object after binding an lvalue to a reference. 893 | * @tparam Value The lvalue that the reference will bind to 894 | */ 895 | template 896 | requires (!uninttp_internals::is_uni_auto>::value) 897 | constexpr uni_auto promote_to_ref = Value; 898 | 899 | /** 900 | * @brief Pre-constructs a `uni_auto` object after binding an lvalue to a const reference. 901 | * @tparam Value The lvalue that the const reference will bind to 902 | */ 903 | template 904 | requires (!uninttp_internals::is_uni_auto>::value) 905 | constexpr uni_auto promote_to_cref = Value; 906 | 907 | /** 908 | * @brief Exchanges the values held by `a` and `b`. 909 | */ 910 | template 911 | constexpr auto swap(const uni_auto& a, const uni_auto& b) noexcept { 912 | using std::swap; 913 | swap(a.operator typename uni_auto::type(), b.operator typename uni_auto::type()); 914 | } 915 | 916 | /** 917 | * @brief Exchanges the values held by `a` and `b`. 918 | */ 919 | template 920 | constexpr auto swap(const uni_auto& a, T2& b) noexcept { 921 | using std::swap; 922 | swap(a.operator typename uni_auto::type(), b); 923 | } 924 | 925 | /** 926 | * @brief Exchanges the values held by `a` and `b`. 927 | */ 928 | template 929 | constexpr auto swap(T1& a, const uni_auto& b) noexcept { 930 | using std::swap; 931 | swap(a, b.operator typename uni_auto::type()); 932 | } 933 | 934 | /** 935 | * @brief Creates an `std::array` object using `a`. 936 | */ 937 | template 938 | requires std::is_array_v> 939 | constexpr auto to_array(const uni_auto& a) noexcept { 940 | return std::to_array(a.value); 941 | } 942 | } 943 | 944 | template 945 | struct std::formatter> : formatter> { 946 | template 947 | auto format(const uninttp::uni_auto& a, FormatContext& c) const { 948 | return formatter>::format(a.operator typename uninttp::uni_auto::type(), c); 949 | } 950 | }; 951 | 952 | #ifdef FMT_EXPORT 953 | template 954 | struct fmt::formatter> : formatter> { 955 | template 956 | auto format(const uninttp::uni_auto& a, FormatContext& c) const { 957 | return formatter>::format(a.operator typename uninttp::uni_auto::type(), c); 958 | } 959 | }; 960 | #endif 961 | 962 | #endif /* UNINTTP_UNI_AUTO_HPP */ 963 | --------------------------------------------------------------------------------