1 /// Automated accessor/property generation with support for flags. 2 module tern.accessors; 3 4 /// Attribute signifying an enum uses flags. 5 public enum flags; 6 /// Attribute signifying an enum should not have properties made. 7 public enum exempt; 8 9 /// Template mixin for auto-generating properties. 10 /// Assumes standardized prefixes! (m_ for backing fields, k for masked enum values) 11 /// Assumes standardized postfixes! (MASK or Mask for masks) 12 // TODO: Overloads (allow devs to write specifically a get/set and have the counterpart auto generated) 13 // ~Bitfield exemption?~ 14 // Conditional get/sets? (check flag -> return a default) (default attribute?) 15 // Flag get/sets from pre-existing get/sets (see methodtable.d relatedTypeKind) 16 // Auto import types (generics!!) 17 // Allow for indiv. get/sets without needing both declared 18 /// Does not support multiple fields with the same enum type! 19 public template accessors() 20 { 21 import std.traits; 22 import std.string; 23 import std.meta; 24 25 static foreach (string member; __traits(allMembers, typeof(this))) 26 { 27 static if (member.startsWith("m_") && !__traits(compiles, { enum _ = mixin(member); }) && 28 isMutable!(TypeOf!(typeof(this), member)) && 29 (isFunction!(__traits(getMember, typeof(this), member)) || staticIndexOf!(exempt, __traits(getAttributes, __traits(getMember, typeof(this), member))) == -1)) 30 { 31 static if (!__traits(hasMember, typeof(this), member[2..$])) 32 { 33 static if (!__traits(hasMember, typeof(this), member[2..$])) 34 { 35 mixin("final @property "~fullyQualifiedName!(TypeOf!(typeof(this), member))~" "~member[2..$]~"() { return "~member~"; }"); 36 mixin("final @property "~fullyQualifiedName!(TypeOf!(typeof(this), member))~" "~member[2..$]~"("~fullyQualifiedName!(TypeOf!(typeof(this), member))~" val) { "~member~" = val; return "~member~"; }"); 37 } 38 39 // Flags 40 static if (is(TypeOf!(typeof(this), member) == enum) && 41 !seqContains!(exempt, __traits(getAttributes, TypeOf!(typeof(this), member))) && 42 seqContains!(flags, __traits(getAttributes, TypeOf!(typeof(this), member)))) 43 { 44 static foreach (string flag; __traits(allMembers, TypeOf!(this, member))) 45 { 46 static if (flag.startsWith('k')) 47 { 48 static foreach_reverse (string mask; __traits(allMembers, TypeOf!(this, member))[0..staticIndexOf!(flag, __traits(allMembers, TypeOf!(this, member)))]) 49 { 50 static if (mask.endsWith("Mask") || mask.endsWith("MASK")) 51 { 52 static if (!__traits(hasMember, typeof(this), "is"~flag[1..$])) 53 { 54 // @property bool isEastern()... 55 mixin("final @property bool is"~flag[1..$]~"() { return ("~member[2..$]~" & "~fullyQualifiedName!(TypeOf!(this, member))~"."~mask~") == "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~"; }"); 56 // @property bool isEastern(bool state)... 57 mixin("final @property bool is"~flag[1..$]~"(bool state) { return ("~member[2..$]~" = cast("~fullyQualifiedName!(TypeOf!(this, member))~")(state ? ("~member[2..$]~" & "~fullyQualifiedName!(TypeOf!(this, member))~"."~mask~") | "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~" : ("~member[2..$]~" & "~fullyQualifiedName!(TypeOf!(this, member))~"."~mask~") & ~"~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~")) == "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~"; }"); 58 } 59 } 60 61 } 62 } 63 else 64 { 65 static if (!__traits(hasMember, typeof(this), "is"~flag)) 66 { 67 // @property bool isEastern()... 68 mixin("final @property bool is"~flag~"() { return ("~member[2..$]~" & "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~") != 0; }"); 69 // @property bool isEastern(bool state)... 70 mixin("final @property bool is"~flag~"(bool state) { return ("~member[2..$]~" = cast("~fullyQualifiedName!(TypeOf!(this, member))~")(state ? ("~member[2..$]~" | "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~") : ("~member[2..$]~" & ~"~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~"))) != 0; }"); 71 } 72 } 73 } 74 } 75 76 // Non-flags 77 static if (is(TypeOf!(typeof(this), member) == enum) && 78 !seqContains!(exempt, __traits(getAttributes, TypeOf!(typeof(this), member))) && 79 !seqContains!(flags, __traits(getAttributes, TypeOf!(typeof(this), member)))) 80 { 81 static foreach (string flag; __traits(allMembers, TypeOf!(this, member))) 82 { 83 static if (!__traits(hasMember, typeof(this), "is"~flag)) 84 { 85 // @property bool Eastern()... 86 mixin("final @property bool is"~flag~"() { return "~member[2..$]~" == "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~"; }"); 87 // @property bool Eastern(bool state)... 88 mixin("final @property bool is"~flag~"(bool state) { return ("~member[2..$]~" = "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~") == "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~"; }"); 89 } 90 } 91 } 92 } 93 } 94 } 95 }