1 /**
2  * Proxy objects
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.meta.proxy;
15 
16 import std.traits;
17 
18 import ae.utils.meta : I;
19 import ae.utils.meta.caps;
20 import ae.utils.meta.reference;
21 
22 /// Mixes in an opDispatch that forwards to the specified target prefix.
23 mixin template StringMixinProxy(string targetPrefix)
24 {
25 	// from std.typecons.Proxy
26 	template opDispatch(string name)
27 	{
28 		static if (is(typeof(mixin(targetPrefix~name)) == function))
29 		{
30 			// non template function
31 			auto ref opDispatch(this X, Args...)(auto ref Args args) { return mixin(targetPrefix~name~q{(args)}); }
32 		}
33 		else static if (is(typeof({ enum x = mixin(targetPrefix~name); })))
34 		{
35 			// built-in type field, manifest constant, and static non-mutable field
36 			enum opDispatch = mixin(targetPrefix~name);
37 		}
38 		else static if (is(typeof(mixin(targetPrefix~name)))
39 		  || (is(typeof(__traits(getOverloads, __traits(parent, mixin(targetPrefix~name)), name)))
40 		             && __traits(getOverloads, __traits(parent, mixin(targetPrefix~name)), name).length != 0
41 		     )
42 		)
43 		{
44 			// field or property function
45 			@property auto ref opDispatch(this X)()                { return mixin(targetPrefix~name        ); }
46 			@property auto ref opDispatch(this X, V)(auto ref V v) { return mixin(targetPrefix~name~q{ = v}); }
47 		}
48 		else
49 		{
50 			// member template
51 			template opDispatch(T...)
52 			{
53 				enum targs = T.length ? "!T" : "";
54 				auto ref opDispatch(this X, Args...)(auto ref Args args){ return mixin(targetPrefix~name~targs~q{(args)}); }
55 			}
56 		}
57 	}
58 }
59 
60 /// Instantiates to a type that points to a named
61 /// sub-aggregate of a struct or class.
62 template SubProxy(alias S, string exp)
63 {
64 	alias RefType!S R;
65 
66 	struct SubProxy
67 	{
68 		R _subProxy;
69 
70 		this(R s) { _subProxy = s; }
71 
72 		mixin StringMixinProxy!(q{_subProxy..} ~ exp);
73 	}
74 }
75 
76 alias parentOf(alias a) = I!(__traits(parent, a));
77 
78 /// Returns a type that points to a sub-aggregate
79 /// (mixin or template alias) of a struct or class.
80 /// Requires __traits(child) support.
81 template scopeProxy(alias a)
82 {
83 	@property auto scopeProxy()
84 	{
85 		return ScopeProxy!a(this.reference);
86 	}
87 
88 	static @property auto scopeProxy(R)(R r)
89 	{
90 		return ScopeProxy!a(r);
91 	}
92 }
93 
94 template ScopeProxy(alias a)
95 {
96 	static assert(haveChildTrait, "Your compiler doesn't support __traits(child)");
97 
98 	alias parentOf!a S;
99 	alias RefType!S R;
100 
101 	struct ScopeProxy
102 	{
103 		R _scopeProxy;
104 
105 		this(R s) { _scopeProxy = s; }
106 
107 		mixin StringMixinProxy!q{__traits(child, _scopeProxy, a).};
108 	}
109 }
110 
111 static if (haveChildTrait && haveFieldAliasBinding)
112 unittest
113 {
114 	// Can't declare template at statement level
115 	static struct Dummy
116 	{
117 		static template T(alias a)
118 		{
119 			void set(int n)
120 			{
121 				a = n;
122 			}
123 		}
124 	}
125 
126 	static struct S
127 	{
128 		int i;
129 		alias t = Dummy.T!i;
130 
131 		auto getProxy() { return scopeProxy!t; }
132 	}
133 
134 	{
135 		S s;
136 		auto w = ScopeProxy!(S.t)(&s);
137 		w.set(42);
138 		assert(s.i == 42);
139 	}
140 
141 	{
142 		S s;
143 		auto w = scopeProxy!(S.t)(&s);
144 		w.set(42);
145 		assert(s.i == 42);
146 	}
147 
148 	{
149 		S s;
150 		auto w = s.getProxy();
151 		w.set(42);
152 		assert(s.i == 42);
153 	}
154 
155 	{
156 		S s;
157 		auto w = SubProxy!(S, "t.")(&s);
158 		w.set(42);
159 		assert(s.i == 42);
160 	}
161 }