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 <ae@cy.md>
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 /// Retrieves `__traits(parent, a)`.
77 alias parentOf(alias a) = I!(__traits(parent, a));
78 
79 /// Returns a type that points to a sub-aggregate
80 /// (mixin or template alias) of a struct or class.
81 /// Requires __traits(child) support.
82 template scopeProxy(alias a)
83 {
84 	@property auto scopeProxy()
85 	{
86 		return ScopeProxy!a(this.reference);
87 	}
88 
89 	static @property auto scopeProxy(R)(R r)
90 	{
91 		return ScopeProxy!a(r);
92 	}
93 }
94 
95 template ScopeProxy(alias a)
96 {
97 	static assert(haveChildTrait, "Your compiler doesn't support __traits(child)");
98 
99 	alias parentOf!a S;
100 	alias RefType!S R;
101 
102 	struct ScopeProxy
103 	{
104 		R _scopeProxy;
105 
106 		this(R s) { _scopeProxy = s; } ///
107 
108 		mixin StringMixinProxy!q{__traits(child, _scopeProxy, a).};
109 	}
110 }
111 
112 static if (haveChildTrait && haveFieldAliasBinding)
113 unittest
114 {
115 	// Can't declare template at statement level
116 	static struct Dummy
117 	{
118 		static template T(alias a)
119 		{
120 			void set(int n)
121 			{
122 				a = n;
123 			}
124 		}
125 	}
126 
127 	static struct S
128 	{
129 		int i;
130 		alias t = Dummy.T!i;
131 
132 		auto getProxy() { return scopeProxy!t; }
133 	}
134 
135 	{
136 		S s;
137 		auto w = ScopeProxy!(S.t)(&s);
138 		w.set(42);
139 		assert(s.i == 42);
140 	}
141 
142 	{
143 		S s;
144 		auto w = scopeProxy!(S.t)(&s);
145 		w.set(42);
146 		assert(s.i == 42);
147 	}
148 
149 	{
150 		S s;
151 		auto w = s.getProxy();
152 		w.set(42);
153 		assert(s.i == 42);
154 	}
155 
156 	{
157 		S s;
158 		auto w = SubProxy!(S, "t.")(&s);
159 		w.set(42);
160 		assert(s.i == 42);
161 	}
162 }