├── .github └── FUNDING.yml ├── .gitignore ├── CMakeLists.txt ├── Guidelines.md ├── LICENSE_1_0.txt ├── README.md ├── Tutorial.md ├── build.sh ├── example ├── CMakeLists.txt ├── arity.cpp ├── compiler_detector.cpp ├── curry.cpp ├── is_lambda.cpp ├── loop_unroll.cpp ├── memoized_invoke.cpp ├── monster.cpp ├── object_pool.cpp ├── overview.cpp ├── sort_tuple.cpp ├── stream.cpp ├── tensor.cpp ├── thread_pool.cpp ├── tuple_algorithm.cpp ├── unique_tuple.cpp └── ycombinator.cpp └── include ├── arity.hpp ├── compiler_detector.hpp ├── curry.hpp ├── hof.hpp ├── is_identical.hpp ├── is_lambda.hpp ├── loop_unroll.hpp ├── memoized_invoke.hpp ├── monster.hpp ├── object_pool.hpp ├── ring.hpp ├── sort_tuple.hpp ├── stream.hpp ├── tensor.hpp ├── thread_pool.hpp ├── to_array.hpp ├── tuple_algorithm.hpp ├── unique_tuple.hpp ├── version.hpp └── ycombinator.hpp /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with a single custom sponsorship URL 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | bin/ 3 | lib/ 4 | *.o 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2011-present DeepGrace (complex dot invoke at gmail dot com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | # 7 | # Official repository: https://github.com/deepgrace/monster 8 | # 9 | 10 | cmake_minimum_required(VERSION 3.22) 11 | project(Monster) 12 | 13 | add_subdirectory(example) 14 | -------------------------------------------------------------------------------- /Guidelines.md: -------------------------------------------------------------------------------- 1 | # Monster Guidelines 2 | 3 | ## Installation 4 | Monster is a header-only library that is self contained in one single header file without external dependencies. 5 | To use it, just download the library and add the directory containing it to your compiler's header search path, then include the library: 6 | ```cpp 7 | #include 8 | using namespace monster; 9 | ``` 10 | Unless specified otherwise, the documentation assumes the above lines to be present before examples and code snippets. 11 | 12 | ## Table of contents 13 | Monster provides the following metaprograms to manipulate heterogeneous sequences. 14 | 15 | - [access elements](#access-elements) 16 | - [accumulate elements](#accumulate-elements) 17 | - [algorithms](#algorithms) 18 | - [combinations](#combinations) 19 | - [currying](#currying) 20 | - [delete elements](#delete-elements) 21 | - [expand sequences](#expand-sequences) 22 | - [generate sequences](#generate-sequences) 23 | - [insert elements](#insert-elements) 24 | - [loops](#loops) 25 | - [metafunctions](#metafunctions) 26 | - [object pool](#object-pool) 27 | - [overload](#overload) 28 | - [permutations](#permutations) 29 | - [predicate elements](#predicate-elements) 30 | - [range of sequences](#range-of-sequences) 31 | - [replace elements](#replace-elements) 32 | - [reverse sequences](#reverse-sequences) 33 | - [rotate sequences](#rotate-sequences) 34 | - [swap elements](#swap-elements) 35 | - [search elements](#search-elements) 36 | - [shift elements](#shift-elements) 37 | - [sort elements](#sort-elements) 38 | - [tensor](#tensor) 39 | - [thread pool](#thread-pool) 40 | - [transform elements](#transform-elements) 41 | - [tuple operations](#tuple-operations) 42 | 43 | ## Reference 44 | 45 | ### Access elements 46 | ```cpp 47 | // get the first element 48 | using f1 = front_t>; 49 | using f2 = front_t>; 50 | auto f3 = front_v>; 51 | using f4 = first_type; 52 | // f1 == char 53 | // f2 == c_1 54 | // f3 == 1 55 | // f4 == int 56 | 57 | // get the first two elements 58 | using f5 = take_t<2, std::tuple>; 59 | // f5 == std::tuple 60 | 61 | // get the last element 62 | using b1 = back_t>; 63 | using b2 = back_t>; 64 | auto b3 = back_v>; 65 | using b4 = last_type; 66 | // b1 == int 67 | // b2 == c_4 68 | // b3 == 4 69 | // b4 == float 70 | 71 | // get the last two elements 72 | using b5 = take_last_t<2, std::integer_sequence>; 73 | // b5 == std::integer_sequence 74 | 75 | // get odd indexed elements 76 | using odd1 = odd_t>; 77 | using odd2 = odd_t>; 78 | // odd1 == std::tuple 79 | // odd2 == std::integer_sequence 80 | 81 | // get even indexed elements 82 | using even1 = even_t>; 83 | using even2 = even_t>; 84 | // even1 == std::tuple 85 | // even2 == std::integer_sequence 86 | 87 | // get an element at specific index 88 | using e1 = element_t<1, std::tuple>; 89 | using e2 = element_t<2, std::integer_sequence>; 90 | using e3 = get_t<1, std::integer_sequence>; 91 | auto e4 = get_v<1, std::integer_sequence>; 92 | auto e5 = element_v<3, std::integer_sequence>; 93 | using e6 = nth_type_t<2, int, double, char*, float>; 94 | // e1 == double 95 | // e2 == c_7 96 | // e3 == c_2 97 | // e4 == 2 98 | // e5 == 4 99 | // e6 == char* 100 | 101 | // previous element of a specific index 102 | using p1 = prev_t<2, std::tuple>; 103 | using p2 = prev_t<1, std::integer_sequence>; 104 | auo p3 = prev_v<1, std::integer_sequence>; 105 | // p1 == double 106 | // p2 == c_7 107 | // p3 == 7 108 | 109 | // next element of a specific index 110 | using n1 = next_t<2, std::tuple>; 111 | using n2 = next_t<1, std::integer_sequence>; 112 | auto n3 = next_v<1, std::integer_sequence>; 113 | // n1 == float 114 | // n2 == c_<-1> 115 | // n3 == -1 116 | 117 | // middle element of a sequence 118 | using m1 = midpoint_t>; 119 | using m2 = midpoint_t>; 120 | auto m3 = midpoint_v>; 121 | // m1 == short 122 | // m2 == c_<-1> 123 | // m3 == -1 124 | 125 | // size of a type 126 | auto s1 = sizeof_t_v; 127 | auto s2 = sizeof_t_v; 128 | auto s3 = sizeof_t_v>; 129 | auto s4 = sizeof_t_v>; 130 | // s1 == 8 131 | // s2 == sizeof(int) 132 | // s3 == 3 133 | // s4 == 4 134 | 135 | // size of a tuple element 136 | auto t = tuple_element_size_v<2, std::tuple>; 137 | // t = sizeof(double) 138 | ``` 139 | 140 | ### Accumulate elements 141 | ```cpp 142 | // sum of a type 143 | using s1 = sum_t>; 144 | auto s2 = sum_v>; 145 | // s1 == c_7 146 | // s2 == 21 147 | 148 | // count the number of occurrences of an element 149 | auto c1 = count_v>; 150 | auto c2 = count_v>; 151 | // c1 == 2 152 | // c2 == 3 153 | 154 | // count the subtype of a sequence 155 | auto n1 = number_of_v, std::tuple>; 156 | auto n2 = number_of_v, std::tuple>; 157 | auto n3 = number_of_v, std::index_sequence<1, 5, 2, 7, 4, 2, 7, 2, 7>>; 158 | auto n4 = number_of_v, std::index_sequence<1, 5, 2, 7, 4, 2, 7, 2, 7>>; 159 | // n1 == 0 160 | // n2 == 2 161 | // n3 == 0 162 | // n4 == 3 163 | 164 | // count the number of elements satisfying specific criteria 165 | auto c3 = count_if_v>; 166 | auto c4 = count_if_v, float, std::tuple>>; 167 | // c3 == 3 168 | // c4 == 2 169 | 170 | // get the number of members in a struct 171 | struct foo 172 | { 173 | }; 174 | 175 | struct bar 176 | { 177 | int x; 178 | double y; 179 | }; 180 | 181 | auto N1 = aggregate_arity(); 182 | auto N2 = aggregate_arity_v; 183 | // N1 == 0 184 | // N2 == 2 185 | ``` 186 | 187 | ### Algorithms 188 | ```cpp 189 | /* 190 | * _ _ _ 191 | * | | | | | | 192 | * _| |_ | | | | 193 | * |_____| | | | | 194 | * |_______| | | | | 195 | * |_________| | | | | 196 | * |___________| | | | | 197 | * |_____________| | | | | 198 | * |_______________| _______| |_______ _______| |_______ 199 | * | | | | | | 200 | * ------------------------------------------------------------------------------- 201 | * #0 #1 #2 202 | */ 203 | 204 | // Tower of Hanoi 205 | using one_disk = hanoi_t<1>; 206 | using two_disks = hanoi_t<2>; 207 | using three_disks = hanoi_t<3>; 208 | /* 209 | * move from to 210 | * one_disk == std::integer_sequence 211 | * 212 | * two_disks == std::integer_sequence 215 | * 216 | * three_disks == std::integer_sequence 223 | */ 224 | 225 | // arrange the same elements adjacent in a sequence, keep the relative order 226 | using a1 = adjacent_t>; 227 | using a2 = adjacent_t>; 228 | // a1 == std::tuple 229 | // a2 == std::index_sequence<7, 7, 7, 2, 2, 0, 0, 3, 3, 8, 5> 230 | 231 | // Boyer-Moore-Horspool (BMH) string searching algorithm for types 232 | using b1 = bmh_t, std::tuple>; 233 | using b2 = bmh_t, std::tuple>; 234 | using b3 = bmh_t, 235 | std::tuple>; 236 | // b1 == std::index_sequence<> 237 | // b2 == std::index_sequence<0, 3> 238 | // b3 == std::index_sequence<1, 7> 239 | 240 | // Knuth–Morris–Pratt (KMP) string searching algorithm for types 241 | using k1 = kmp_t, std::tuple>; 242 | using k2 = kmp_t, std::tuple>; 243 | using k3 = kmp_t, 244 | std::tuple>; 245 | // k1 == std::index_sequence<> 246 | // k2 == std::index_sequence<0, 3> 247 | // k3 == std::index_sequence<1, 7> 248 | 249 | // Knuth–Morris–Pratt (KMP) string searching algorithm for values 250 | using k4 = kmp_t, 251 | std::integer_sequence>; 252 | using k5 = kmp_t, 253 | std::integer_sequence>; 254 | // k4 == std::index_sequence<3, 10> 255 | // k5 == std::index_sequence<> 256 | 257 | // stable partition in the range [begin, end) 258 | using p1 = typeof_t>, is_tuple>>; 259 | using p2 = typeof_t, double, 260 | std::tuple, int, std::tuple>, is_tuple>>; 261 | // p1 == std::tuple, int> 262 | // p2 == std::tuple, std::tuple, 263 | char, double, int, std::tuple> 264 | 265 | using p3 = typeof_t, is_odd>>; 266 | using p4 = typeof_t, is_even>>; 267 | // p3 == std::integer_sequence 268 | // p4 == std::integer_sequence 269 | 270 | // partition point in the range [begin, end) 271 | using p5 = partition_point_t; 272 | auto p6 = partition_point_v; 273 | // p5 == c_3 274 | // p6 == 4 275 | 276 | // partial sort in the range [begin, end) 277 | using p7 = partial_sort_t<0, 4, std::integer_sequence>; 278 | using p8 = partial_sort_t<4, 6, std::tuple>; 279 | // p7 == std::integer_sequence 280 | // p8 == std::tuple 281 | 282 | // sort in the subrange [begin, end) 283 | using su = sort_subrange_t<3, 6, std::index_sequence<3, 1, 4, 0, 2, 5, 9, 7, 4, 8>>; 284 | // su == std::index_sequence<0, 1, 2, 3, 4, 4, 9, 7, 8, 5> 285 | 286 | // partition in the subrange [begin, end) 287 | using ps = partition_subrange_t<2, 7, std::index_sequence<4, 1, 8, 7, 0, 5, 3, 6, 2>>; 288 | // ps == std::index_sequence<0, 1, 3, 2, 4, 5, 6, 7, 8> 289 | 290 | // get the range and sum of maximum subarray of a sequence 291 | using array = std::integer_sequence; 292 | 293 | using m1 = max_subarray_t; // iterative 294 | using m2 = find_maximum_subarray_t; // recursive 295 | using m3 = kadane_t; // maximum type 296 | auto m4 = kadane_v; // maximum value 297 | // m1 == std::integer_sequence 298 | // m2 == m1 299 | // m3 == c_7 300 | // m4 == 7 301 | // the elements in the range [2, 6] is the maximum subarray of array with sum 7 302 | 303 | // equal range 304 | using ins = std::integer_sequence; 305 | 306 | using equal_range1 = equal_range_t; 307 | using rng1 = range_t; 308 | // rng1 == std::integer_sequence 309 | 310 | using equal_range2 = equal_range_t; 311 | using rng2 = range_t; 312 | // rng2 == std::integer_sequence 313 | 314 | // edit distance 315 | auto ed1 = edit_distance_v, 316 | std::tuple>; 317 | auto ed2 = edit_distance_v, 318 | std::integer_sequence>; 319 | // ed1 == 3 320 | // ed2 == 3 321 | 322 | // set operations 323 | using s1 = set_difference_t, 324 | std::integer_sequence>; 325 | using s2 = set_symmetric_difference_t, 326 | std::integer_sequence>; 327 | using s3 = set_intersection_t, 328 | std::integer_sequence>; 329 | using s4 = set_union_t, 330 | std::integer_sequence>; 331 | // s1 == std::integer_sequence 332 | // s2 == std::integer_sequence 333 | // s3 == std::integer_sequence 334 | // s4 == std::integer_sequence 335 | 336 | // find 337 | auto f0 = find_v>; 338 | auto f1 = find_v>; 339 | // f0 == 1 340 | // f1 == 2 341 | 342 | // find backward 343 | auto f2 = find_backward_v>; 344 | auto f3 = find_backward_v>; 345 | // f2 == 4 346 | // f3 == 3 347 | 348 | // find not 349 | auto f4 = find_not_v>; 350 | auto f5 = find_not_v>; 351 | // f4 == 0 352 | // f5 == 1 353 | 354 | // find not backward 355 | auto f6 = find_not_backward_v>; 356 | auto f7 = find_not_backward_v>; 357 | // f6 == 3 358 | // f7 == 5 359 | 360 | // find if 361 | auto f8 = find_if_v, float>>; 362 | auto f9 = find_if_v>; 363 | // f8 == 2 364 | // f9 == 3 365 | 366 | // find if backward 367 | auto f10 = find_if_backward_v::template apply, std::integer_sequence>; 368 | auto f11 = find_if_backward_v>; 369 | // f10 == 6 370 | // f11 == 6 371 | 372 | // find if not 373 | auto f12 = find_if_not_v, char, std::tuple, float>>; 374 | auto f13 = find_if_not_v>; 375 | // f12 == 1 376 | // f13 == 0 377 | 378 | // find if not backward 379 | auto f14 = find_if_not_backward_v::template apply, std::integer_sequence>; 380 | auto f15 = find_if_not_backward_v>; 381 | // f14 == 5 382 | // f15 == 7 383 | 384 | // find first of 385 | auto f16 = find_first_of_v, std::tuple>; 386 | auto f17 = find_first_of_v, std::index_sequence<9, 8, 3, 6>>; 387 | // f16 == 1 388 | // f17 == 2 389 | 390 | // any_of: apply an unary predicate to the sequence in the range [begin, end) 391 | auto a1 = any_of_v, 0, 10>; 392 | auto a2 = any_of_v, double, 393 | std::tuple, int, std::tuple>, 0, 7>; 394 | // a1 == true 395 | // a2 == true 396 | 397 | // adjacent find: for a predicate f, where f(a[i], a[i+1]) returns true 398 | auto a3 = adjacent_find_v>; 399 | auto a4 = adjacent_find_v>; 400 | // a3 == 5 401 | // a4 == 2 402 | 403 | // adjacent difference: a[i] - a[i-1], a[i+1] - a[i], a[i+2] - a[i+1], ... 404 | using a5 = adjacent_difference_t>; 405 | // a5 == std::integer_sequence 406 | 407 | // mismatch 408 | using m = mismatch_t, 409 | std::integer_sequence>; 410 | // m == pair_v<3, 3> 411 | 412 | // includes 413 | auto i = includes_v, 414 | std::integer_sequence>; 415 | // i == true 416 | 417 | // search 418 | auto s5 = search_v, 419 | std::integer_sequence> 420 | // s5 == 2 421 | 422 | // find_end 423 | auto f = find_end_v, 424 | std::integer_sequence>; 425 | // f == 6 426 | 427 | // search_n 428 | auto s6 = search_n_v>; 429 | auto s7 = search_n_v>; 430 | // s6 == 3 431 | // s7 == 2 432 | 433 | // least common ancestor (lca) of two sequences, if not found, returns the first one 434 | using lca1 = lca_t, std::tuple>; 435 | using lca2 = lca_t, std::tuple>; 436 | using lca3 = lca_t, std::index_sequence<1, 3, 7, 9, 4, 5>>; 437 | auto lca4 = lca_v, std::index_sequence<0, 9, 4, 5>>; 438 | // lca1 == int 439 | // lca2 == int 440 | // lca3 == index_t<9> (std::integral_constant) 441 | // lca4 == 9 442 | 443 | // inclusive_scan 444 | using seq = std::integer_sequence; 445 | 446 | using in1 = inclusive_scan_t; 447 | using in2 = inclusive_scan_t; 448 | // in1 == std::integer_sequence 449 | // in2 == std::integer_sequence 450 | 451 | // exclusive_scan 452 | using ex1 = exclusive_scan_t; 453 | using ex2 = exclusive_scan_t; 454 | // ex1 == std::integer_sequence 455 | // ex2 == std::integer_sequence 456 | 457 | // transform_inclusive_scan 458 | using ti1 = transform_inclusive_scan_t; 459 | using ti2 = transform_inclusive_scan_t; 460 | // ti1 == std::integer_sequence 461 | // ti2 == std::integer_sequence 462 | 463 | // transform_exclusive_scan 464 | using te1 = transform_exclusive_scan_t; 465 | using te2 = transform_exclusive_scan_t; 466 | // te1 == std::integer_sequence 467 | // te2 == std::integer_sequence 468 | 469 | // sum two sequences 470 | using t = transmute_t, 471 | std::integer_sequence>; 472 | // t == std::integer_sequence 473 | 474 | // inner product 475 | auto in1 = inner_product_v, 476 | std::integer_sequence, c_0>; 477 | auto in2 = inner_product_v, 478 | std::integer_sequence, c_0>; 479 | // in1 == 2 480 | // in2 == 20 481 | 482 | // accumulate 483 | auto acc = accumulate_v>; 484 | // acc == -18 485 | 486 | // iota 487 | using io = iota_t, std::integer_sequence>; 488 | // io == std::integer_sequence 489 | 490 | // tokenize a number into a sequence of specific length (default in base ten with length one) 491 | constexpr auto N = 123456; 492 | using t1 = tokenize_t; 493 | using t2 = tokenize_t; 494 | using t3 = tokenize_t; 495 | // t1 == std::integer_sequence 496 | // t2 == std::integer_sequence 497 | // t3 == std::integer_sequence 498 | // tokenize_t == tokenize_t 499 | 500 | // multiply two large numbers that represented as two sequences 501 | using l = large_number_multiplier_t, tokenize_t<987>>; 502 | // l == std::integer_sequence 503 | // 984 * 987 == 971208 504 | 505 | // starts with 506 | auto sw = starts_with_v, std::tuple>; 507 | // sw == true 508 | 509 | // ends with 510 | auto ew = ends_with_v, std::index_sequence<4, 2>>; 511 | // ew == true 512 | 513 | // make a nested typelist flat 514 | using nested = std::tuple, double, 515 | std::tuple>, short>; 516 | using ft = flat_t; 517 | // ft == std::tuple 518 | 519 | // reorder elements with index sequence order 520 | // apply_permutation == apply_permutation 521 | // apply_permutation == apply_permutation, reverse_t> 522 | using types1 = std::tuple; 523 | using types2 = std::integer_sequence; 524 | using index1 = std::index_sequence<0, 2, 4, 1, 3>; 525 | using index2 = std::index_sequence<4, 0, 2, 1, 3>; 526 | 527 | using ap1 = apply_permutation_t; 528 | using ap2 = apply_permutation_t; 529 | // ap1 == std::tuple 530 | // ap2 == std::integer_sequence 531 | 532 | using ap3 = apply_permutation_t; 533 | using ap4 = apply_permutation_t; 534 | // ap3 == std::tuple 535 | // ap4 == std::integer_sequence 536 | 537 | // divides a sequence into several parts according to given criteria 538 | using sp1 = split_t, float, std::tuple<>>>; 539 | using sp2 = split_t>; 540 | // sp1 == std::tuple, std::tuple> 541 | // sp2 == std::tuple, std::index_sequence<9>, std::index_sequence<5>> 542 | ``` 543 | 544 | ### Combinations 545 | ```cpp 546 | // next combination of types C(4, 2) 547 | using type0 = std::tuple; // range_t<0, 2, type0> == std::tuple 548 | using type1 = next_combination_t<2, type0>; // range_t<0, 2, type1> == std::tuple 549 | using type2 = next_combination_t<2, type1>; // range_t<0, 2, type2> == std::tuple 550 | using type3 = next_combination_t<2, type2>; // range_t<0, 2, type3> == std::tuple 551 | using type4 = next_combination_t<2, type3>; // range_t<0, 2, type4> == std::tuple 552 | using type5 = next_combination_t<2, type4>; // range_t<0, 2, type5> == std::tuple 553 | 554 | using next = next_combination_list<2, type0>; 555 | /* 556 | * next == std::tuple, 557 | * std::tuple, 558 | * std::tuple, 559 | * std::tuple, 560 | * std::tuple, 561 | * std::tuple> 562 | */ 563 | 564 | // prev combination of types C(4, 2) 565 | using prev = prev_combination_list<2, std::tuple>; 566 | /* 567 | * prev == std::tuple, 568 | * std::tuple, 569 | * std::tuple, 570 | * std::tuple, 571 | * std::tuple, 572 | * std::tuple> 573 | */ 574 | 575 | // next combination of values C(4, 2) 576 | using val1 = next_combination_list<2, std::integer_sequence>; 577 | /* 578 | * val1 == std::tuple, 579 | * std::integer_sequence, 580 | * std::integer_sequence, 581 | * std::integer_sequence, 582 | * std::integer_sequence, 583 | * std::integer_sequence>> 584 | */ 585 | 586 | // next combination of values C(5, 3) 587 | using val2 = next_combination_list<3, std::integer_sequence>; 588 | /* 589 | * val2 == std::tuple, 590 | * std::integer_sequence, 591 | * std::integer_sequence, 592 | * std::integer_sequence, 593 | * std::integer_sequence, 594 | * std::integer_sequence, 595 | * std::integer_sequence, 596 | * std::integer_sequence, 597 | * std::integer_sequence, 598 | * std::integer_sequence> 599 | */ 600 | 601 | // next combination counts x + y + z = 2 602 | using next_counts = next_combination_counts_list<0, 3, std::integer_sequence>; 603 | /* 604 | * next_counts = std::tuple, 605 | * std::integer_sequence, 606 | * std::integer_sequence, 607 | * std::integer_sequence, 608 | * std::integer_sequence, 609 | * std::integer_sequence> 610 | */ 611 | 612 | // prev combination counts x + y + z = 2 613 | using prev_counts = prev_combination_counts_list<0, 3, std::integer_sequence>; 614 | // prev_counts == reverse_t 615 | 616 | // hypercube indices 617 | /* 618 | * for i1 in [m..n] 619 | * for i2 in [m..n] 620 | * for i3 in [m..n] 621 | * . 622 | * . 623 | * . 624 | * for ik in [m..n] 625 | * [i1..ik] 626 | */ 627 | 628 | // next hypercube indices for k = 3, m = 1, n = 2 629 | using next_hyper = next_hypercube_indices_list<1, 3, std::integer_sequence>; 630 | /* 631 | * next_hyper == std::tuple, 632 | * std::integer_sequence, 633 | * std::integer_sequence, 634 | * std::integer_sequence, 635 | * std::integer_sequence, 636 | * std::integer_sequence, 637 | * std::integer_sequence, 638 | * std::integer_sequence> 639 | */ 640 | 641 | // prev hypercube indices for k = 3, m = 1, n = 2 642 | using prev_hyper = prev_hypercube_indices_list<1, 3, std::integer_sequence>; 643 | // prev_hyper == reverse_t 644 | ``` 645 | 646 | ### Currying 647 | In mathematics and computer science, currying is the technique of translating the evaluation of a function 648 | that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument. 649 | 650 | Partial application refers to the process of fixing a number of arguments to a function, producing another function of smaller arity. 651 | It is a generalized form of currying: instead of binding one argument and getting (arity - 1) unary functions back, 652 | it can bind n arguments at once and get another partially-applicable function with (arity - n) arity. 653 | 654 | Currying is a special form of partial application where you can only bind a single argument at a time. 655 | The result is another function with exactly 1 fewer parameter. 656 | 657 | ```cpp 658 | #include 659 | #include 660 | using namespace monster; 661 | 662 | int main(int argc, char* argv[]) 663 | { 664 | auto sum = [](auto a, auto b, auto c, auto d, auto e, auto f) 665 | { 666 | return a + b + c + d + e + f; 667 | }; 668 | 669 | auto expected = sum(0, 1, 2, 3, 4, 5); 670 | 671 | auto s0 = curry(sum)(0, 1, 2, 3, 4, 5); 672 | auto s1 = curry(sum)(0)(1, 2, 3, 4, 5); 673 | auto s2 = curry(sum)(0, 1)(2, 3, 4, 5); 674 | auto s3 = curry(sum)(0, 1, 2)(3, 4, 5); 675 | auto s4 = curry(sum)(0, 1, 2, 3)(4, 5); 676 | auto s5 = curry(sum)(0, 1, 2, 3, 4)(5); 677 | 678 | auto t0 = curry(sum, std::make_tuple())(0, 1, 2, 3, 4, 5); 679 | auto t1 = curry(sum, std::make_tuple(0))(1, 2, 3, 4, 5); 680 | auto t2 = curry(sum, std::make_tuple(0, 1))(2, 3, 4, 5); 681 | auto t3 = curry(sum, std::make_tuple(0, 1, 2))(3, 4, 5); 682 | auto t4 = curry(sum, std::make_tuple(0, 1, 2, 3))(4, 5); 683 | auto t5 = curry(sum, std::make_tuple(0, 1, 2, 3, 4))(5); 684 | auto t6 = curry(sum, std::make_tuple(0, 1, 2, 3, 4, 5))(); 685 | 686 | assert(s0 == expected); 687 | assert(s1 == expected); 688 | assert(s2 == expected); 689 | assert(s3 == expected); 690 | assert(s4 == expected); 691 | assert(s5 == expected); 692 | 693 | assert(t0 == expected); 694 | assert(t1 == expected); 695 | assert(t2 == expected); 696 | assert(t3 == expected); 697 | assert(t4 == expected); 698 | assert(t5 == expected); 699 | assert(t6 == expected); 700 | 701 | return expected; 702 | } 703 | ``` 704 | 705 | ### Delete elements 706 | ```cpp 707 | // delete the first element 708 | using p1 = tail_t>; 709 | using p2 = tail_t>; 710 | // p1 == std::tuple 711 | // p2 == std::integer_sequence 712 | 713 | // delete the last element 714 | using p3 = head_t>; 715 | using p4 = head_t>; 716 | // p3 == std::tuple 717 | // p4 == std::integer_sequence 718 | 719 | // delete an element at specific index 720 | using d1 = erase_at_t<2, std::tuple>; 721 | using d2 = erase_at_t<1, std::integer_sequence>; 722 | // d1 == std::tuple 723 | // d2 == std::integer_sequence 724 | 725 | // delete an element of specific type or value 726 | using d3 = remove_copy_t>; 727 | using d4 = remove_copy_t>; 728 | using d5 = remove_copy_c<4, std::integer_sequence>; 729 | // d3 == std::tuple 730 | // d4 == std::integer_sequence 731 | // d5 == std::integer_sequence 732 | 733 | // delete elements in the range [begin, end) 734 | using d6 = erase_t<2, 5, std::tuple>; 735 | using d7 = erase_t<0, 6, std::tuple>; 736 | using d8 = erase_t<1, 4, std::integer_sequence>; 737 | using d9 = erase_t<0, 6, std::integer_sequence>; 738 | // d6 == std::tuple 739 | // d7 == std::tuple<> 740 | // d8 == std::integer_sequence 741 | // d9 == std::integer_sequence 742 | 743 | // delete subtype of a sequence 744 | using tpl = std::tuple; 745 | using tp2 = std::index_sequence<2, 0, 3, 5, 4, 4, 7, 3, 5, 4, 4, 3, 5, 4>; 746 | using es1 = erase_subtype_t, tpl>; 747 | using es2 = erase_subtype_t, tp2>; 748 | using es3 = eliminate_subtype_t, tpl>; 749 | using es4 = eliminate_subtype_t, tp2>; 750 | // es1 == std::tuple 751 | // es2 == std::index_sequence<2, 0, 4, 7, 4> 752 | // es3 == std::tuple 753 | // es4 == std::index_sequence<2, 0, 3, 7, 3, 3, 5, 4> 754 | 755 | // delete the first two elements 756 | using x = drop_t<2, std::tuple>; 757 | // x == std::tuple 758 | 759 | // delete the last two elements 760 | using y = drop_last_t<2, std::integer_sequence>; 761 | // y == std::integer_sequence 762 | 763 | // delete all duplicate elements, keep the first appearance 764 | using u1 = unique_t>; 765 | using u2 = unique_t>; 766 | // u1 == std::tuple 767 | // u2 == std::integer_sequence 768 | 769 | // keep unique elements 770 | using ue1 = unique_elements_t>; 771 | using ue2 = unique_elements_t>; 772 | // ue1 == std::tuple 773 | // ue2 == std::index_sequence<0, 3, 2> 774 | 775 | // keep duplicate elements 776 | using de1 = duplicate_elements_t>; 777 | using de2 = duplicate_elements_t>; 778 | // de1 == std::tuple 779 | // de2 == std::index_sequence<5, 7> 780 | 781 | // remove unique elements, keep the relative order 782 | using ru1 = remove_unique_t>; 783 | using ru2 = remove_unique_t>; 784 | // ru1 == std::tuple 785 | // ru2 == std::index_sequence<8, 8, 2, 2, 0, 0, 0, 7, 7> 786 | 787 | // remove duplicate elements, keep the relative order 788 | using rd1 = remove_duplicate_t>; 789 | using rd2 = remove_duplicate_t>; 790 | // rd1 == std::tuple 791 | // rd2 == std::index_sequence<4, 9, 6> 792 | 793 | // delete all indices with duplicate elements, keep the last appearance 794 | using u3 = unique_index_t>; 795 | using u4 = unique_index_t>; 796 | // u3 == std::index_sequence<1, 2, 4, 5> 797 | // u4 == u3 798 | 799 | // delete all duplicate elements satisfying specific criteria 800 | // for a type T, is_pointer_of_v == true 801 | using u5 = unique_if_t>; 802 | // u5 == std::tuple 803 | 804 | // delete all elements satisfying specific criteria 805 | using value = std::integer_sequence; 806 | using type = std::tuple, double, std::tuple, float, char>; 807 | 808 | using r1 = erase_if_t; 809 | using r2 = erase_if_not_t; 810 | using r3 = erase_if_t; 811 | using r4 = erase_if_t; 812 | // r1 == std::tuple 813 | // r2 == std::tuple, std::tuple> 814 | // r3 == std::integer_sequence 815 | // r4 == std::integer_sequence 816 | 817 | // delete all elements satisfying specific criteria in the range [begin, end) 818 | using r5 = remove_if_t; 819 | using r6 = remove_if_t; 820 | using r7 = remove_if_t; 821 | using r8 = remove_if_not_t; 822 | // r7 == std::tuple 823 | // r8 == std::tuple, std::tuple, float, char> 824 | // r5 == std::integer_sequence 825 | // r6 == std::integer_sequence 826 | 827 | // copy the elements in the range [begin, end) for which the predicate F returns true 828 | // copy_if == copy_if> 829 | using type1 = std::integer_sequence; 830 | using type2 = std::tuple, double, std::tuple, float, char>; 831 | using ci1 = copy_if_t; 832 | using ci2 = copy_if_t; 833 | // ci1 == std::integer_sequence 834 | // ci2 == std::tuple, std::tuple> 835 | 836 | // delete elements at specific sorted index sequence 837 | using indices = std::index_sequence<0, 1, 3, 4>; 838 | using e1 = exclude_t, indices>; 839 | using e2 = exclude_t, indices>; 840 | // e1 == std::tuple 841 | // e2 == std::integer_sequence 842 | ``` 843 | 844 | ### Expand sequences 845 | **expand** provides indexed access to the elements of a list with specific index sequence, 846 | it takes a accessor `F`, a list `L`, an index sequence `I`, and an index offset `O` which is 847 | optional (default value is 0). 848 | 849 | **expand** is called like this `expand` or `expand`. 850 | 851 | ```cpp 852 | using list1 = std::tuple; 853 | using list2 = std::integer_sequence; 854 | 855 | // get the elements of the list at indices 0, 2 856 | using e1 = expand_t>; 857 | using e2 = expand_t>; 858 | // e1 == std::tuple 859 | // e2 == std::integer_sequence 860 | 861 | // get the elements of the list at indices 0, 2 with offset 1, 862 | // it's same as get the elements of the list at indices 1, 3 without offset 863 | using e3 = expand_t, 1>; 864 | using e4 = expand_t, 1>; 865 | // e3 == std::tuple 866 | // e4 == std::integer_sequence 867 | 868 | // element combines std::tuple_element and get as a single accessor 869 | using e5 = expand_t>; 870 | using e6 = expand_t>; 871 | // e5 == std::tuple 872 | // e6 == std::integer_sequence 873 | // e6 == reverse_t 874 | 875 | // element is the default accessor of expand_of 876 | using list3 = std::tuple; 877 | using list4 = std::integer_sequence 878 | 879 | using e7 = expand_of_t>; 880 | using e8 = expand_of_t>; 881 | // e7 == std::tuple 882 | // e8 == std::integer_sequence 883 | // e7 == even_t 884 | // e8 == odd_t> 885 | ``` 886 | 887 | ### Generate sequences 888 | ```cpp 889 | // generate a list 890 | using f1 = fill_t<3, int>; 891 | using f2 = fill_t<4, c_1>; 892 | using f3 = fill_c<5, 2>; 893 | using f4 = integral_range_t; 894 | using f5 = index_range<2, 7>; 895 | using f6 = zero_sequence_t; 896 | // f1 == std::tuple 897 | // f2 == std::integer_sequence 898 | // f3 == std::integer_sequence 899 | // f4 == index_list 900 | // f5 == index_list 901 | // f6 == std::integer_sequence 902 | 903 | // integer sequence {a, a + d, a + 2d, a + 3d, ...} 904 | using i1 = integer_sequence_t; 905 | using i2 = integer_sequence_t; 906 | using i3 = integer_sequence_t; 907 | // i1 == std::integer_sequence 908 | // i2 == std::integer_sequence 909 | // i3 == std::integer_sequence 910 | 911 | // concat elements 912 | using c1 = concat_t, std::tuple>; 913 | using c2 = concat_t, std::integer_sequence>; 914 | // c1 == std::tuple 915 | // c2 == std::integer_sequence 916 | 917 | // cartesian product 918 | using c3 = cartesian_product_t, std::tuple>; 919 | using c4 = cartesian_product_t, std::integer_sequence>; 920 | // c3 == std::tuple 921 | // c4 == std::integer_sequence 922 | 923 | // descartes product 924 | std::vector v1 { 1, 2, 3, 4 }; 925 | std::vector v2 { 9.8f, 4.4f }; 926 | std::vector v3 { "r", "g", "b" }; 927 | 928 | descartes_product([](int x, float y, const std::string& z) 929 | { 930 | std::cout << x << " " << y << " " << z << std::endl; 931 | }, v1, v2, v3); 932 | 933 | // print elements from v1 x v2 x v3 934 | 935 | // matrix operatings 936 | using matrix = std::tuple, 937 | std::tuple, 938 | std::tuple, 939 | std::tuple, 940 | std::tuple, 941 | std::tuple>; 942 | 943 | using m = get_matrix_element_t<3, 1, matrix>; 944 | using n = set_matrix_element_t<3, 1, nullptr_t, matrix>; 945 | // m == double 946 | // get_matrix_element_t<3, 1, n> == nullptr_t 947 | 948 | using t = matrix_transpose_t; 949 | // t == std::tuple, 950 | std::tuple, 951 | std::tuple> 952 | 953 | // matrix_transpose_t> == matrix 954 | 955 | using row1 = get_matrix_row_t<1, matrix>; 956 | using col1 = get_matrix_col_t<1, matrix>; 957 | // row1 == std::tuple 958 | // col1 == std::tuple 959 | 960 | using numbers = std::tuple, 961 | std::integer_sequence, 962 | std::integer_sequence, 963 | std::integer_sequence, 964 | std::integer_sequence, 965 | std::integer_sequence>; 966 | 967 | using row2 = set_matrix_row_t<3, std::integer_sequence, numbers>; 968 | using col2 = set_matrix_col_t<1, std::integer_sequence, numbers>; 969 | // row2 == std::tuple, 970 | std::integer_sequence, 971 | std::integer_sequence, 972 | std::integer_sequence, 973 | std::integer_sequence, 974 | std::integer_sequence> 975 | 976 | // col2 == std::tuple, 977 | std::integer_sequence, 978 | std::integer_sequence, 979 | std::integer_sequence, 980 | std::integer_sequence, 981 | std::integer_sequence> 982 | 983 | // zip elements 984 | using z1 = zip_t, std::tuple>; 985 | using z2 = zip_t, std::integer_sequence>; 986 | using z3 = zip_t, std::integer_sequence>; 987 | // z1 == std::tuple 988 | // z2 == std::integer_sequence 989 | // z3 == std::integer_sequence 990 | 991 | // zip with 992 | using z4 = zip_with_t, 993 | std::integer_sequence>; 994 | using z5 = zip_with_t, std::tuple>; 995 | // z4 == std::integer_sequence 996 | // z5 == std::tuple, pair_t, pair_t> 997 | 998 | // call a function n times 999 | using p = nth_level_pointer_t<4, int>; 1000 | using i = call_ntimes_t<4, int, std::add_pointer>; 1001 | // p == int**** 1002 | // i == int**** 1003 | 1004 | // takes a single element in a sequence and replicates it to 1005 | // create another sequence with some number of copies of that element 1006 | using s1 = splat_t<2, 4, std::integer_sequence>; 1007 | using s2 = splat_t<3, 5, std::tuple>; 1008 | // s1 == std::integer_sequence 1009 | // s2 == std::tuple 1010 | 1011 | ``` 1012 | 1013 | ### Insert elements 1014 | ```cpp 1015 | // insert elements at specific index 1016 | using i1 = insert_t<2, std::tuple, float, char>; 1017 | using i2 = insert_t<1, std::integer_sequence, c_0, c_4>; 1018 | using i3 = insert_c<0, std::integer_sequence, 5, 6, 7>; 1019 | // i1 == std::tuple 1020 | // i2 == std::integer_sequence 1021 | // i3 == std::integer_sequence 1022 | 1023 | // insert at specific index with elements in the range [begin, end) 1024 | using i4 = insert_range_t<2, std::tuple, std::tuple, 0, 2>; 1025 | using i5 = insert_range_t<1, std::integer_sequence, 1026 | std::integer_sequence, 1, 3>; 1027 | // i4 == std::tuple 1028 | // i5 == std::integer_sequence 1029 | 1030 | ``` 1031 | ### Loops 1032 | ```cpp 1033 | // compile time for 1034 | for_value<1, -2, 3>([]() 1035 | { 1036 | std::cout<< N << std::endl; 1037 | }); 1038 | 1039 | for_range<4, 7>([]() 1040 | { 1041 | std::cout<< N << std::endl; 1042 | }); 1043 | 1044 | for_type([]() 1045 | { 1046 | std::cout << N << " " << typeid(T).name() << std::endl; 1047 | }); 1048 | 1049 | // call a function N times 1050 | loop<3>([](auto N, auto&& v) 1051 | { 1052 | std::cout << N << " " << v << std::endl; 1053 | }, 4); 1054 | 1055 | // call a function within a nested loop with the loop indices 1056 | // as arguments (optional) in ascending or descending order 1057 | 1058 | loop_for(indices...)(f); 1059 | 1060 | // ascending order 1061 | loop_for(2, 3, 4) 1062 | ( 1063 | [](auto i, auto j, auto k) 1064 | { 1065 | std::cout << '(' << i << ", " << j << ", " << k << ")" << std::endl; 1066 | } 1067 | ); 1068 | 1069 | /* This is equivalent to the following: 1070 | for (int i = 0; i != 2; ++i) 1071 | for (int j = 0; j != 3; ++j) 1072 | for (int k = 0; k != 4; ++k) 1073 | f(i, j, k); 1074 | */ 1075 | 1076 | // descending order 1077 | loop_for(2, 3, 4) 1078 | ( 1079 | [](auto i, auto j, auto k) 1080 | { 1081 | std::cout << '(' << i << ", " << j << ", " << k << ")" << std::endl; 1082 | } 1083 | ); 1084 | 1085 | /* This is equivalent to the following: 1086 | for (int i = 2 - 1; i >=0; --i) 1087 | for (int j = 3 - 1; j >= 0; --j) 1088 | for (int k = 4 - 1; k >= 0; --k) 1089 | f(i, j, k); 1090 | */ 1091 | 1092 | // f can take fewer and even empty arguments than the loop depth 1093 | loop_for(2, 3, 4) 1094 | ( 1095 | [](auto i, auto j) 1096 | { 1097 | std::cout << '(' << i << ", " << j << ")" << std::endl; 1098 | } 1099 | ); 1100 | 1101 | loop_for(2, 3, 4) 1102 | ( 1103 | [] 1104 | { 1105 | std::cout << "don't use the loop index at all" << std::endl; 1106 | } 1107 | ); 1108 | 1109 | iterate<4>([] 1110 | { 1111 | std::cout << "x" << std::endl; 1112 | }); 1113 | 1114 | unroller<3> unroll; 1115 | 1116 | unroll([](auto&&... args) 1117 | { 1118 | (std::cout << ... << args) << std::endl; 1119 | }, 1, 2.3, "[](){}();"); 1120 | 1121 | // fmap(f, Args...) 1122 | // invoke the callable object f with each argument of the parameter pack Args... independently 1123 | // fmap(f, Arg1, Arg2, Arg3, ..., ArgN) is equivalent to f(Arg1); f(Arg2); f(Args3); ...; f(ArgN); 1124 | fmap([](auto& v) 1125 | { 1126 | std::cout << v << std::endl; 1127 | }, 1, 2.3, "string", 'x'); 1128 | ``` 1129 | 1130 | ### Metafunctions 1131 | **bind_front**, bind arguments `Args...` to the front parameters of a function `F` in a a member template `apply` 1132 | that can be later called with extra arguments `args...`. 1133 | In other words, `bind_front::template apply` is equivalent to `F`. 1134 | 1135 | **bind_front** is defined as: 1136 | ```cpp 1137 | template