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 *   Definition of expression lists - the base of meta-programming facilities of the library.
7 */
8 module stribog.meta.base;
9 
10 /**
11 *   Simple expression list wrapper.
12 *
13 *   See_Also: Expression list at dlang.org documentation.
14 */
15 template ExpressionList(T...)
16 {
17     alias ExpressionList = T;
18     
19     /// Pretty printing
20     template toString() {
21         private template innerToString(U...) {
22             static if(U.length == 0) {
23                 enum innerToString = "";
24             }
25             else static if(U.length == 1) {
26                 enum innerToString = staticToString!(U[0]);
27             }
28             else {
29                 enum innerToString = staticToString!(U[0]) ~ ", " ~ innerToString!(U[1 .. $]);
30             }
31         }
32         
33         static if(T.length == 0) {
34             enum toString = "StrictExpressionList!()";
35         }
36         else static if(T.length == 1) {
37             enum toString = "StrictExpressionList!(" ~ staticToString!(T[0]) ~ ")";
38         } 
39         else {
40             enum toString = "StrictExpressionList!(" ~ innerToString!T ~ ")";
41         }
42     }
43 }
44 /// Example
45 unittest
46 {
47     static assert([ExpressionList!(1, 2, 3)] == [1, 2, 3]);
48 }
49 
50 /**
51 *   Sometimes we don't want to auto expand expression ExpressionLists.
52 *   That can be used to pass several lists into templates without
53 *   breaking their boundaries.
54 */
55 template StrictExpressionList(T...)
56 {
57     /// Explicit expand
58     alias expand = T;
59     
60     /// Pretty printing
61     template toString() {
62         private template innerToString(U...) {
63             static if(U.length == 0) {
64                 enum innerToString = "";
65             }
66             else static if(U.length == 1) {
67                 enum innerToString = staticToString!(U[0]);
68             }
69             else {
70                 enum innerToString = staticToString!(U[0]) ~ ", " ~ innerToString!(U[1 .. $]);
71             }
72         }
73         
74         static if(T.length == 0) {
75             enum toString = "StrictExpressionList!()";
76         }
77         else static if(T.length == 1) {
78             enum toString = "StrictExpressionList!(" ~ staticToString!(T[0]) ~ ")";
79         } 
80         else {
81         	enum toString = "StrictExpressionList!(" ~ innerToString!T ~ ")";
82     	}
83     }
84 }
85 /// Example
86 unittest
87 {
88     template Test(alias T1, alias T2)
89     {
90         static assert([T1.expand] == [1, 2]);
91         static assert([T2.expand] == [3, 4]);
92         enum Test = true;
93     }
94     
95     static assert(Test!(StrictExpressionList!(1, 2), StrictExpressionList!(3, 4)));
96 }
97 
98 /**
99 *   Checks two expression lists to be equal. 
100 *   $(B ET1) and $(B ET2) should be wrapped to $(B StrictExpressionList).
101 *
102 *   Types are checked via $(B is) operator. Values are compared via $(B ==) operator.
103 *   Templates are checked via special inner template-member $(B opEquals) or via
104 *   ($B stringof) conversion (if no one from pair does not define $(B opEquals)). 
105 */
106 template staticEqual(alias ET1, alias ET2)
107 {
108     alias T1 = ET1.expand;
109     alias T2 = ET2.expand;
110     
111     static if(T1.length == 0 || T2.length == 0) // length isn't equal thus not equal
112     {
113         enum staticEqual = T1.length == T2.length;
114     }
115     else
116     {
117         static if(is(T1[0]) && is(T2[0])) // is types
118         {
119             enum staticEqual = is(T1[0] == T2[0]) && 
120                 staticEqual!(StrictExpressionList!(T1[1 .. $]), StrictExpressionList!(T2[1 .. $]));
121         } else static if(!is(T1[0]) && !is(T2[0])) // isn't types
122         {
123             static if(is(typeof(T1[0]) == void) && is(typeof(T2[0]) == void)) // check if both are templates
124             {
125                 static if(__traits(compiles, T1[0].opEquals!(T2[0]))) // first has opEquals
126                 	enum staticEqual = T1[0].opEquals!(T2[0]) &&
127                 		staticEqual!(StrictExpressionList!(T1[1 .. $]), StrictExpressionList!(T2[1 .. $]));
128         		else static if(__traits(compiles, T2[0].opEquals!(T1[0]))) // second has opEquals
129                     enum staticEqual = T2[0].opEquals!(T1[0]) &&
130                         staticEqual!(StrictExpressionList!(T1[1 .. $]), StrictExpressionList!(T2[1 .. $]));
131                 else // compare via strings
132                 	enum staticEqual = T1[0].stringof == (T2[0].stringof) &&
133                 		staticEqual!(StrictExpressionList!(T1[1 .. $]), StrictExpressionList!(T2[1 .. $]));
134     		}
135     		else // are values
136     		{
137                 enum staticEqual = T1[0] == T2[0] &&  
138                     staticEqual!(StrictExpressionList!(T1[1 .. $]), StrictExpressionList!(T2[1 .. $]));
139             }
140         } else // different kinds (one is type, another is template or value)
141         {
142             enum staticEqual = false;
143         }
144     }
145 }
146 /// Example
147 unittest
148 {
149     // trivial cases
150     static assert(staticEqual!(StrictExpressionList!(1, 2, 3), StrictExpressionList!(1, 2, 3)));
151     static assert(staticEqual!(StrictExpressionList!(int, float, 3), StrictExpressionList!(int, float, 3)));
152     static assert(!staticEqual!(StrictExpressionList!(int, float, 4), StrictExpressionList!(int, float, 3)));
153     static assert(!staticEqual!(StrictExpressionList!(void, float, 4), StrictExpressionList!(int, float, 4)));
154     static assert(!staticEqual!(StrictExpressionList!(1, 2, 3), StrictExpressionList!(1, void, 3)));
155     static assert(!staticEqual!(StrictExpressionList!(float), StrictExpressionList!()));
156     static assert(staticEqual!(StrictExpressionList!(), StrictExpressionList!()));
157     
158     // compare templates
159     template Dummy(T) {
160     }
161     static assert(staticEqual!(StrictExpressionList!(Dummy!int), StrictExpressionList!(Dummy!int)));
162     static assert(!staticEqual!(StrictExpressionList!(Dummy!int), StrictExpressionList!(Dummy!float)));
163     
164     // compare templates via opEquals
165     template DummyStrange(_T, U) {
166         alias T = _T; // re-export to outside
167         template opEquals(alias S) {
168             static if(__traits(compiles, S.T)) enum opEquals = is(T == S.T);
169             else enum opEquals = false;
170         }
171     }
172     static assert(staticEqual!(StrictExpressionList!(DummyStrange!(int, float)), StrictExpressionList!(DummyStrange!(int, ubyte))));
173     static assert(!staticEqual!(StrictExpressionList!(DummyStrange!(int, float)), StrictExpressionList!(DummyStrange!(bool, float))));
174 }
175 
176 /**
177 *   Converts to string template $(B T). 
178 *
179 *   If $(B T) defines inner template (or naked enum) $(B toString), then the member is used,
180 *   otherwise $(B T.stringof) is printed.
181 *
182 *   The template is helpful while debugging. DMD generates ugly mangled names for
183 *   templates after several transformations.
184 */
185 template staticToString(T...)
186 {
187     private template convertOne(alias U)
188     {
189         static if(__traits(compiles, U.staticToStringImpl!())) {
190             enum convertOne = U.staticToStringImpl!();
191         }
192         else static if(__traits(compiles, U.staticToStringImpl)) { // special case for naked enum
193             enum convertOne = U.staticToStringImpl;
194         }
195         else {
196             enum convertOne = U.stringof;
197         }
198     }
199     
200     static if(T.length == 0) {
201         enum staticToString = "";
202     }
203     else static if(T.length == 1) {
204         enum staticToString = convertOne!(T[0]);
205     } 
206     else {
207         enum staticToString = convertOne!(T[0]) ~ ", " ~ staticToString!(T[1 .. $]);
208     }
209 }
210 /// Example
211 unittest
212 {
213     template Dummy(T) {}
214     static assert(staticToString!(Dummy!int) == "Dummy!int");
215     
216     template DummyCustom1(T) {
217         template staticToStringImpl() {
218             enum staticToStringImpl = T.stringof;
219         }
220     }
221     static assert(staticToString!(DummyCustom1!int) == "int");
222     
223     template DummyCustom2(T) {
224         enum staticToStringImpl = T.stringof;
225     }
226     static assert(staticToString!(DummyCustom2!int) == "int");
227 }