1 module painlesstraits;
2 
3 import std.traits;
4 
5 package alias Identity(alias A) = A;
6 
7 template hasAnnotation(alias f, alias Attr)
8 {
9 	import std.typetuple : anySatisfy, TypeTuple;
10 
11 	alias allAnnotations = TypeTuple!(__traits(getAttributes, f));
12 	template hasMatch(alias attr)
13 	{
14 		static if (is(Attr))
15 		{
16 			alias hasMatch = Identity!(is(typeof(attr) == Attr) || is(attr == Attr));
17 		}
18 		else
19 		{
20 			alias hasMatch = Identity!(is(attr == Attr));
21 		}
22 	}
23 
24 	enum bool hasAnnotation = anySatisfy!(hasMatch, allAnnotations);
25 }
26 
27 unittest
28 {
29 	enum FooUDA;
30 	enum BarUDA;
31 	@FooUDA int x;
32 
33 	static assert(hasAnnotation!(x, FooUDA));
34 	static assert(!hasAnnotation!(x, BarUDA));
35 }
36 
37 template hasAnyOfTheseAnnotations(alias f, Attr...)
38 {
39 	enum bool hasAnyOfTheseAnnotations = (function() {
40 			bool any = false;
41 			foreach (annotation; Attr)
42 			{
43 				any |= hasAnnotation!(f, annotation);
44 			}
45 			return any;
46 		})();
47 }
48 
49 version (unittest)
50 {
51 	// Some types are outside unittest block since old compilers couldn't find them otherwise
52 	// This might indicate brittleness of these functions but I'm not sure how to fix that
53 	enum AnyFooUDA;
54 	enum AnyBazUDA;
55 	enum AnyQuxUDA;
56 	struct AnyBarUDA
57 	{
58 		int data;
59 	}
60 
61 	@AnyFooUDA @(AnyBarUDA(1)) int anyx;
62 	@(AnyBarUDA(1)) int anyy;
63 	@AnyFooUDA int anyz;
64 }
65 
66 unittest
67 {
68 	static assert(!hasAnyOfTheseAnnotations!(anyy, AnyBazUDA, AnyQuxUDA));
69 	static assert(!hasAnyOfTheseAnnotations!(anyx, AnyBazUDA));
70 	static assert(!hasAnyOfTheseAnnotations!(anyx, AnyQuxUDA));
71 	static assert(hasAnyOfTheseAnnotations!(anyz, AnyFooUDA, AnyBarUDA));
72 	static assert(hasAnyOfTheseAnnotations!(anyx, AnyBarUDA, AnyQuxUDA));
73 }
74 
75 template hasValueAnnotation(alias f, alias Attr)
76 {
77 	import std.typetuple : anySatisfy, TypeTuple;
78 
79 	alias allAnnotations = TypeTuple!(__traits(getAttributes, f));
80 	alias hasMatch(alias attr) = Identity!(is(Attr) && is(typeof(attr) == Attr));
81 	enum bool hasValueAnnotation = anySatisfy!(hasMatch, allAnnotations);
82 }
83 
84 unittest
85 {
86 	enum FooUDA;
87 	struct BarUDA
88 	{
89 		int data;
90 	}
91 
92 	@FooUDA int x;
93 	@FooUDA @(BarUDA(1)) int y;
94 
95 	static assert(!hasValueAnnotation!(x, BarUDA));
96 	static assert(!hasValueAnnotation!(x, FooUDA));
97 	static assert(hasValueAnnotation!(y, BarUDA));
98 	static assert(!hasValueAnnotation!(y, FooUDA));
99 }
100 
101 template hasAnyOfTheseValueAnnotations(alias f, Attr...)
102 {
103 	enum bool hasAnyOfTheseValueAnnotations = (function() {
104 			bool any = false;
105 			foreach (annotation; Attr)
106 			{
107 				any |= hasValueAnnotation!(f, annotation);
108 			}
109 			return any;
110 		})();
111 }
112 
113 unittest
114 {
115 	static assert(!hasAnyOfTheseValueAnnotations!(anyz, AnyBarUDA));
116 	static assert(!hasAnyOfTheseValueAnnotations!(anyz, AnyBarUDA, AnyFooUDA));
117 	static assert(hasAnyOfTheseValueAnnotations!(anyx, AnyBarUDA));
118 	static assert(!hasAnyOfTheseValueAnnotations!(anyx, AnyFooUDA));
119 	static assert(hasAnyOfTheseValueAnnotations!(anyx, AnyFooUDA, AnyBarUDA));
120 	static assert(hasAnyOfTheseValueAnnotations!(anyy, AnyBarUDA, AnyFooUDA));
121 }
122 
123 template getAnnotation(alias f, Attr)
124 {
125 	static if (hasValueAnnotation!(f, Attr))
126 	{
127 		enum getAnnotation = (function() {
128 				foreach (attr; __traits(getAttributes, f))
129 					static if (is(typeof(attr) == Attr))
130 						return attr;
131 				assert(0);
132 			})();
133 	}
134 	else
135 		static assert(0);
136 }
137 
138 unittest
139 {
140 	static assert(getAnnotation!(anyy, AnyBarUDA).data == 1);
141 }
142 
143 template isField(alias T)
144 {
145 	enum isField = (function() {
146 			// isTemplate is relatively new only use it if it exists
147 			static if (__traits(compiles, __traits(isTemplate, T)))
148 				return (!isSomeFunction!(T) && !__traits(isTemplate, T));
149 			else
150 				return (!isSomeFunction!(T));
151 		})();
152 }
153 
154 unittest
155 {
156 	struct Foo
157 	{
158 		@property auto property()
159 		{
160 			return 1.0;
161 		}
162 
163 		auto func()
164 		{
165 			return 1.0;
166 		}
167 
168 		auto tmplt()()
169 		{
170 			return 1.0;
171 		}
172 
173 		auto field = 0;
174 	}
175 
176 	static assert(!isField!(Foo.property));
177 	static assert(!isField!(Foo.func));
178 
179 	static if (__traits(compiles, __traits(isTemplate, Foo.tmplt)))
180 	{
181 		static assert(!isField!(Foo.tmplt));
182 		static assert(!isSomeFunction!(Foo.tmplt));
183 	}
184 
185 	static assert(isField!(Foo.field));
186 
187 	// Make sure the struct behaves as expected
188 	Foo foo;
189 	assert(foo.property == 1.0);
190 	assert(foo.func() == 1.0);
191 	assert(foo.tmplt() == 1.0);
192 	assert(foo.field == 0.0);
193 }
194 
195 template isFieldOrProperty(alias T)
196 {
197 	enum isFieldOrProperty = (function() {
198 			static if (isField!(T))
199 			{
200 				return true;
201 			}
202 			else static if (isSomeFunction!(T))
203 			{
204 				return (functionAttributes!(T) & FunctionAttribute.property);
205 			}
206 			else
207 				return false;
208 		})();
209 }
210 
211 unittest
212 {
213 	struct Foo
214 	{
215 		int success;
216 		int failure(int x)
217 		{
218 			return x;
219 		}
220 	}
221 
222 	static assert(isFieldOrProperty!(Foo.success));
223 	static assert(!isFieldOrProperty!(Foo.failure));
224 
225 	// Make sure the struct behaves as expected
226 	Foo foo;
227 	assert(foo.failure(1) == 1);
228 }
229 
230 private template publicMemberFilter(T, alias filter)
231 {
232 	template Filter(alias memberName)
233 	{
234 		static if (is(T == class))
235 		{
236 			static if (is(typeof(__traits(getMember, T, memberName))))
237 			{
238 				static if (isSomeFunction!(__traits(getMember, T, memberName)))
239 				{
240 					static if (!__traits(compiles, {
241 							auto foo = __traits(getMember, T, memberName);
242 						}))
243 					{
244 						enum Filter = filter!(__traits(getMember, T, memberName));
245 					}
246 					else
247 						enum Filter = false;
248 				}
249 				else
250 				{
251 					static if (!__traits(compiles, {
252 							auto foo = &__traits(getMember, T, memberName);
253 						}))
254 					{
255 						enum Filter = filter!(__traits(getMember, T, memberName));
256 					}
257 					else
258 						enum Filter = false;
259 				}
260 			}
261 			else
262 				enum Filter = false;
263 		}
264 		else
265 		{
266 			static if (is(typeof(__traits(getMember, T.init, memberName))))
267 			{
268 				static if (__traits(compiles, {
269 						enum Foo = __traits(getMember, T.init, memberName);
270 					}))
271 				{
272 					enum Filter = filter!(__traits(getMember, T.init, memberName));
273 				}
274 				else
275 					enum Filter = false;
276 			}
277 			else
278 				enum Filter = false;
279 		}
280 	}
281 }
282 
283 // from std.meta to support older compilers
284 private template AliasSeq(TList...)
285 {
286 	alias AliasSeq = TList;
287 }
288 
289 private template Filter(alias pred, TList...)
290 {
291 	static if (TList.length == 0)
292 	{
293 		alias Filter = AliasSeq!();
294 	}
295 	else static if (TList.length == 1)
296 	{
297 		static if (pred!(TList[0]))
298 			alias Filter = AliasSeq!(TList[0]);
299 		else
300 			alias Filter = AliasSeq!();
301 	}
302 	else
303 	{
304 		alias Filter = AliasSeq!(Filter!(pred, TList[0 .. $ / 2]), Filter!(pred, TList[$ / 2 .. $]));
305 	}
306 }
307 
308 template allPublicFieldsOrProperties(T)
309 {
310 	enum allPublicFieldsOrProperties = Filter!(publicMemberFilter!(T,
311 				isFieldOrProperty).Filter, __traits(allMembers, T));
312 	static assert(allPublicFieldsOrProperties.length > 0, "No properties for type " ~ T.stringof);
313 }
314 
315 template allPublicFields(T)
316 {
317 	enum allPublicFields = Filter!(publicMemberFilter!(T, isField).Filter, __traits(allMembers, T));
318 	static assert(allPublicFields.length > 0, "No properties for type " ~ T.stringof);
319 }
320 
321 unittest
322 {
323 	import std.typecons : Tuple;
324 
325 	// toString is template, should be ignored
326 	static assert(!isFieldOrProperty!(Tuple!(int)(0).toString));
327 }