1 /******************************************************************************* 2 3 Smart enum template class, encapsulates an enum with a map of its codes 4 and descriptions. 5 6 Contains two main mixin templates: 7 8 1. SmartEnum 9 2. AutoSmartEnum 10 11 1. SmartEnum 12 ---------------------------------------------------------------------------- 13 14 Mixin template declaring a class containing an enum with code<->description 15 lookup. All methods are static, so that the class can be used without an 16 instance. 17 18 This mixin creates classes which contain a real (anonymous) enum, and two 19 associative arrays mapping from the enum values to their string descriptions 20 and vice versa, allowing two-way lookup between codes <-> descriptions, and 21 an implementation of the 'in' operator to tell if a code or description is 22 valid. 23 24 This is especially useful for "Const" classes which define a list of command 25 or status codes where a kind of reverse lookup is needed, going from a code 26 to its name / description (or vice versa). 27 28 The classes also implement a foreach iterator over the values of the enum, 29 and several other methods (see SmartEunmCore, below). 30 31 The mixin template takes as parameters the name of the class to be generated 32 and a variadic list of SmartEnumValue structs, specifying the names and 33 values of the enum's members. 34 35 The SmartEnumValue struct is also a template, which takes as parameter the 36 base type of the enum, which must be an integral type (see 37 http://www.digitalmars.com/d/1.0/enum.html). 38 39 Note that as the class generated by the mixin template contains only static 40 members, it is not necessary to actually instantiate it, only to declare the 41 class (as demonstrated in the example below). 42 43 Usage example: 44 45 --- 46 47 import ocean.core.SmartEnum; 48 import ocean.io.Stdout; 49 50 alias SmartEnumValue!(int) Code; 51 52 mixin(SmartEnum!("Commands", 53 Code("first", 1), 54 Code("second", 2) 55 )); 56 57 // Getting descriptions of codes 58 // (Note that the getter methods work like opIn, returning pointers.) 59 Stdout.formatln("Description for code first = {}", *Commands.description(Commands.first)); 60 Stdout.formatln("Description for code 1 = {}", *Commands.description(1)); 61 62 // Getting codes by description 63 // (Note that the getter methods work like opIn, returning pointers.) 64 Stdout.formatln("Code for description 'first' = {}", *Commands.code("first")); 65 66 // Testing whether codes exist 67 Stdout.formatln("1 in enum? {}", !!(1 in Commands)); 68 Stdout.formatln("first in enum? {}", !!(Commands.first in Commands)); 69 70 // Testing whether codes exist by description 71 Stdout.formatln("'first' in enum? {}", !!("first" in Commands)); 72 Stdout.formatln("'third' in enum? {}", !!("third" in Commands)); 73 74 // Iteration over enum 75 foreach ( code, descr; Commands ) 76 { 77 Stdout.formatln("{} -> {}", code, descr); 78 } 79 80 --- 81 82 2. AutoSmartEnum 83 ---------------------------------------------------------------------------- 84 85 Template to automatically create a SmartEnum from a list of strings. The 86 enum's base type is specified, and the enum values are automatically 87 numbered, starting at 0. 88 89 Usage example: 90 91 --- 92 93 import ocean.core.SmartEnum; 94 95 mixin(AutoSmartEnum!("Commands", int, 96 "first", 97 "second")); 98 99 --- 100 101 Copyright: 102 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 103 All rights reserved. 104 105 License: 106 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 107 Alternatively, this file may be distributed under the terms of the Tango 108 3-Clause BSD License (see LICENSE_BSD.txt for details). 109 110 *******************************************************************************/ 111 112 module ocean.core.SmartEnum; 113 114 115 116 import ocean.transition; 117 118 public import ocean.core.Enforce; 119 120 import CTFE = ocean.meta.codegen.CTFE; 121 122 import ocean.core.Tuple; 123 import ocean.core.Verify; 124 125 version (UnitTest) 126 { 127 import ocean.core.Test; 128 } 129 130 131 /******************************************************************************* 132 133 Abstract base class for SmartEnums. Contains no members, just provided as a 134 convenient way of checking that a class is in fact a SmartEnum, using 135 is(T : ISmartEnum). 136 137 *******************************************************************************/ 138 139 public abstract class ISmartEnum 140 { 141 } 142 143 144 145 /******************************************************************************* 146 147 Struct template representing a single member of an enum -- containing a 148 string for the enum identifier and a code for the corresponding value. 149 150 Params: 151 T = base type of enum 152 153 *******************************************************************************/ 154 155 public struct SmartEnumValue ( T ) 156 { 157 alias T BaseType; 158 159 istring name; 160 T value; 161 } 162 163 unittest 164 { 165 alias SmartEnumValue!(int) _; 166 } 167 168 169 170 /******************************************************************************* 171 172 Members forming the core of each class generated by the SmartEnum mixin. 173 This template is mixed into each class created by the SmartEnum template. 174 175 Params: 176 BaseType = base type of enum 177 178 *******************************************************************************/ 179 180 public template SmartEnumCore ( BaseType ) 181 { 182 import ocean.transition; 183 184 /*************************************************************************** 185 186 Two way mapping between codes <-> descriptions. 187 188 ***************************************************************************/ 189 190 static public TwoWayMap!(BaseType) map; 191 192 193 /*************************************************************************** 194 195 Looks up the description of a code. 196 197 Aliased to opIn. 198 199 Params: 200 code = code to look up 201 202 Returns: 203 pointer to code's description, or null if code not in enum 204 205 ***************************************************************************/ 206 207 static public istring* description ( BaseType code ) 208 { 209 return code in map; 210 } 211 212 public alias description opIn_r; 213 214 215 /*************************************************************************** 216 217 Looks up the code of a description. 218 219 Aliased to opIn. 220 221 Params: 222 description = description to look up 223 224 Returns: 225 pointer to description's code, or null if description not in enum 226 227 ***************************************************************************/ 228 229 static public BaseType* code ( cstring description ) 230 { 231 return description in map; 232 } 233 234 public alias code opIn_r; 235 236 237 /*************************************************************************** 238 239 Gets the description of a code. 240 241 Params: 242 code = code to look up 243 244 Returns: 245 code's description 246 247 Throws: 248 if code is not in the map 249 250 ***************************************************************************/ 251 252 static public istring opIndex ( BaseType code ) 253 { 254 auto description = code in map; 255 enforce(description, "code not found in SmartEnum"); 256 return *description; 257 } 258 259 260 /*************************************************************************** 261 262 Gets the code of a description. 263 264 Params: 265 description = description to look up 266 267 Returns: 268 description's code 269 270 Throws: 271 if description is not in the map 272 273 ***************************************************************************/ 274 275 static public BaseType opIndex ( cstring description ) 276 { 277 auto code = description in map; 278 enforce(code, cast(istring)(description ~ " not found in SmartEnum")); 279 return *code; 280 } 281 282 283 /*************************************************************************** 284 285 Looks up the index of a code in the enum (ie code is the nth code in the 286 enum). This can be useful if the actual enum codes are not consecutive. 287 288 Params: 289 code = code to get index for 290 291 Returns: 292 pointer to code's index, or null if code not in enum 293 294 ***************************************************************************/ 295 296 static public size_t* indexOf ( BaseType code ) 297 { 298 return map.indexOf(code); 299 } 300 301 302 /*************************************************************************** 303 304 Looks up the index of a description in the enum (ie description is for 305 the nth code in the enum). 306 307 Params: 308 description = description to get index for 309 310 Returns: 311 pointer to description's index, or null if description not in enum 312 313 ***************************************************************************/ 314 315 static public size_t* indexOf ( cstring description ) 316 { 317 return map.indexOf(description); 318 } 319 320 321 /*************************************************************************** 322 323 Looks up a code in the enum by its index. (ie gets the nth code in the 324 enum). 325 326 Params: 327 index = index of code to get 328 329 Returns: 330 nth code in enum 331 332 Throws: 333 array out of bounds if index is > number of codes in the enum 334 335 ***************************************************************************/ 336 337 static public BaseType codeFromIndex ( size_t index ) 338 { 339 return map.keys[index]; 340 } 341 342 343 /*************************************************************************** 344 345 Looks up a description in the enum by the index of its code. (ie gets 346 the description of the nth code in the enum). 347 348 Params: 349 index = index of code to get description for 350 351 Returns: 352 description of nth code in enum 353 354 Throws: 355 array out of bounds if index is > number of codes in the enum 356 357 ***************************************************************************/ 358 359 static public istring descriptionFromIndex ( size_t index ) 360 { 361 return map.values[index]; 362 } 363 364 365 /*************************************************************************** 366 367 foreach iterator over the codes and descriptions of the enum. 368 369 ***************************************************************************/ 370 371 static public int opApply ( scope int delegate ( ref BaseType code, ref istring desc ) dg ) 372 { 373 int res; 374 foreach ( code, description; map ) 375 { 376 res = dg(code, description); 377 } 378 return res; 379 } 380 381 382 /*************************************************************************** 383 384 foreach iterator over the codes and descriptions of the enum and their 385 indices. 386 387 ***************************************************************************/ 388 389 static public int opApply ( scope int delegate ( ref size_t index, ref BaseType code, ref istring desc ) dg ) 390 { 391 int res; 392 foreach ( index, code, description; map ) 393 { 394 res = dg(index, code, description); 395 } 396 return res; 397 } 398 } 399 400 unittest 401 { 402 alias SmartEnumCore!(int) _; 403 } 404 405 /******************************************************************************* 406 407 Template to mixin a comma separated list of SmartEnumValues. 408 409 Params: 410 T = variadic list of one or more SmartEnumValues 411 412 Generates output of the form: 413 414 --- 415 first = 1, 416 second = 2, 417 last = 100 418 --- 419 420 *******************************************************************************/ 421 422 private template EnumValuesList ( T ... ) 423 { 424 static if ( T.length == 1 ) 425 { 426 static immutable EnumValuesList = T[0].name ~ "=" ~ CTFE.toString(T[0].value); 427 } 428 else 429 { 430 static immutable EnumValuesList = T[0].name ~ "=" ~ CTFE.toString(T[0].value) ~ "," ~ EnumValuesList!(T[1..$]); 431 } 432 } 433 434 435 /******************************************************************************* 436 437 Template to mixin an enum from a list of SmartEnumValues. 438 439 Params: 440 T = variadic list of one or more SmartEnumValues 441 442 Generates output of the form: 443 444 --- 445 enum 446 { 447 first = 1, 448 second = 2, 449 last = 100 450 } 451 --- 452 453 *******************************************************************************/ 454 455 private template DeclareEnum ( T ... ) 456 { 457 static immutable DeclareEnum = "alias " ~ T[0].BaseType.stringof ~ " BaseType; enum : BaseType {" ~ EnumValuesList!(T) ~ "} "; 458 } 459 460 461 /******************************************************************************* 462 463 Template to mixin a series of TwoWayMap value initialisations. 464 465 Params: 466 T = variadic list of one or more SmartEnumValues 467 468 Generates output of the form: 469 470 --- 471 map["first"]=1; 472 map["second"]=2; 473 map["last"]=100; 474 --- 475 476 *******************************************************************************/ 477 478 private template InitialiseMap ( T ... ) 479 { 480 static if ( T.length == 1 ) 481 { 482 static immutable InitialiseMap = `map["` ~ T[0].name ~ `"]=` ~ T[0].name ~ ";"; 483 } 484 else 485 { 486 static immutable InitialiseMap = `map["` ~ T[0].name ~ `"]=` ~ T[0].name ~ ";" ~ InitialiseMap!(T[1..$]); 487 } 488 } 489 490 491 /******************************************************************************* 492 493 Template to mixin a static constructor which initialises and rehashes a 494 TwoWayMap. 495 496 Params: 497 T = variadic list of one or more SmartEnumValues 498 499 Generates output of the form: 500 501 --- 502 static this ( ) 503 { 504 map["first"]=1; 505 map["second"]=2; 506 map["last"]=100; 507 508 map.rehash; 509 } 510 --- 511 512 *******************************************************************************/ 513 514 private template StaticThis ( T ... ) 515 { 516 static immutable StaticThis = "static this() {" ~ InitialiseMap!(T) ~ "map.rehash;} "; 517 } 518 519 520 /******************************************************************************* 521 522 Template to find the maximum code from a list of SmartEnumValues. 523 524 Params: 525 T = variadic list of one or more SmartEnumValues 526 527 *******************************************************************************/ 528 529 private template MaxValue ( T ... ) 530 { 531 static if ( T.length == 1 ) 532 { 533 static immutable MaxValue = T[0].value; 534 } 535 else 536 { 537 static immutable MaxValue = T[0].value > MaxValue!(T[1..$]) ? T[0].value : MaxValue!(T[1..$]); 538 } 539 } 540 541 542 /******************************************************************************* 543 544 Template to find the minimum code from a list of SmartEnumValues. 545 546 Params: 547 T = variadic list of one or more SmartEnumValues 548 549 *******************************************************************************/ 550 551 private template MinValue ( T ... ) 552 { 553 static if ( T.length == 1 ) 554 { 555 static immutable MinValue = T[0].value; 556 } 557 else 558 { 559 static immutable MinValue = T[0].value < MinValue!(T[1..$]) ? T[0].value : MinValue!(T[1..$]); 560 } 561 } 562 563 564 /******************************************************************************* 565 566 Template to find the length of the longest description in a list of 567 SmartEnumValues. 568 569 Params: 570 T = variadic list of one or more SmartEnumValues 571 572 *******************************************************************************/ 573 574 private template LongestName ( T ... ) 575 { 576 static if ( T.length == 1 ) 577 { 578 static immutable LongestName = T[0].name.length; 579 } 580 else 581 { 582 static immutable LongestName = T[0].name.length > LongestName!(T[1..$]) ? T[0].name.length : LongestName!(T[1..$]); 583 } 584 } 585 586 587 /******************************************************************************* 588 589 Template to find the length of the shortest description in a list of 590 SmartEnumValues. 591 592 Params: 593 T = variadic list of one or more SmartEnumValues 594 595 *******************************************************************************/ 596 597 private template ShortestName ( T ... ) 598 { 599 static if ( T.length == 1 ) 600 { 601 static immutable ShortestName = T[0].name.length; 602 } 603 else 604 { 605 static immutable ShortestName = T[0].name.length < ShortestName!(T[1..$]) ? T[0].name.length : ShortestName!(T[1..$]); 606 } 607 } 608 609 610 /******************************************************************************* 611 612 Template to mixin the declaration of a set of constants. 613 614 Params: 615 T = variadic list of one or more SmartEnumValues 616 617 Generates output of the form: 618 619 --- 620 const length = 3; 621 const min = 1; 622 const max = 100; 623 const min_descr_length = 4; 624 const max_descr_length = 6; 625 --- 626 627 *******************************************************************************/ 628 629 private template DeclareConstants ( T ... ) 630 { 631 static immutable istring DeclareConstants = 632 "enum length = " ~ CTFE.toString(T.length) ~ "; " ~ 633 "enum min = " ~ CTFE.toString(MinValue!(T)) ~ "; " ~ 634 "enum max = " ~ CTFE.toString(MaxValue!(T)) ~ "; " ~ 635 "enum min_descr_length = " ~ CTFE.toString(ShortestName!(T)) ~ "; " ~ 636 "enum max_descr_length = " ~ CTFE.toString(LongestName!(T)) ~ "; "; 637 } 638 639 640 /******************************************************************************* 641 642 Template to mixin code for a mixin template declaring the enum class' 643 core members. 644 645 Params: 646 T = variadic list of one or more SmartEnumValues 647 648 Generates output of the form: 649 650 --- 651 mixin SmartEnumCore!(int); 652 --- 653 654 *******************************************************************************/ 655 656 private template MixinCore ( T ... ) 657 { 658 static immutable MixinCore = "mixin SmartEnumCore!(" ~ T[0].BaseType.stringof ~ ");"; 659 } 660 661 662 /******************************************************************************* 663 664 Template to mixin a SmartEnum class. 665 666 Params: 667 Name = name of class 668 T = variadic list of one or more SmartEnumValues 669 670 Generates output of the form: 671 672 --- 673 class EnumName : ISmartEnum 674 { 675 // class contents (see templates above) 676 } 677 --- 678 679 *******************************************************************************/ 680 681 public template SmartEnum ( istring Name, T ... ) 682 { 683 static if ( T.length > 0 ) 684 { 685 static immutable SmartEnum = "class " ~ Name ~ " : ISmartEnum { " ~ DeclareEnum!(T) ~ 686 DeclareConstants!(T) ~ StaticThis!(T) ~ MixinCore!(T) ~ "}"; 687 } 688 else 689 { 690 static assert(false, "Cannot create a SmartEnum with no entries!"); 691 } 692 } 693 694 /// 695 unittest 696 { 697 alias SmartEnumValue!(int) Code; 698 699 mixin (SmartEnum!("Commands", 700 Code("first", 1), 701 Code("second", 2) 702 )); 703 704 test!("==")(*Commands.description(Commands.first), "first"[]); 705 test!("==")(*Commands.description(1), "first"[]); 706 707 test!("==")(*Commands.code("first"), Commands.first); 708 test!("==")(*Commands.code("second"), 2); 709 710 test((1 in Commands) !is null); 711 test((Commands.second in Commands) !is null); 712 test((5 in Commands) is null); 713 714 test(("first" in Commands) !is null); 715 test(("third" in Commands) is null); 716 717 size_t count; 718 foreach ( code, descr; Commands ) 719 ++count; 720 test!("==")(count, 2); 721 } 722 723 /******************************************************************************* 724 725 Template to create a tuple of enum codes from 0 to the length of the passed 726 tuple of strings. 727 728 Params: 729 BaseType = base type of enum 730 i = counter (used recursively) 731 Strings = variadic list of descriptions of the enum values 732 733 *******************************************************************************/ 734 735 private template CreateCodes ( BaseType, uint i, Strings ... ) 736 { 737 static if ( Strings.length == 1 ) 738 { 739 alias Tuple!(SmartEnumValue!(BaseType)(Strings[0], i)) CreateCodes; 740 } 741 else 742 { 743 alias Tuple!(SmartEnumValue!(BaseType)(Strings[0], i), CreateCodes!(BaseType, i + 1, Strings[1 .. $])) CreateCodes; 744 } 745 } 746 747 748 /******************************************************************************* 749 750 Template to mixin a SmartEnum class with the codes automatically generated, 751 starting at 0. 752 753 Params: 754 Name = name of class 755 BaseType = base type of enum 756 Strings = variadic list of descriptions of the enum values (statically 757 asserted to be istring's) 758 759 *******************************************************************************/ 760 761 public template AutoSmartEnum ( istring Name, BaseType, Strings ... ) 762 { 763 static assert ( is(typeof(Strings[0]) : istring), "AutoSmartEnum - please only give immutable strings as template parameters"); 764 765 static immutable AutoSmartEnum = SmartEnum!(Name, CreateCodes!(BaseType, 0, Strings)); 766 } 767 768 unittest 769 { 770 mixin(AutoSmartEnum!("Name", int, "a", "b", "c")); 771 test!("==")(*Name.code("a"), 0); 772 test!("==")(*Name.code("b"), 1); 773 test!("==")(*Name.code("c"), 2); 774 775 // Test lookup with mutable values 776 mstring name1 = "a".dup, name2 = "b".dup, name3 = "c".dup; 777 Name n; 778 779 test!("==")(*Name.code(name1), 0); 780 test!("==")(*Name.code(name2), 1); 781 test!("==")(*Name.code(name3), 2); 782 783 test!("in")(name1, n); 784 test!("in")(name2, n); 785 test!("in")(name3, n); 786 // `!in` doesn't exist in D1 :( 787 test(!("NON_EXISTENT".dup in n), "Found non existent description!"); 788 } 789 790 /******************************************************************************* 791 792 Moved from ocean.core.TwoWayMap 793 794 *******************************************************************************/ 795 796 797 798 import ocean.core.Enforce; 799 800 import ocean.core.Array : find; 801 802 /******************************************************************************* 803 804 Two way map struct template 805 806 Params: 807 A = key type 808 B = value type 809 Indexed = true to include methods for getting the index of keys or 810 values in the internal arrays 811 812 Note: 'key' and 'value' types are arbitrarily named, as the mapping goes 813 both ways. They are just named this way for convenience and to present the 814 same interface as the standard associative array. 815 816 *******************************************************************************/ 817 818 public struct TwoWayMap ( A ) 819 { 820 /*************************************************************************** 821 822 Ensure that the template is mapping between two different types. Most of 823 the methods won't compile otherwise. 824 825 ***************************************************************************/ 826 827 static if ( is(A : cstring) ) 828 { 829 static assert(false, "TwoWayMap does not support mapping to a string type"); 830 } 831 832 833 /*************************************************************************** 834 835 Type aliases. 836 837 ***************************************************************************/ 838 839 public alias A KeyType; 840 public alias istring ValueType; 841 842 843 /*************************************************************************** 844 845 Associative arrays which store the mappings. 846 847 ***************************************************************************/ 848 849 private ValueType[KeyType] a_to_b; 850 private KeyType[ValueType] b_to_a; 851 852 853 /*************************************************************************** 854 855 Dynamic arrays storing all keys and values added to the mappings. 856 Storing these locally avoids calling the associative array .key and 857 .value properties, which cause a memory allocation on each use. 858 859 TODO: maybe this should be optional, controlled by a template parameter 860 861 ***************************************************************************/ 862 863 private KeyType[] keys_list; 864 private ValueType[] values_list; 865 866 867 /*************************************************************************** 868 869 Optional indices for mapped items. 870 871 ***************************************************************************/ 872 873 private size_t[KeyType] a_to_index; // A to index in keys_list 874 private size_t[ValueType] b_to_index; // B to index in values_list 875 876 877 /*************************************************************************** 878 879 Invariant checking that the length of both mappings should always be 880 identical. 881 Use -debug=TwoWayMapFullConsistencyCheck to check that the indices of 882 mapped items are consistent, too (this check may significantly impact 883 performance). 884 885 ***************************************************************************/ 886 887 invariant ( ) 888 { 889 assert((&this).a_to_b.length == (&this).b_to_a.length); 890 891 debug ( TwoWayMapFullConsistencyCheck ) 892 { 893 foreach ( a, b; (&this).a_to_b ) 894 { 895 assert((&this).a_to_index[a] == (&this).b_to_index[b]); 896 } 897 } 898 } 899 900 901 /*************************************************************************** 902 903 Assigns a set of mappings from an associative array. 904 905 Params: 906 assoc_array = associative array to assign 907 908 ***************************************************************************/ 909 910 public void opAssign ( ValueType[KeyType] assoc_array ) 911 { 912 (&this).keys_list.length = 0; 913 (&this).values_list.length = 0; 914 915 (&this).a_to_b = assoc_array; 916 917 foreach ( a, b; (&this).a_to_b ) 918 { 919 (&this).b_to_a[b] = a; 920 921 (&this).keys_list ~= *(b in (&this).b_to_a); 922 (&this).values_list ~= *(a in (&this).a_to_b); 923 } 924 925 (&this).updateIndices(); 926 } 927 928 public void opAssign ( KeyType[ValueType] assoc_array ) 929 { 930 (&this).keys_list.length = 0; 931 (&this).values_list.length = 0; 932 933 (&this).b_to_a = assoc_array; 934 935 foreach ( b, a; (&this).b_to_a ) 936 { 937 (&this).a_to_b[a] = b; 938 939 (&this).keys_list ~= *(b in (&this).b_to_a); 940 (&this).values_list ~= *(a in (&this).a_to_b); 941 } 942 943 (&this).updateIndices(); 944 } 945 946 947 /*************************************************************************** 948 949 Adds a mapping. 950 951 Params: 952 a = item to map to 953 b = item to map to 954 955 ***************************************************************************/ 956 957 public void opIndexAssign ( KeyType a, ValueType b ) 958 out 959 { 960 assert((&this).a_to_index[a] < (&this).keys_list.length); 961 assert((&this).b_to_index[b] < (&this).values_list.length); 962 } 963 body 964 { 965 auto already_exists = !!(a in (&this).a_to_b); 966 967 (&this).a_to_b[a] = b; 968 (&this).b_to_a[b] = a; 969 970 if ( !already_exists ) 971 { 972 (&this).keys_list ~= *(b in (&this).b_to_a); 973 (&this).values_list ~= *(a in (&this).a_to_b); 974 } 975 976 (&this).updateIndices(); 977 } 978 979 public void opIndexAssign ( ValueType b, KeyType a ) 980 out 981 { 982 assert((&this).a_to_index[a] < (&this).keys_list.length); 983 assert((&this).b_to_index[b] < (&this).values_list.length); 984 } 985 body 986 { 987 auto already_exists = !!(a in (&this).a_to_b); 988 989 (&this).a_to_b[a] = b; 990 (&this).b_to_a[b] = a; 991 992 if ( !already_exists ) 993 { 994 (&this).keys_list ~= *(b in (&this).b_to_a); 995 (&this).values_list ~= *(a in (&this).a_to_b); 996 } 997 998 (&this).updateIndices(); 999 } 1000 1001 1002 /*************************************************************************** 1003 1004 Rehashes the mappings. 1005 1006 ***************************************************************************/ 1007 1008 public void rehash ( ) 1009 { 1010 (&this).a_to_b.rehash; 1011 (&this).b_to_a.rehash; 1012 1013 (&this).updateIndices(); 1014 } 1015 1016 1017 /*************************************************************************** 1018 1019 opIn_r operator - performs a lookup of an item A in the map 1020 corresponding to an item B. 1021 1022 Params: 1023 b = item to look up 1024 1025 Returns: 1026 item of type A corresponding to specified item of type B, or null if 1027 no mapping exists 1028 1029 ***************************************************************************/ 1030 1031 public KeyType* opIn_r ( cstring b ) 1032 { 1033 return b in (&this).b_to_a; 1034 } 1035 1036 1037 /*************************************************************************** 1038 1039 opIn_r operator - performs a lookup of an item B in the map 1040 corresponding to an item A. 1041 1042 Params: 1043 a = item to look up 1044 1045 Returns: 1046 item of type B corresponding to specified item of type A, or null if 1047 no mapping exists 1048 1049 ***************************************************************************/ 1050 1051 public ValueType* opIn_r ( KeyType a ) 1052 { 1053 return a in (&this).a_to_b; 1054 } 1055 1056 1057 /*************************************************************************** 1058 1059 opIndex operator - performs a lookup of an item A in the map 1060 corresponding to an item B. 1061 1062 Params: 1063 b = item to look up 1064 1065 Throws: 1066 as per the normal opIndex operator over an associative array 1067 1068 Returns: 1069 item of type A corresponding to specified item of type B 1070 1071 ***************************************************************************/ 1072 1073 public KeyType opIndex ( cstring b ) 1074 { 1075 return (&this).b_to_a[b]; 1076 } 1077 1078 1079 /*************************************************************************** 1080 1081 opIndex operator - performs a lookup of an item B in the map 1082 corresponding to an item A. 1083 1084 Params: 1085 a = item to look up 1086 1087 Throws: 1088 as per the normal opIndex operator over an associative array 1089 1090 Returns: 1091 item of type B corresponding to specified item of type A 1092 1093 ***************************************************************************/ 1094 1095 public ValueType opIndex ( KeyType a ) 1096 { 1097 return (&this).a_to_b[a]; 1098 } 1099 1100 1101 /*************************************************************************** 1102 1103 Returns: 1104 number of items in the map 1105 1106 ***************************************************************************/ 1107 1108 public size_t length ( ) 1109 { 1110 return (&this).a_to_b.length; 1111 } 1112 1113 1114 /*************************************************************************** 1115 1116 Returns: 1117 dynamic array containing all map elements of type A 1118 1119 ***************************************************************************/ 1120 1121 public KeyType[] keys ( ) 1122 { 1123 return (&this).keys_list; 1124 } 1125 1126 1127 /*************************************************************************** 1128 1129 Returns: 1130 dynamic array containing all map elements of type B 1131 1132 ***************************************************************************/ 1133 1134 public ValueType[] values ( ) 1135 { 1136 return (&this).values_list; 1137 } 1138 1139 1140 /*************************************************************************** 1141 1142 foreach iterator over the mapping. 1143 1144 Note that the order of iteration over the map is determined by the 1145 elements of type A (the keys). 1146 1147 ***************************************************************************/ 1148 1149 public int opApply ( scope int delegate ( ref KeyType a, ref ValueType b ) dg ) 1150 { 1151 int res; 1152 foreach ( a, b; (&this).a_to_b ) 1153 { 1154 res = dg(a, b); 1155 } 1156 return res; 1157 } 1158 1159 1160 /*************************************************************************** 1161 1162 foreach iterator over the mapping, including each value's index. 1163 1164 Note that the order of iteration over the map is determined by the 1165 elements of type A (the keys). 1166 1167 ***************************************************************************/ 1168 1169 public int opApply ( scope int delegate ( ref size_t index, ref KeyType a, ref ValueType b ) dg ) 1170 { 1171 int res; 1172 foreach ( a, b; (&this).a_to_b ) 1173 { 1174 auto index = (&this).indexOf(a); 1175 verify(index !is null); 1176 1177 res = dg(*index, a, b); 1178 } 1179 return res; 1180 } 1181 1182 1183 /*************************************************************************** 1184 1185 Gets the index of an element of type A in the list of all elements of 1186 type A. 1187 1188 Params: 1189 a = element to look up 1190 1191 Returns: 1192 pointer to the index of an element of type A in this.keys_list, or 1193 null if the element is not in the map 1194 1195 ***************************************************************************/ 1196 1197 public size_t* indexOf ( KeyType a ) 1198 { 1199 auto index = a in this.a_to_index; 1200 enforce(index, typeof((&this)).stringof ~ ".indexOf - element not present in map"); 1201 return index; 1202 } 1203 1204 1205 /*************************************************************************** 1206 1207 Gets the index of an element of type B in the list of all elements of 1208 type B. 1209 1210 Params: 1211 b = element to look up 1212 1213 Returns: 1214 pointer to the index of an element of type B in this.values_list, 1215 or null if the element is not in the map 1216 1217 ***************************************************************************/ 1218 1219 public size_t* indexOf ( cstring b ) 1220 { 1221 auto index = b in this.b_to_index; 1222 enforce(index, typeof((&this)).stringof ~ ".indexOf - element not present in map"); 1223 return index; 1224 } 1225 1226 1227 /*************************************************************************** 1228 1229 Updates the index arrays when the mapping is altered. 1230 1231 ***************************************************************************/ 1232 1233 private void updateIndices ( ) 1234 { 1235 foreach ( a, b; (&this).a_to_b ) 1236 { 1237 (&this).a_to_index[a] = (&this).keys_list.find(a); 1238 (&this).b_to_index[b] = (&this).values_list.find(b); 1239 } 1240 } 1241 }