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 }