1 /**
2     Provides compile time utilities that can query a type's members
3 */
4 module bolts.members;
5 
6 import bolts.internal;
7 
8 private template AliasesOf(T, membersTuple...) {
9     import std.meta: Alias, staticMap, ApplyLeft;
10     alias getMember(T, string name) = Alias!(__traits(getMember, T, name));
11     alias mapMembers(T, names...) = staticMap!(ApplyLeft!(getMember, T), names);
12     alias AliasesOf = mapMembers!(T, membersTuple);
13 }
14 
15 /**
16     Returns a list of member functions of T
17 
18     You can retireve them as a tuple of aliases, or strings
19 */
20 template memberFunctionsOf(T) {
21     import bolts.traits: hasFunctionMember;
22     import bolts.meta: FilterMembersOf;
23 
24     /// Get as tuple of strings
25     alias asStrings = FilterMembersOf!(T, hasFunctionMember);
26 
27     /// Get as a tuple of aliases
28     alias asAliases = AliasesOf!(T, memberFunctionsOf!T.asStrings);
29 }
30 
31 ///
32 unittest {
33     import std.meta: AliasSeq;
34     struct A {
35         void opCall() {}
36         void g() {}
37     }
38 
39     struct B {
40         int m;
41         A a;
42         alias a this;
43         void f0() {}
44         void f1() {}
45     }
46 
47     immutable array = [memberFunctionsOf!B.asStrings];
48     alias strings = memberFunctionsOf!B.asStrings;
49     alias aliases = memberFunctionsOf!B.asAliases;
50 
51     static assert(array == ["f0", "f1"]);
52     static assert(strings == AliasSeq!("f0", "f1"));
53 
54     static assert(is(typeof(array) == immutable string[]));
55     static assert(is(typeof(strings) == AliasSeq!(string, string)));
56     static assert(is(typeof(aliases) == AliasSeq!(typeof(B.f0), typeof(B.f1))));
57 }
58 
59 /**
60     Returns a list of all the static members of a type
61 
62     You can retireve them as a sequence of aliases, or strings.
63 
64     See_Also:
65      - https://forum.dlang.org/post/duvxnpwnuphuxlrkjplh@forum.dlang.org
66 */
67 template staticMembersOf(T) {
68     import std.traits: hasStaticMember;
69     import bolts.meta: FilterMembersOf;
70 
71     /// Get as tuple of strings
72     alias asStrings = FilterMembersOf!(T, hasStaticMember);
73 
74     /// Get as a tuple of aliases
75     alias asAliases = AliasesOf!(T, staticMembersOf!T.asStrings);
76 }
77 
78 ///
79 unittest {
80     import std.meta: AliasSeq, Alias;
81     import bolts.traits: TypesOf;
82     struct S {
83         static void s0() {}
84         static int s1 = 3;
85         static immutable int s2 = 3;
86         enum e = 9;
87         void f() {}
88         int i = 3;
89     }
90 
91     immutable array = [staticMembersOf!S.asStrings];
92     alias strings = staticMembersOf!S.asStrings;
93     alias aliases = staticMembersOf!S.asAliases;
94 
95     static assert(array == ["s0", "s1", "s2"]);
96     static assert(strings == AliasSeq!("s0", "s1", "s2"));
97 
98     static assert(is(typeof(array) == immutable string[]));
99     static assert(is(typeof(strings) == AliasSeq!(string, string, string)));
100     static assert(is(typeof(aliases) == AliasSeq!(typeof(S.s0), typeof(S.s1), typeof(S.s2))));
101 }
102 
103 /**
104     Used to extract details about a specific member
105 
106     Available member traits:
107         $(LI `exists`)
108         $(LI `self`)
109         $(LI `isProperty`)
110         $(LI `protection`)
111 
112     Params:
113         Params[0] = type or alias to instance of a type
114         Params[1] = name of member
115 
116 */
117 template member(Params...) if (Params.length == 2) {
118     private enum name = Params[1];
119     private alias T = bolts.traits.TypesOf!Params[0];
120     private alias ResolvedType = ResolvePointer!T;
121 
122     /**
123         True if the member field exists
124     */
125     enum exists = __traits(hasMember, ResolvedType, name);
126 
127     /**
128         Aliases to the member if it exists
129     */
130     static if (exists) {
131         alias self = __traits(getMember, ResolvedType, name);
132     } else {
133         template self() {
134             static assert(
135                 0,
136                 "Type '" ~ T.stringof ~ "' does not have member '" ~ name ~ "'."
137             );
138         }
139     }
140 
141     /**
142         See: `bolts.traits.protectionLevel`
143     */
144     static if (exists) {
145         enum protection = from.bolts.traits.protectionLevel!self;
146     }
147 
148     /**
149         See: `bolts.traits.hasProperty`
150     */
151     static if (exists) {
152         enum isProperty = from.bolts.traits.hasProperty!(ResolvedType, name);
153     } else {
154         enum isProperty = false;
155     }
156 
157     /**
158         See `bolts.traits.propertySemantics`
159     */
160     static if (exists && isProperty) {
161         enum propertySemantics = from.bolts.traits.propertySemantics!self;
162     }
163 }
164 
165 ///
166 unittest {
167     import bolts.traits: ProtectionLevel, PropertySemantics;
168     static struct S {
169         public int publicI;
170         protected int protectedI;
171         private @property int readPropI() { return protectedI; }
172     }
173 
174     // Check the protection level of various members
175     static assert(member!(S, "publicI").protection == ProtectionLevel.public_);
176     static assert(member!(S, "protectedI").protection == ProtectionLevel.protected_);
177     static assert(member!(S, "readPropI").protection == ProtectionLevel.private_);
178 
179     // Check if any are properties
180     static assert(!member!(S, "na").isProperty);
181     static assert(!member!(S, "publicI").isProperty);
182     static assert( member!(S, "readPropI").isProperty);
183 
184     // Check their semantics
185     static assert(member!(S, "readPropI").propertySemantics == PropertySemantics.r);
186 }