1 /*******************************************************************************
2 3 Mixin for an enum class with the following basic features:
4 * Contains an enum, called E, with members specified by an associative
5 array passed to the mixin.
6 * Implements an interface, IEnum, with common shared methods:
7 * opIndex: look up an enum member's name by its value and
8 vice-versa.
9 * opIn_r: check whether a value (int) or name (char[]) is a member
10 of the enum.
11 * opApply: iteration over over the names & values of the enum's
12 members.
13 * length: returns the number of members in the enum.
14 * min & max: return the minimum/maximum value of the enum's members.
15 * A static opCall() method which returns a singleton instance of the
16 class. This is the most convenient means of calling the methods listed
17 above.
18 19 Basic usage example:
20 21 ---
22 23 // Define enum class by implementing IEnum and mixing in EnumBase with
24 // an associative array defining the enum members
25 class Commands : IEnum
26 {
27 // Note: the [] after the first string ensures that the associative
28 // array is of type int[char[]], not int[char[3]].
29 mixin EnumBase!([
30 "Get"[]:1,
31 "Put":2,
32 "Remove":3
33 ]);
34 }
35 36 // Look up enum member names by value. (Note that the singleton instance
37 // of the enum class is passed, using the static opCall method.)
38 assert(Commands()["Get"] == 1);
39 40 // Look up enum member values by name
41 assert(Commands()[1] == "Get");
42 43 // Check whether a value is in the enum
44 assert(!(5 in Commands()));
45 46 // Check whether a name is in the enum
47 assert(!("Delete" in Commands()));
48 49 // Iterate over enum members
50 import ocean.io.Stdout;
51 52 foreach ( n, v; Commands() )
53 {
54 Stdout.formatln("{}: {}", n, v);
55 }
56 57 ---
58 59 The mixin also supports the following more advanced features:
60 * One enum class can be inherited from another, using standard class
61 inheritance. The enum members in a derived enum class extend those of
62 the super class.
63 * The use of normal class inheritance, along with the IEnum interface,
64 allows enum classes to be used abstractly.
65 66 Advanced usage example:
67 68 ---
69 70 import ocean.core.Enum;
71 72 // Basic enum class
73 class BasicCommands : IEnum
74 {
75 mixin EnumBase!([
76 "Get"[]:1,
77 "Put":2,
78 "Remove":3
79 ]);
80 }
81 82 // Inherited enum class
83 class ExtendedCommands : BasicCommands
84 {
85 mixin EnumBase!([
86 "GetAll"[]:4,
87 "RemoveAll":5
88 ]);
89 }
90 91 // Check for a few names.
92 assert("Get" in BasicCommands());
93 assert("Get" in ExtendedCommands());
94 assert(!("GetAll" in BasicCommands()));
95 assert("GetAll" in ExtendedCommands());
96 97 // Example of abstract usage of enum classes
98 import ocean.io.Stdout;
99 100 void printEnumMembers ( IEnum e )
101 {
102 foreach ( n, v; e )
103 {
104 Stdout.formatln("{}: {}", n, v);
105 }
106 }
107 108 printEnumMembers(BasicCommands());
109 printEnumMembers(ExtendedCommands());
110 111 ---
112 113 TODO: does it matter that the enum values are always int? We could add a
114 template parameter to specify the base type, but I think it'd be a shame to
115 make things more complex. IEnum would have to become a template then.
116 117 Copyright:
118 Copyright (c) 2009-2016 dunnhumby Germany GmbH.
119 All rights reserved.
120 121 License:
122 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
123 Alternatively, this file may be distributed under the terms of the Tango
124 3-Clause BSD License (see LICENSE_BSD.txt for details).
125 126 *******************************************************************************/127 128 moduleocean.core.Enum;
129 130 131 132 importocean.transition;
133 importocean.core.Test;
134 135 /*******************************************************************************
136 137 Interface defining the basic functionality of an enum class.
138 139 *******************************************************************************/140 141 publicinterfaceIEnum142 {
143 /***************************************************************************
144 145 Aliases for the types of an enum class' names & values.
146 147 ***************************************************************************/148 149 publicaliasistringName;
150 publicaliasintValue;
151 152 153 /***************************************************************************
154 155 Looks up an enum member's name from its value.
156 157 Params:
158 v = value to look up
159 160 Returns:
161 pointer to corresponding name, or null if value doesn't exist in
162 enum
163 164 ***************************************************************************/165 166 publicName* opIn_r ( Valuev );
167 168 169 /***************************************************************************
170 171 Looks up an enum member's value from its name.
172 173 Params:
174 n = name to look up
175 176 Returns:
177 pointer to corresponding value, or null if name doesn't exist in
178 enum
179 180 ***************************************************************************/181 182 publicValue* opIn_r ( Namen );
183 184 185 /***************************************************************************
186 187 Looks up an enum member's name from its value, using opIndex.
188 189 Params:
190 v = value to look up
191 192 Returns:
193 corresponding name
194 195 Throws:
196 ArrayBoundsException if value doesn't exist in enum
197 198 ***************************************************************************/199 200 publicNameopIndex ( Valuev );
201 202 203 /***************************************************************************
204 205 Looks up an enum member's value from its name, using opIndex.
206 207 Params:
208 n = name to look up
209 210 Returns:
211 corresponding value
212 213 Throws:
214 ArrayBoundsException if name doesn't exist in enum
215 216 ***************************************************************************/217 218 publicValueopIndex ( Namen );
219 220 221 /***************************************************************************
222 223 Returns:
224 the number of members in the enum
225 226 ***************************************************************************/227 228 publicsize_tlength ( );
229 230 231 /***************************************************************************
232 233 Returns:
234 the lowest value in the enum
235 236 ***************************************************************************/237 238 Valuemin ( );
239 240 241 /***************************************************************************
242 243 Returns:
244 the highest value in the enum
245 246 ***************************************************************************/247 248 Valuemax ( );
249 250 251 /***************************************************************************
252 253 foreach iteration over the names and values in the enum.
254 255 ***************************************************************************/256 257 publicintopApply ( scopeintdelegate ( refConst!(Name) name,
258 refConst!(Value) value ) dg );
259 260 261 /***************************************************************************
262 263 foreach iteration over the names and values in the enum and their
264 indices.
265 266 ***************************************************************************/267 268 publicintopApply ( scopeintdelegate ( refsize_ti, refConst!(Name) name,
269 refConst!(Value) value ) dg );
270 }
271 272 273 /*******************************************************************************
274 275 Template which evaluates to a string containing the code for a list of enum
276 members, as specified by the first two members of the passed tuple, which
277 must be an array of strings and an array of integers, respectively. The
278 strings specify the names of the enum members, and the integers their
279 values.
280 281 This template is public for technical reason, and should not be needed
282 in client code - See IEnum and EnumBase for template / interface you
283 should use.
284 285 Params:
286 T = tuple:
287 T[0] must be an array of strings
288 T[1] must be an array of ints
289 (Note that the template accepts a tuple purely as a workaround for the
290 compiler's inability to handle templates which accept values of types
291 such as char[][] and int[].)
292 293 *******************************************************************************/294 295 publictemplateEnumValues ( size_ti, T ... )
296 {
297 staticassert(T.length == 2);
298 staticassert(is(typeof(T[0]) : Const!(istring[])));
299 staticassert(is(typeof(T[1]) : Const!(int[])));
300 301 staticif ( i == T[0].length - 1 )
302 {
303 staticimmutableEnumValues = T[0][i] ~ "=" ~ T[1][i].stringof;
304 }
305 else306 {
307 staticimmutableEnumValues = T[0][i] ~ "=" ~ T[1][i].stringof ~ ","308 ~ EnumValues!(i + 1, T);
309 }
310 }
311 312 313 /*******************************************************************************
314 315 Template which evaluates to a size_t corresponding to the index in the type
316 tuple T which contains a class implementing the IEnum interface. If no such
317 type exists in T, then the template evaluates to T.length.
318 319 This template is public for technical reason, and should not be needed
320 in client code - See IEnum and EnumBase for template / interface you
321 should use.
322 323 Params:
324 i = recursion index over T
325 T = type tuple
326 327 *******************************************************************************/328 329 publictemplateSuperClassIndex ( size_ti, T ... )
330 {
331 staticif ( i == T.length )
332 {
333 staticimmutablesize_tSuperClassIndex = i;
334 }
335 else336 {
337 staticif ( is(T[i] == class) && is(T[i] : IEnum) )
338 {
339 staticimmutablesize_tSuperClassIndex = i;
340 }
341 else342 {
343 staticimmutablesize_tSuperClassIndex = SuperClassIndex!(i + 1, T);
344 }
345 }
346 }
347 348 349 /*******************************************************************************
350 351 Template mixin to add enum functionality to a class.
352 353 Note that the [0..$] which is used in places in this method is a workaround
354 for various weird compiler issues / segfaults.
355 356 Params:
357 T = tuple:
358 T[0] must be an associative array of type int[char[]]
359 (Note that the template accepts a tuple purely as a workaround for the
360 compiler's inability to handle templates which accept associative array
361 values.)
362 363 TODO: adapt to accept *either* an AA or a simple list of names (for an
364 auto-enum with values starting at 0).
365 366 *******************************************************************************/367 368 publictemplateEnumBase ( T ... )
369 {
370 importocean.transition;
371 372 aliasIEnum.NameName;
373 aliasIEnum.ValueValue;
374 375 /***************************************************************************
376 377 Ensure that the class into which this template is mixed is an IEnum.
378 379 ***************************************************************************/380 381 staticassert(is(typeof(this) : IEnum));
382 383 384 /***************************************************************************
385 386 Ensure that the tuple T contains a single element which is of type
387 int[char[]].
388 389 ***************************************************************************/390 391 staticassert(T.length == 1);
392 staticassert(is(typeof(T[0].keys) : Const!(char[][])));
393 staticassert(is(typeof(T[0].values) : Const!(int[])));
394 395 396 /***************************************************************************
397 398 Constants determining whether this class is derived from another class
399 which implements IEnum.
400 401 ***************************************************************************/402 403 staticif ( is(typeof(this) S == super) )
404 {
405 privatestaticimmutablesuper_class_index = SuperClassIndex!(0, S);
406 407 privatestaticimmutableis_derived_enum = super_class_index < S.length;
408 }
409 else410 {
411 privatestaticimmutableis_derived_enum = false;
412 }
413 414 415 /***************************************************************************
416 417 Constant arrays of enum member names and values.
418 419 If the class into which this template is mixed has a super class which
420 is also an IEnum, the name and value arrays of the super class are
421 concatenated with those in the associative array in T[0].
422 423 ***************************************************************************/424 425 staticif ( is_derived_enum )
426 {
427 publicstaticimmutable_internal_names =
428 S[super_class_index]._internal_names[0..$] ~ T[0].keys[0..$];
429 publicstaticimmutable_internal_values =
430 S[super_class_index]._internal_values[0..$] ~ T[0].values[0..$];
431 }
432 else433 {
434 publicstaticimmutable_internal_names = T[0].keys;
435 publicstaticimmutable_internal_values = T[0].values;
436 }
437 438 staticassert(_internal_names.length == _internal_values.length);
439 440 privatestaticnames = _internal_names;
441 privatestaticvalues = _internal_values;
442 443 /***************************************************************************
444 445 The actual enum, E.
446 447 ***************************************************************************/448 449 mixin("enum E {" ~ EnumValues!(0, _internal_names[0..$],
450 _internal_values[0..$]) ~ "}");
451 452 453 /***************************************************************************
454 455 Internal maps from names <-> values. The maps are filled in the static
456 constructor.
457 458 ***************************************************************************/459 460 staticprotectedValue[Name] n_to_v;
461 staticprotectedName[Value] v_to_n;
462 463 staticthis ( )
464 {
465 foreach ( i, n; names )
466 {
467 n_to_v[n] = values[i];
468 }
469 n_to_v.rehash;
470 471 foreach ( i, v; values )
472 {
473 v_to_n[v] = names[i];
474 }
475 v_to_n.rehash;
476 }
477 478 479 /***************************************************************************
480 481 Protected constructor, prevents external instantiation. (Use the
482 singleton instance returned by opCall().)
483 484 ***************************************************************************/485 486 protectedthis ( )
487 {
488 staticif ( is_derived_enum )
489 {
490 super();
491 }
492 }
493 494 495 /***************************************************************************
496 497 Singleton instance of this class (used to access the IEnum methods).
498 499 ***************************************************************************/500 501 privatealiastypeof(this) This;
502 503 staticprivateThisinst;
504 505 506 /***************************************************************************
507 508 Returns:
509 class singleton instance
510 511 ***************************************************************************/512 513 staticpublicThisopCall ( )
514 {
515 if ( !inst )
516 {
517 inst = newThis;
518 }
519 returninst;
520 }
521 522 523 /***************************************************************************
524 525 Looks up an enum member's name from its value.
526 527 Params:
528 v = value to look up
529 530 Returns:
531 pointer to corresponding name, or null if value doesn't exist in
532 enum
533 534 ***************************************************************************/535 536 publicoverrideName* opIn_r ( Valuev )
537 {
538 returnvinv_to_n;
539 }
540 541 542 /***************************************************************************
543 544 Looks up an enum member's value from its name.
545 546 Params:
547 n = name to look up
548 549 Returns:
550 pointer to corresponding value, or null if name doesn't exist in
551 enum
552 553 ***************************************************************************/554 555 publicoverrideValue* opIn_r ( Namen )
556 {
557 returnninn_to_v;
558 }
559 560 561 /***************************************************************************
562 563 Looks up an enum member's name from its value, using opIndex.
564 565 Params:
566 v = value to look up
567 568 Returns:
569 corresponding name
570 571 Throws:
572 (in non-release builds) ArrayBoundsException if value doesn't exist
573 in enum
574 575 ***************************************************************************/576 577 publicoverrideNameopIndex ( Valuev )
578 {
579 returnv_to_n[v];
580 }
581 582 583 /***************************************************************************
584 585 Looks up an enum member's value from its name, using opIndex.
586 587 Params:
588 n = name to look up
589 590 Returns:
591 corresponding value
592 593 Throws:
594 (in non-release builds) ArrayBoundsException if value doesn't exist
595 in enum
596 597 ***************************************************************************/598 599 publicoverrideValueopIndex ( Namen )
600 {
601 returnn_to_v[n];
602 }
603 604 605 /***************************************************************************
606 607 Returns:
608 the number of members in the enum
609 610 ***************************************************************************/611 612 publicoverridesize_tlength ( )
613 {
614 returnnames.length;
615 }
616 617 618 /***************************************************************************
619 620 Returns:
621 the lowest value in the enum
622 623 ***************************************************************************/624 625 publicoverrideValuemin ( )
626 {
627 returnE.min;
628 }
629 630 631 /***************************************************************************
632 633 Returns:
634 the highest value in the enum
635 636 ***************************************************************************/637 638 publicoverrideValuemax ( )
639 {
640 returnE.max;
641 }
642 643 644 /***************************************************************************
645 646 foreach iteration over the names and values in the enum.
647 648 Note that the iterator passes the enum values as type Value (i.e. int),
649 rather than values of the real enum E. This is in order to keep the
650 iteration functionality in the IEnum interface, which knows nothing of
651 E.
652 653 ***************************************************************************/654 655 publicoverrideintopApply ( scopeintdelegate ( refConst!(Name) name,
656 refConst!(Value) value ) dg )
657 {
658 intres;
659 foreach ( i, name; this.names )
660 {
661 res = dg(name, values[i]);
662 if ( res ) break;
663 }
664 returnres;
665 }
666 667 668 /***************************************************************************
669 670 foreach iteration over the names and values in the enum and their
671 indices.
672 673 Note that the iterator passes the enum values as type Value (i.e. int),
674 rather than values of the real enum E. This is in order to keep the
675 iteration functionality in the IEnum interface, which knows nothing of
676 E.
677 678 ***************************************************************************/679 680 publicoverrideintopApply ( scopeintdelegate ( refsize_ti,
681 refConst!(Name) name, refConst!(Value) value ) dg )
682 {
683 intres;
684 foreach ( i, name; this.names )
685 {
686 res = dg(i, name, values[i]);
687 ++i;
688 if ( res ) break;
689 }
690 returnres;
691 }
692 }
693 694 695 696 /*******************************************************************************
697 698 Unit test.
699 700 Tests:
701 * All IEnum interface methods.
702 * Enum class inheritance.
703 704 *******************************************************************************/705 706 version ( UnitTest )
707 {
708 /***************************************************************************
709 710 Runs a series of tests to check that the specified enum type contains
711 members with the specified names and values. The name and value lists
712 are assumed to be in the same order (i.e. names[i] corresponds to
713 values[i]).
714 715 Params:
716 E = enum type to check
717 718 Params:
719 names = list of names expected to be in the enum
720 values = list of values expected to be in the enum
721 722 ***************************************************************************/723 724 voidcheckEnum ( E : IEnum ) ( istring[] names, int[] values )
725 {
726 test(names.length == values.length);
727 test(names.length);
728 test(E.names == names);
729 test(E.values == values);
730 731 // opIn_r lookup by name732 foreach ( i, n; names )
733 {
734 test(ninE());
735 test(*(ninE()) == values[i]);
736 }
737 738 // opIn_r lookup by value739 foreach ( i, v; values )
740 {
741 test(vinE());
742 test(*(vinE()) == names[i]);
743 }
744 745 // opIndex lookup by name746 foreach ( i, n; names )
747 {
748 test(E()[n] == values[i]);
749 }
750 751 // opIndex lookup by value752 foreach ( i, v; values )
753 {
754 test(E()[v] == names[i]);
755 }
756 757 // length758 test(E().length == names.length);
759 760 // Check min & max761 intmin = int.max;
762 intmax = int.min;
763 foreach ( v; values )
764 {
765 if ( v < min ) min = v;
766 if ( v > max ) max = v;
767 }
768 test(E().min == min);
769 test(E().max == max);
770 771 // opApply 1772 size_ti;
773 foreach ( n, v; E() )
774 {
775 test(n == names[i]);
776 test(v == values[i]);
777 i++;
778 }
779 780 // opApply 2781 foreach ( i, n, v; E() )
782 {
783 test(n == names[i]);
784 test(v == values[i]);
785 }
786 }
787 788 classEnum1 : IEnum789 {
790 mixinEnumBase!(["a"[]:1, "b":2, "c":3]);
791 }
792 793 classEnum2 : Enum1794 {
795 mixinEnumBase!(["d"[]:4, "e":5, "f":6]);
796 }
797 }
798 799 unittest800 {
801 checkEnum!(Enum1)(["a", "b", "c"], [1, 2, 3]);
802 checkEnum!(Enum2)(["a", "b", "c", "d", "e", "f"], [1, 2, 3, 4, 5, 6]);
803 }