1 /**
2 *   Copyright: © 2014-2015 Anton Gushcha
3 *   License: Subject to the terms of the Boost 1.0 license as specified in LICENSE file
4 *   Authors: Anton Gushcha <ncrashed@gmail.com>
5 *
6 *   Template for aggregates members expection - extension of std.traits;
7 */
8 module stribog.meta.members;
9 
10 import std.traits : isIntegral;
11 import std.typetuple : staticIndexOf;
12 import stribog.meta.base;
13 import stribog.meta.filter;
14 
15 /**
16 *   More useful version of allMembers trait, that returns only
17 *   fields and methods of class/struct/interface/union without
18 *   service members like constructors and Object members.
19 *
20 *   Note: if Object methods are explicitly override in $(B T) 
21 *   (not other base class), then the methods are included into
22 *   the result.
23 */
24 template fieldsAndMethods(T)  
25 {
26     static if(is(T == class) || is(T == struct) || is(T == interface) || is(T == union))
27     {
28         /// Getting all inherited members from Object exluding overrided
29         private template derivedFromObject()
30         {
31             alias objectMembers = ExpressionList!(__traits(allMembers, Object));
32             alias derivedMembers = ExpressionList!(__traits(derivedMembers, T));
33             
34             private template removeDerived(string name)
35             {
36                 enum removeDerived = staticIndexOf!(name, derivedMembers);
37             }
38             
39             alias derivedFromObject = staticFilter!(removeDerived, objectMembers);
40         }
41         
42         /// Filter unrelated symbols like constructors and Object methods
43         private template filterUtil(string name)
44         {
45             static if(name == "this")
46             {
47                 enum filterUtil = false;
48             } 
49             else
50             {
51                 static if(is(T == class))
52                 {
53                     enum filterUtil = staticIndexOf!(name, derivedFromObject!()) == -1;
54                 }
55                 else
56                 {
57                     enum filterUtil = true;
58                 }
59             }
60         }
61         
62         alias fieldsAndMethods = staticFilter!(filterUtil, __traits(allMembers, T));
63     }
64     else
65     {
66         alias fieldsAndMethods = ExpressionList!();
67     }
68 }
69 /// Example
70 unittest
71 {
72     struct A
73     {
74         string a;
75         float b;
76         void foo();
77         string bar(float);
78     }
79     
80     class B
81     {
82         string a;
83         float b;
84         void foo() {}
85         string bar(float) {return "";}
86     }
87     
88     class C
89     {
90         override string toString() const {return "";}
91     }
92     
93     static assert(staticEqual!(StrictExpressionList!(fieldsAndMethods!A), StrictExpressionList!("a", "b", "foo", "bar")));
94     static assert(staticEqual!(StrictExpressionList!(fieldsAndMethods!B), StrictExpressionList!("a", "b", "foo", "bar"))); 
95     static assert(staticEqual!(StrictExpressionList!(fieldsAndMethods!C), StrictExpressionList!("toString"))); 
96 }
97 
98 /// Checks if $(B T1) and $(B T2) have an operator $(B op): T1 op T2
99 template hasOp(T1, T2, string op)
100 {
101     static if(isIntegral!T1 && isIntegral!T2 && op == "/")
102     {
103         enum hasOp = true; // to not fall into 0 divizion
104     } else
105     {
106         enum hasOp = __traits(compiles, mixin("T1.init" ~ op ~ "T2.init"));
107     }
108 }
109 ///
110 unittest
111 {
112     static assert(hasOp!(float, int, "*"));
113     static assert(hasOp!(double, double, "/"));
114     static assert(!hasOp!(double, void, "/"));
115     static assert(hasOp!(int, int, "/"));
116     
117     struct B {}
118     
119     struct A
120     {
121         void opBinary(string op)(B b) if(op == "*") {}
122     }
123     
124     static assert(hasOp!(A, B, "*"));
125 }
126 
127 /// Shortcut for trait allMembers
128 template allMembers(T)
129 {
130     alias allMembers = ExpressionList!(__traits(allMembers, T));
131 }
132 
133 // hack to feed up parser a traits alias
134 private template Alias(alias T)
135 {
136     alias Alias = T;
137 }
138 
139 /// Shortcut for getMember
140 template getMember(T, string name)
141 {   
142     alias getMember = Alias!(__traits(getMember, T, name));
143 }