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 }