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.meta.types.Qualifiers; 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.meta.types.Qualifiers; 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 opBinaryRight ( istring op : "in" ) = description; 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 opBinaryRight ( istring op : "in" ) = code; 235 236 237 238 /*************************************************************************** 239 240 Gets the description of a code. 241 242 Params: 243 code = code to look up 244 245 Returns: 246 code's description 247 248 Throws: 249 if code is not in the map 250 251 ***************************************************************************/ 252 253 static public istring opIndex ( BaseType code ) 254 { 255 auto description = code in map; 256 enforce(description, "code not found in SmartEnum"); 257 return *description; 258 } 259 260 261 /*************************************************************************** 262 263 Gets the code of a description. 264 265 Params: 266 description = description to look up 267 268 Returns: 269 description's code 270 271 Throws: 272 if description is not in the map 273 274 ***************************************************************************/ 275 276 static public BaseType opIndex ( cstring description ) 277 { 278 auto code = description in map; 279 enforce(code, cast(istring)(description ~ " not found in SmartEnum")); 280 return *code; 281 } 282 283 284 /*************************************************************************** 285 286 Looks up the index of a code in the enum (ie code is the nth code in the 287 enum). This can be useful if the actual enum codes are not consecutive. 288 289 Params: 290 code = code to get index for 291 292 Returns: 293 pointer to code's index, or null if code not in enum 294 295 ***************************************************************************/ 296 297 static public size_t* indexOf ( BaseType code ) 298 { 299 return map.indexOf(code); 300 } 301 302 303 /*************************************************************************** 304 305 Looks up the index of a description in the enum (ie description is for 306 the nth code in the enum). 307 308 Params: 309 description = description to get index for 310 311 Returns: 312 pointer to description's index, or null if description not in enum 313 314 ***************************************************************************/ 315 316 static public size_t* indexOf ( cstring description ) 317 { 318 return map.indexOf(description); 319 } 320 321 322 /*************************************************************************** 323 324 Looks up a code in the enum by its index. (ie gets the nth code in the 325 enum). 326 327 Params: 328 index = index of code to get 329 330 Returns: 331 nth code in enum 332 333 Throws: 334 array out of bounds if index is > number of codes in the enum 335 336 ***************************************************************************/ 337 338 static public BaseType codeFromIndex ( size_t index ) 339 { 340 return map.keys[index]; 341 } 342 343 344 /*************************************************************************** 345 346 Looks up a description in the enum by the index of its code. (ie gets 347 the description of the nth code in the enum). 348 349 Params: 350 index = index of code to get description for 351 352 Returns: 353 description of nth code in enum 354 355 Throws: 356 array out of bounds if index is > number of codes in the enum 357 358 ***************************************************************************/ 359 360 static public istring descriptionFromIndex ( size_t index ) 361 { 362 return map.values[index]; 363 } 364 365 366 /*************************************************************************** 367 368 foreach iterator over the codes and descriptions of the enum. 369 370 ***************************************************************************/ 371 372 static public int opApply ( scope int delegate ( ref BaseType code, ref istring desc ) dg ) 373 { 374 int res; 375 foreach ( enum_code, enum_description; map ) 376 { 377 res = dg(enum_code, enum_description); 378 } 379 return res; 380 } 381 382 383 /*************************************************************************** 384 385 foreach iterator over the codes and descriptions of the enum and their 386 indices. 387 388 ***************************************************************************/ 389 390 static public int opApply ( scope int delegate ( ref size_t index, ref BaseType code, ref istring desc ) dg ) 391 { 392 int res; 393 foreach ( index, enum_code, enum_description; map ) 394 { 395 res = dg(index, enum_code, enum_description); 396 } 397 return res; 398 } 399 } 400 401 unittest 402 { 403 alias SmartEnumCore!(int) _; 404 } 405 406 /******************************************************************************* 407 408 Template to mixin a comma separated list of SmartEnumValues. 409 410 Params: 411 T = variadic list of one or more SmartEnumValues 412 413 Generates output of the form: 414 415 --- 416 first = 1, 417 second = 2, 418 last = 100 419 --- 420 421 *******************************************************************************/ 422 423 private template EnumValuesList ( T ... ) 424 { 425 static if ( T.length == 1 ) 426 { 427 static immutable EnumValuesList = T[0].name ~ "=" ~ CTFE.toString(T[0].value); 428 } 429 else 430 { 431 static immutable EnumValuesList = T[0].name ~ "=" ~ CTFE.toString(T[0].value) ~ "," ~ EnumValuesList!(T[1..$]); 432 } 433 } 434 435 436 /******************************************************************************* 437 438 Template to mixin an enum from a list of SmartEnumValues. 439 440 Params: 441 T = variadic list of one or more SmartEnumValues 442 443 Generates output of the form: 444 445 --- 446 enum 447 { 448 first = 1, 449 second = 2, 450 last = 100 451 } 452 --- 453 454 *******************************************************************************/ 455 456 private template DeclareEnum ( T ... ) 457 { 458 static immutable DeclareEnum = "alias " ~ T[0].BaseType.stringof ~ " BaseType; enum : BaseType {" ~ EnumValuesList!(T) ~ "} "; 459 } 460 461 462 /******************************************************************************* 463 464 Template to mixin a series of TwoWayMap value initialisations. 465 466 Params: 467 T = variadic list of one or more SmartEnumValues 468 469 Generates output of the form: 470 471 --- 472 map["first"]=1; 473 map["second"]=2; 474 map["last"]=100; 475 --- 476 477 *******************************************************************************/ 478 479 private template InitialiseMap ( T ... ) 480 { 481 static if ( T.length == 1 ) 482 { 483 static immutable InitialiseMap = `map["` ~ T[0].name ~ `"]=` ~ T[0].name ~ ";"; 484 } 485 else 486 { 487 static immutable InitialiseMap = `map["` ~ T[0].name ~ `"]=` ~ T[0].name ~ ";" ~ InitialiseMap!(T[1..$]); 488 } 489 } 490 491 492 /******************************************************************************* 493 494 Template to mixin a static constructor which initialises and rehashes a 495 TwoWayMap. 496 497 Params: 498 T = variadic list of one or more SmartEnumValues 499 500 Generates output of the form: 501 502 --- 503 static this ( ) 504 { 505 map["first"]=1; 506 map["second"]=2; 507 map["last"]=100; 508 509 map.rehash; 510 } 511 --- 512 513 *******************************************************************************/ 514 515 private template StaticThis ( T ... ) 516 { 517 static immutable StaticThis = "static this() {" ~ InitialiseMap!(T) ~ "map.rehash;} "; 518 } 519 520 521 /******************************************************************************* 522 523 Template to find the maximum code from a list of SmartEnumValues. 524 525 Params: 526 T = variadic list of one or more SmartEnumValues 527 528 *******************************************************************************/ 529 530 private template MaxValue ( T ... ) 531 { 532 static if ( T.length == 1 ) 533 { 534 static immutable MaxValue = T[0].value; 535 } 536 else 537 { 538 static immutable MaxValue = T[0].value > MaxValue!(T[1..$]) ? T[0].value : MaxValue!(T[1..$]); 539 } 540 } 541 542 543 /******************************************************************************* 544 545 Template to find the minimum code from a list of SmartEnumValues. 546 547 Params: 548 T = variadic list of one or more SmartEnumValues 549 550 *******************************************************************************/ 551 552 private template MinValue ( T ... ) 553 { 554 static if ( T.length == 1 ) 555 { 556 static immutable MinValue = T[0].value; 557 } 558 else 559 { 560 static immutable MinValue = T[0].value < MinValue!(T[1..$]) ? T[0].value : MinValue!(T[1..$]); 561 } 562 } 563 564 565 /******************************************************************************* 566 567 Template to find the length of the longest description in a list of 568 SmartEnumValues. 569 570 Params: 571 T = variadic list of one or more SmartEnumValues 572 573 *******************************************************************************/ 574 575 private template LongestName ( T ... ) 576 { 577 static if ( T.length == 1 ) 578 { 579 static immutable LongestName = T[0].name.length; 580 } 581 else 582 { 583 static immutable LongestName = T[0].name.length > LongestName!(T[1..$]) ? T[0].name.length : LongestName!(T[1..$]); 584 } 585 } 586 587 588 /******************************************************************************* 589 590 Template to find the length of the shortest description in a list of 591 SmartEnumValues. 592 593 Params: 594 T = variadic list of one or more SmartEnumValues 595 596 *******************************************************************************/ 597 598 private template ShortestName ( T ... ) 599 { 600 static if ( T.length == 1 ) 601 { 602 static immutable ShortestName = T[0].name.length; 603 } 604 else 605 { 606 static immutable ShortestName = T[0].name.length < ShortestName!(T[1..$]) ? T[0].name.length : ShortestName!(T[1..$]); 607 } 608 } 609 610 611 /******************************************************************************* 612 613 Template to mixin the declaration of a set of constants. 614 615 Params: 616 T = variadic list of one or more SmartEnumValues 617 618 Generates output of the form: 619 620 --- 621 const length = 3; 622 const min = 1; 623 const max = 100; 624 const min_descr_length = 4; 625 const max_descr_length = 6; 626 --- 627 628 *******************************************************************************/ 629 630 private template DeclareConstants ( T ... ) 631 { 632 static immutable istring DeclareConstants = 633 "enum length = " ~ CTFE.toString(T.length) ~ "; " ~ 634 "enum min = " ~ CTFE.toString(MinValue!(T)) ~ "; " ~ 635 "enum max = " ~ CTFE.toString(MaxValue!(T)) ~ "; " ~ 636 "enum min_descr_length = " ~ CTFE.toString(ShortestName!(T)) ~ "; " ~ 637 "enum max_descr_length = " ~ CTFE.toString(LongestName!(T)) ~ "; "; 638 } 639 640 641 /******************************************************************************* 642 643 Template to mixin code for a mixin template declaring the enum class' 644 core members. 645 646 Params: 647 T = variadic list of one or more SmartEnumValues 648 649 Generates output of the form: 650 651 --- 652 mixin SmartEnumCore!(int); 653 --- 654 655 *******************************************************************************/ 656 657 private template MixinCore ( T ... ) 658 { 659 static immutable MixinCore = "mixin SmartEnumCore!(" ~ T[0].BaseType.stringof ~ ");"; 660 } 661 662 663 /******************************************************************************* 664 665 Template to mixin a SmartEnum class. 666 667 Params: 668 Name = name of class 669 T = variadic list of one or more SmartEnumValues 670 671 Generates output of the form: 672 673 --- 674 class EnumName : ISmartEnum 675 { 676 // class contents (see templates above) 677 } 678 --- 679 680 *******************************************************************************/ 681 682 public template SmartEnum ( istring Name, T ... ) 683 { 684 static if ( T.length > 0 ) 685 { 686 static immutable SmartEnum = "class " ~ Name ~ " : ISmartEnum { " ~ DeclareEnum!(T) ~ 687 DeclareConstants!(T) ~ StaticThis!(T) ~ MixinCore!(T) ~ "}"; 688 } 689 else 690 { 691 static assert(false, "Cannot create a SmartEnum with no entries!"); 692 } 693 } 694 695 /// 696 unittest 697 { 698 alias SmartEnumValue!(int) Code; 699 700 mixin (SmartEnum!("Commands", 701 Code("first", 1), 702 Code("second", 2) 703 )); 704 705 test!("==")(*Commands.description(Commands.first), "first"[]); 706 test!("==")(*Commands.description(1), "first"[]); 707 708 test!("==")(*Commands.code("first"), Commands.first); 709 test!("==")(*Commands.code("second"), 2); 710 711 test((1 in Commands) !is null); 712 test((Commands.second in Commands) !is null); 713 test((5 in Commands) is null); 714 715 test(("first" in Commands) !is null); 716 test(("third" in Commands) is null); 717 718 size_t count; 719 foreach ( code, descr; Commands ) 720 ++count; 721 test!("==")(count, 2); 722 } 723 724 725 /******************************************************************************* 726 727 Template to mixin a SmartEnum class with the codes automatically generated, 728 starting at 0. 729 730 Params: 731 Name = name of class 732 BaseType = base type of enum 733 Strings = variadic list of descriptions of the enum values (statically 734 asserted to be istring's) 735 736 *******************************************************************************/ 737 738 public template AutoSmartEnum ( istring Name, BaseType, Strings ... ) 739 { 740 static assert ( is(typeof(Strings[0]) : istring), "AutoSmartEnum - please only give immutable strings as template parameters"); 741 742 static immutable AutoSmartEnum = autoSmartEnumImpl(Name, BaseType.stringof, 743 Strings); 744 } 745 746 unittest 747 { 748 mixin(AutoSmartEnum!("Name", int, "a", "b", "c")); 749 test!("==")(*Name.code("a"), 0); 750 test!("==")(*Name.code("b"), 1); 751 test!("==")(*Name.code("c"), 2); 752 753 // Test lookup with mutable values 754 mstring name1 = "a".dup, name2 = "b".dup, name3 = "c".dup; 755 Name n; 756 757 test!("==")(*Name.code(name1), 0); 758 test!("==")(*Name.code(name2), 1); 759 test!("==")(*Name.code(name3), 2); 760 761 test!("in")(name1, n); 762 test!("in")(name2, n); 763 test!("in")(name3, n); 764 test!("!in")("NON_EXISTENT".dup, n); 765 } 766 767 768 /******************************************************************************* 769 770 CTFE function to mixin a SmartEnum class with the codes automatically 771 generated, starting at 0. 772 773 Params: 774 name = name of the class to be created 775 basetype = base type of enum 776 members = variadic list of descriptions of the enum values 777 778 *******************************************************************************/ 779 780 private istring autoSmartEnumImpl ( istring name, istring basetype, 781 istring[] members ... ) 782 { 783 // Create two strings, one to declare the members, and one to populate 784 // the map. 785 // Also determine the minimum and maximum length of each member name 786 787 istring declstr = null; 788 istring mapstr = null; 789 790 long maxlen = members[0].length; 791 long minlen = members[0].length; 792 793 foreach ( i, s ; members ) 794 { 795 if ( s.length > maxlen ) 796 { 797 maxlen = s.length; 798 } 799 if ( s.length < minlen ) 800 { 801 minlen = s.length; 802 } 803 804 // declstr will be of the form "A=0,B=1,C=2" 805 806 if ( i > 0 ) 807 { 808 declstr ~= ","; 809 } 810 811 declstr ~= s ~ "=" ~ CTFE.toString(i); 812 813 // mapstr will be of the form `map["A"]=A;map["B"]=B;` 814 815 mapstr ~= `map["` ~ s ~ `"]=` ~ s ~ `;`; 816 } 817 818 return "class " ~ name ~ " : ISmartEnum {" 819 ~ " alias " ~ basetype ~ " BaseType; enum : BaseType {" 820 ~ declstr ~ "} enum length = " ~ CTFE.toString(members.length) 821 ~ "; enum min = 0; enum max = " ~ CTFE.toString(members.length - 1) 822 ~ "; enum min_descr_length = " ~ CTFE.toString(minlen) 823 ~ "; enum max_descr_length = " ~ CTFE.toString(maxlen) 824 ~ "; static this() {" ~ mapstr ~ "map.rehash;} mixin SmartEnumCore!(" 825 ~ basetype~ ");}"; 826 } 827 828 unittest 829 { 830 test!("==")(autoSmartEnumImpl("Name", "int", "aa", "bbbb", "c"), 831 `class Name : ISmartEnum { alias int BaseType; enum : BaseType ` 832 ~ `{aa=0,bbbb=1,c=2} enum length = 3; enum min = 0; enum max = 2; ` 833 ~ `enum min_descr_length = 1; enum max_descr_length = 4; ` 834 ~ `static this() {map["aa"]=aa;map["bbbb"]=bbbb;map["c"]=c;` 835 ~ `map.rehash;} mixin SmartEnumCore!(int);}`); 836 } 837 838 /******************************************************************************* 839 840 Moved from ocean.core.TwoWayMap 841 842 *******************************************************************************/ 843 844 845 846 import ocean.core.Enforce; 847 848 import ocean.core.Array : find; 849 850 /******************************************************************************* 851 852 Two way map struct template 853 854 Params: 855 A = key type 856 B = value type 857 Indexed = true to include methods for getting the index of keys or 858 values in the internal arrays 859 860 Note: 'key' and 'value' types are arbitrarily named, as the mapping goes 861 both ways. They are just named this way for convenience and to present the 862 same interface as the standard associative array. 863 864 *******************************************************************************/ 865 866 public struct TwoWayMap ( A ) 867 { 868 /*************************************************************************** 869 870 Ensure that the template is mapping between two different types. Most of 871 the methods won't compile otherwise. 872 873 ***************************************************************************/ 874 875 static if ( is(A : cstring) ) 876 { 877 static assert(false, "TwoWayMap does not support mapping to a string type"); 878 } 879 880 881 /*************************************************************************** 882 883 Type aliases. 884 885 ***************************************************************************/ 886 887 public alias A KeyType; 888 public alias istring ValueType; 889 890 891 /*************************************************************************** 892 893 Associative arrays which store the mappings. 894 895 ***************************************************************************/ 896 897 private ValueType[KeyType] a_to_b; 898 private KeyType[ValueType] b_to_a; 899 900 901 /*************************************************************************** 902 903 Dynamic arrays storing all keys and values added to the mappings. 904 Storing these locally avoids calling the associative array .key and 905 .value properties, which cause a memory allocation on each use. 906 907 TODO: maybe this should be optional, controlled by a template parameter 908 909 ***************************************************************************/ 910 911 private KeyType[] keys_list; 912 private ValueType[] values_list; 913 914 915 /*************************************************************************** 916 917 Optional indices for mapped items. 918 919 ***************************************************************************/ 920 921 private size_t[KeyType] a_to_index; // A to index in keys_list 922 private size_t[ValueType] b_to_index; // B to index in values_list 923 924 925 /*************************************************************************** 926 927 Invariant checking that the length of both mappings should always be 928 identical. 929 Use -debug=TwoWayMapFullConsistencyCheck to check that the indices of 930 mapped items are consistent, too (this check may significantly impact 931 performance). 932 933 ***************************************************************************/ 934 935 invariant ( ) 936 { 937 assert(this.a_to_b.length == this.b_to_a.length); 938 939 debug ( TwoWayMapFullConsistencyCheck ) 940 { 941 foreach ( a, b; this.a_to_b ) 942 { 943 assert(this.a_to_index[a] == this.b_to_index[b]); 944 } 945 } 946 } 947 948 949 /*************************************************************************** 950 951 Assigns a set of mappings from an associative array. 952 953 Params: 954 assoc_array = associative array to assign 955 956 ***************************************************************************/ 957 958 public void opAssign ( ValueType[KeyType] assoc_array ) 959 { 960 this.keys_list.length = 0; 961 this.values_list.length = 0; 962 963 this.a_to_b = assoc_array; 964 965 foreach ( a, b; this.a_to_b ) 966 { 967 this.b_to_a[b] = a; 968 969 this.keys_list ~= *(b in this.b_to_a); 970 this.values_list ~= *(a in this.a_to_b); 971 } 972 973 this.updateIndices(); 974 } 975 976 public void opAssign ( KeyType[ValueType] assoc_array ) 977 { 978 this.keys_list.length = 0; 979 this.values_list.length = 0; 980 981 this.b_to_a = assoc_array; 982 983 foreach ( b, a; this.b_to_a ) 984 { 985 this.a_to_b[a] = b; 986 987 this.keys_list ~= *(b in this.b_to_a); 988 this.values_list ~= *(a in this.a_to_b); 989 } 990 991 this.updateIndices(); 992 } 993 994 995 /*************************************************************************** 996 997 Adds a mapping. 998 999 Params: 1000 a = item to map to 1001 b = item to map to 1002 1003 ***************************************************************************/ 1004 1005 public void opIndexAssign ( KeyType a, ValueType b ) 1006 out 1007 { 1008 assert(this.a_to_index[a] < this.keys_list.length); 1009 assert(this.b_to_index[b] < this.values_list.length); 1010 } 1011 do 1012 { 1013 auto already_exists = !!(a in this.a_to_b); 1014 1015 this.a_to_b[a] = b; 1016 this.b_to_a[b] = a; 1017 1018 if ( !already_exists ) 1019 { 1020 this.keys_list ~= *(b in this.b_to_a); 1021 this.values_list ~= *(a in this.a_to_b); 1022 } 1023 1024 this.updateIndices(); 1025 } 1026 1027 public void opIndexAssign ( ValueType b, KeyType a ) 1028 out 1029 { 1030 assert(this.a_to_index[a] < this.keys_list.length); 1031 assert(this.b_to_index[b] < this.values_list.length); 1032 } 1033 do 1034 { 1035 auto already_exists = !!(a in this.a_to_b); 1036 1037 this.a_to_b[a] = b; 1038 this.b_to_a[b] = a; 1039 1040 if ( !already_exists ) 1041 { 1042 this.keys_list ~= *(b in this.b_to_a); 1043 this.values_list ~= *(a in this.a_to_b); 1044 } 1045 1046 this.updateIndices(); 1047 } 1048 1049 1050 /*************************************************************************** 1051 1052 Rehashes the mappings. 1053 1054 ***************************************************************************/ 1055 1056 public void rehash ( ) 1057 { 1058 this.a_to_b.rehash; 1059 this.b_to_a.rehash; 1060 1061 this.updateIndices(); 1062 } 1063 1064 1065 /*************************************************************************** 1066 1067 `in` operator - performs a lookup of an item A in the map 1068 corresponding to an item B. 1069 1070 Params: 1071 b = item to look up 1072 1073 Returns: 1074 item of type A corresponding to specified item of type B, or null if 1075 no mapping exists 1076 1077 ***************************************************************************/ 1078 1079 public KeyType* opBinaryRight ( istring op : "in" ) ( cstring b ) 1080 { 1081 return b in this.b_to_a; 1082 } 1083 1084 1085 /*************************************************************************** 1086 1087 opIn_r operator - performs a lookup of an item B in the map 1088 corresponding to an item A. 1089 1090 Params: 1091 a = item to look up 1092 1093 Returns: 1094 item of type B corresponding to specified item of type A, or null if 1095 no mapping exists 1096 1097 ***************************************************************************/ 1098 1099 public ValueType* opBinaryRight ( istring op : "in" ) ( KeyType a ) 1100 { 1101 return a in this.a_to_b; 1102 } 1103 1104 1105 /*************************************************************************** 1106 1107 opIndex operator - performs a lookup of an item A in the map 1108 corresponding to an item B. 1109 1110 Params: 1111 b = item to look up 1112 1113 Throws: 1114 as per the normal opIndex operator over an associative array 1115 1116 Returns: 1117 item of type A corresponding to specified item of type B 1118 1119 ***************************************************************************/ 1120 1121 public KeyType opIndex ( cstring b ) 1122 { 1123 return this.b_to_a[b]; 1124 } 1125 1126 1127 /*************************************************************************** 1128 1129 opIndex operator - performs a lookup of an item B in the map 1130 corresponding to an item A. 1131 1132 Params: 1133 a = item to look up 1134 1135 Throws: 1136 as per the normal opIndex operator over an associative array 1137 1138 Returns: 1139 item of type B corresponding to specified item of type A 1140 1141 ***************************************************************************/ 1142 1143 public ValueType opIndex ( KeyType a ) 1144 { 1145 return this.a_to_b[a]; 1146 } 1147 1148 1149 /*************************************************************************** 1150 1151 Returns: 1152 number of items in the map 1153 1154 ***************************************************************************/ 1155 1156 public size_t length ( ) 1157 { 1158 return this.a_to_b.length; 1159 } 1160 1161 1162 /*************************************************************************** 1163 1164 Returns: 1165 dynamic array containing all map elements of type A 1166 1167 ***************************************************************************/ 1168 1169 public KeyType[] keys ( ) 1170 { 1171 return this.keys_list; 1172 } 1173 1174 1175 /*************************************************************************** 1176 1177 Returns: 1178 dynamic array containing all map elements of type B 1179 1180 ***************************************************************************/ 1181 1182 public ValueType[] values ( ) 1183 { 1184 return this.values_list; 1185 } 1186 1187 1188 /*************************************************************************** 1189 1190 foreach iterator over the mapping. 1191 1192 Note that the order of iteration over the map is determined by the 1193 elements of type A (the keys). 1194 1195 ***************************************************************************/ 1196 1197 public int opApply ( scope int delegate ( ref KeyType a, ref ValueType b ) dg ) 1198 { 1199 int res; 1200 foreach ( a, b; this.a_to_b ) 1201 { 1202 res = dg(a, b); 1203 } 1204 return res; 1205 } 1206 1207 1208 /*************************************************************************** 1209 1210 foreach iterator over the mapping, including each value's index. 1211 1212 Note that the order of iteration over the map is determined by the 1213 elements of type A (the keys). 1214 1215 ***************************************************************************/ 1216 1217 public int opApply ( scope int delegate ( ref size_t index, ref KeyType a, ref ValueType b ) dg ) 1218 { 1219 int res; 1220 foreach ( a, b; this.a_to_b ) 1221 { 1222 auto index = this.indexOf(a); 1223 verify(index !is null); 1224 1225 res = dg(*index, a, b); 1226 } 1227 return res; 1228 } 1229 1230 1231 /*************************************************************************** 1232 1233 Gets the index of an element of type A in the list of all elements of 1234 type A. 1235 1236 Params: 1237 a = element to look up 1238 1239 Returns: 1240 pointer to the index of an element of type A in this.keys_list, or 1241 null if the element is not in the map 1242 1243 ***************************************************************************/ 1244 1245 public size_t* indexOf ( KeyType a ) 1246 { 1247 auto index = a in this.a_to_index; 1248 enforce(index, typeof(this).stringof ~ ".indexOf - element not present in map"); 1249 return index; 1250 } 1251 1252 1253 /*************************************************************************** 1254 1255 Gets the index of an element of type B in the list of all elements of 1256 type B. 1257 1258 Params: 1259 b = element to look up 1260 1261 Returns: 1262 pointer to the index of an element of type B in this.values_list, 1263 or null if the element is not in the map 1264 1265 ***************************************************************************/ 1266 1267 public size_t* indexOf ( cstring b ) 1268 { 1269 auto index = b in this.b_to_index; 1270 enforce(index, typeof(this).stringof ~ ".indexOf - element not present in map"); 1271 return index; 1272 } 1273 1274 1275 /*************************************************************************** 1276 1277 Updates the index arrays when the mapping is altered. 1278 1279 ***************************************************************************/ 1280 1281 private void updateIndices ( ) 1282 { 1283 foreach ( a, b; this.a_to_b ) 1284 { 1285 this.a_to_index[a] = this.keys_list.find(a); 1286 this.b_to_index[b] = this.values_list.find(b); 1287 } 1288 } 1289 }