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 moduleocean.core.SmartEnum;
113 114 115 116 importocean.meta.types.Qualifiers;
117 118 publicimportocean.core.Enforce;
119 120 importCTFE = ocean.meta.codegen.CTFE;
121 122 importocean.core.Tuple;
123 importocean.core.Verify;
124 125 version (unittest)
126 {
127 importocean.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 publicabstractclassISmartEnum140 {
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 publicstructSmartEnumValue ( T )
156 {
157 aliasTBaseType;
158 159 istringname;
160 Tvalue;
161 }
162 163 unittest164 {
165 aliasSmartEnumValue!(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 publictemplateSmartEnumCore ( BaseType )
181 {
182 importocean.meta.types.Qualifiers;
183 184 /***************************************************************************
185 186 Two way mapping between codes <-> descriptions.
187 188 ***************************************************************************/189 190 staticpublicTwoWayMap!(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 staticpublicistring* description ( BaseTypecode )
208 {
209 returncodeinmap;
210 }
211 212 publicaliasopBinaryRight ( istringop : "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 staticpublicBaseType* code ( cstringdescription )
230 {
231 returndescriptioninmap;
232 }
233 234 publicaliasopBinaryRight ( istringop : "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 staticpublicistringopIndex ( BaseTypecode )
254 {
255 autodescription = codeinmap;
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 staticpublicBaseTypeopIndex ( cstringdescription )
277 {
278 autocode = descriptioninmap;
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 staticpublicsize_t* indexOf ( BaseTypecode )
298 {
299 returnmap.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 staticpublicsize_t* indexOf ( cstringdescription )
317 {
318 returnmap.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 staticpublicBaseTypecodeFromIndex ( size_tindex )
339 {
340 returnmap.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 staticpublicistringdescriptionFromIndex ( size_tindex )
361 {
362 returnmap.values[index];
363 }
364 365 366 /***************************************************************************
367 368 foreach iterator over the codes and descriptions of the enum.
369 370 ***************************************************************************/371 372 staticpublicintopApply ( scopeintdelegate ( refBaseTypecode, refistringdesc ) dg )
373 {
374 intres;
375 foreach ( enum_code, enum_description; map )
376 {
377 res = dg(enum_code, enum_description);
378 }
379 returnres;
380 }
381 382 383 /***************************************************************************
384 385 foreach iterator over the codes and descriptions of the enum and their
386 indices.
387 388 ***************************************************************************/389 390 staticpublicintopApply ( scopeintdelegate ( refsize_tindex, refBaseTypecode, refistringdesc ) dg )
391 {
392 intres;
393 foreach ( index, enum_code, enum_description; map )
394 {
395 res = dg(index, enum_code, enum_description);
396 }
397 returnres;
398 }
399 }
400 401 unittest402 {
403 aliasSmartEnumCore!(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 privatetemplateEnumValuesList ( T ... )
424 {
425 staticif ( T.length == 1 )
426 {
427 staticimmutableEnumValuesList = T[0].name ~ "=" ~ CTFE.toString(T[0].value);
428 }
429 else430 {
431 staticimmutableEnumValuesList = 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 privatetemplateDeclareEnum ( T ... )
457 {
458 staticimmutableDeclareEnum = "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 privatetemplateInitialiseMap ( T ... )
480 {
481 staticif ( T.length == 1 )
482 {
483 staticimmutableInitialiseMap = `map["` ~ T[0].name ~ `"]=` ~ T[0].name ~ ";";
484 }
485 else486 {
487 staticimmutableInitialiseMap = `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 privatetemplateStaticThis ( T ... )
516 {
517 staticimmutableStaticThis = "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 privatetemplateMaxValue ( T ... )
531 {
532 staticif ( T.length == 1 )
533 {
534 staticimmutableMaxValue = T[0].value;
535 }
536 else537 {
538 staticimmutableMaxValue = 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 privatetemplateMinValue ( T ... )
553 {
554 staticif ( T.length == 1 )
555 {
556 staticimmutableMinValue = T[0].value;
557 }
558 else559 {
560 staticimmutableMinValue = 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 privatetemplateLongestName ( T ... )
576 {
577 staticif ( T.length == 1 )
578 {
579 staticimmutableLongestName = T[0].name.length;
580 }
581 else582 {
583 staticimmutableLongestName = 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 privatetemplateShortestName ( T ... )
599 {
600 staticif ( T.length == 1 )
601 {
602 staticimmutableShortestName = T[0].name.length;
603 }
604 else605 {
606 staticimmutableShortestName = 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 privatetemplateDeclareConstants ( T ... )
631 {
632 staticimmutableistringDeclareConstants =
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 privatetemplateMixinCore ( T ... )
658 {
659 staticimmutableMixinCore = "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 publictemplateSmartEnum ( istringName, T ... )
683 {
684 staticif ( T.length > 0 )
685 {
686 staticimmutableSmartEnum = "class " ~ Name ~ " : ISmartEnum { " ~ DeclareEnum!(T) ~
687 DeclareConstants!(T) ~ StaticThis!(T) ~ MixinCore!(T) ~ "}";
688 }
689 else690 {
691 staticassert(false, "Cannot create a SmartEnum with no entries!");
692 }
693 }
694 695 ///696 unittest697 {
698 aliasSmartEnumValue!(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((1inCommands) !isnull);
712 test((Commands.secondinCommands) !isnull);
713 test((5inCommands) isnull);
714 715 test(("first"inCommands) !isnull);
716 test(("third"inCommands) isnull);
717 718 size_tcount;
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 publictemplateAutoSmartEnum ( istringName, BaseType, Strings ... )
739 {
740 staticassert ( is(typeof(Strings[0]) : istring), "AutoSmartEnum - please only give immutable strings as template parameters");
741 742 staticimmutableAutoSmartEnum = autoSmartEnumImpl(Name, BaseType.stringof,
743 Strings);
744 }
745 746 unittest747 {
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 values754 mstringname1 = "a".dup, name2 = "b".dup, name3 = "c".dup;
755 Namen;
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 privateistringautoSmartEnumImpl ( istringname, istringbasetype,
781 istring[] members ... )
782 {
783 // Create two strings, one to declare the members, and one to populate784 // the map.785 // Also determine the minimum and maximum length of each member name786 787 istringdeclstr = null;
788 istringmapstr = null;
789 790 longmaxlen = members[0].length;
791 longminlen = 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 unittest829 {
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 importocean.core.Enforce;
847 848 importocean.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 publicstructTwoWayMap ( 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 staticif ( is(A : cstring) )
876 {
877 staticassert(false, "TwoWayMap does not support mapping to a string type");
878 }
879 880 881 /***************************************************************************
882 883 Type aliases.
884 885 ***************************************************************************/886 887 publicaliasAKeyType;
888 publicaliasistringValueType;
889 890 891 /***************************************************************************
892 893 Associative arrays which store the mappings.
894 895 ***************************************************************************/896 897 privateValueType[KeyType] a_to_b;
898 privateKeyType[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 privateKeyType[] keys_list;
912 privateValueType[] values_list;
913 914 915 /***************************************************************************
916 917 Optional indices for mapped items.
918 919 ***************************************************************************/920 921 privatesize_t[KeyType] a_to_index; // A to index in keys_list922 privatesize_t[ValueType] b_to_index; // B to index in values_list923 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 publicvoidopAssign ( 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 ~= *(binthis.b_to_a);
970 this.values_list ~= *(ainthis.a_to_b);
971 }
972 973 this.updateIndices();
974 }
975 976 publicvoidopAssign ( 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 ~= *(binthis.b_to_a);
988 this.values_list ~= *(ainthis.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 publicvoidopIndexAssign ( KeyTypea, ValueTypeb )
1006 out1007 {
1008 assert(this.a_to_index[a] < this.keys_list.length);
1009 assert(this.b_to_index[b] < this.values_list.length);
1010 }
1011 do1012 {
1013 autoalready_exists = !!(ainthis.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 ~= *(binthis.b_to_a);
1021 this.values_list ~= *(ainthis.a_to_b);
1022 }
1023 1024 this.updateIndices();
1025 }
1026 1027 publicvoidopIndexAssign ( ValueTypeb, KeyTypea )
1028 out1029 {
1030 assert(this.a_to_index[a] < this.keys_list.length);
1031 assert(this.b_to_index[b] < this.values_list.length);
1032 }
1033 do1034 {
1035 autoalready_exists = !!(ainthis.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 ~= *(binthis.b_to_a);
1043 this.values_list ~= *(ainthis.a_to_b);
1044 }
1045 1046 this.updateIndices();
1047 }
1048 1049 1050 /***************************************************************************
1051 1052 Rehashes the mappings.
1053 1054 ***************************************************************************/1055 1056 publicvoidrehash ( )
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 publicKeyType* opBinaryRight ( istringop : "in" ) ( cstringb )
1080 {
1081 returnbinthis.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 publicValueType* opBinaryRight ( istringop : "in" ) ( KeyTypea )
1100 {
1101 returnainthis.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 publicKeyTypeopIndex ( cstringb )
1122 {
1123 returnthis.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 publicValueTypeopIndex ( KeyTypea )
1144 {
1145 returnthis.a_to_b[a];
1146 }
1147 1148 1149 /***************************************************************************
1150 1151 Returns:
1152 number of items in the map
1153 1154 ***************************************************************************/1155 1156 publicsize_tlength ( )
1157 {
1158 returnthis.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 publicKeyType[] keys ( )
1170 {
1171 returnthis.keys_list;
1172 }
1173 1174 1175 /***************************************************************************
1176 1177 Returns:
1178 dynamic array containing all map elements of type B
1179 1180 ***************************************************************************/1181 1182 publicValueType[] values ( )
1183 {
1184 returnthis.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 publicintopApply ( scopeintdelegate ( refKeyTypea, refValueTypeb ) dg )
1198 {
1199 intres;
1200 foreach ( a, b; this.a_to_b )
1201 {
1202 res = dg(a, b);
1203 }
1204 returnres;
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 publicintopApply ( scopeintdelegate ( refsize_tindex, refKeyTypea, refValueTypeb ) dg )
1218 {
1219 intres;
1220 foreach ( a, b; this.a_to_b )
1221 {
1222 autoindex = this.indexOf(a);
1223 verify(index !isnull);
1224 1225 res = dg(*index, a, b);
1226 }
1227 returnres;
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 publicsize_t* indexOf ( KeyTypea )
1246 {
1247 autoindex = ainthis.a_to_index;
1248 enforce(index, typeof(this).stringof ~ ".indexOf - element not present in map");
1249 returnindex;
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 publicsize_t* indexOf ( cstringb )
1268 {
1269 autoindex = binthis.b_to_index;
1270 enforce(index, typeof(this).stringof ~ ".indexOf - element not present in map");
1271 returnindex;
1272 }
1273 1274 1275 /***************************************************************************
1276 1277 Updates the index arrays when the mapping is altered.
1278 1279 ***************************************************************************/1280 1281 privatevoidupdateIndices ( )
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 }