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