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 }