1 module reduxed; 2 3 import mutex : DummyMutex; 4 5 enum Reduxed; 6 7 private string buildExecuteCallString(Args...)() { 8 import std.traits : isInstanceOf, moduleName; 9 import std.format : format; 10 import observable; 11 enum isStruct = is(Args[0].Type == struct); 12 enum isClass = is(Args[0].Type == class); 13 static if(isStruct || isClass) { 14 string ret = format("import %s;\n", moduleName!(Args[0].Type)); 15 } else { 16 string ret; 17 } 18 ret ~= "args[0] = Fun("; 19 size_t idx = 0; 20 static foreach(arg; Args) { 21 if(idx > 0) { 22 ret ~= ", "; 23 } 24 static if(isInstanceOf!(Observable, arg)) { 25 ret ~= format("cast(%s)args[%u]", arg.Type.stringof, idx); 26 } else { 27 ret ~= format("args[%u]", idx); 28 } 29 ++idx; 30 } 31 ret ~= ");\nargs[0].publish();"; 32 return ret; 33 } 34 35 private string buildMutexArray(Args...)() { 36 import std.traits : isInstanceOf; 37 import std.format : format; 38 import observable; 39 string ret = "import std.algorithm.sorting : sort;\n"; 40 ret ~= format("Mutex*[%d] mutexes;\n", Args.length); 41 ret ~= "size_t mutexIdx;\n"; 42 size_t idx = 0; 43 static foreach(arg; Args) {{ 44 static if(isInstanceOf!(Observable, arg)) { 45 ret ~= format("mutexes[mutexIdx++] = &(args[%u].mutex);\n", idx); 46 } 47 ++idx; 48 }} 49 ret ~= "sort(mutexes[0 .. mutexIdx]);\n"; 50 return ret; 51 } 52 53 private string indentString(size_t indent) pure { 54 string ret; 55 foreach(it; 0 .. indent) { 56 ret ~= "\t"; 57 } 58 return ret; 59 } 60 61 private string buildStructImpl(S, size_t indent)() { 62 import std.traits : isBasicType, hasUDA, fullyQualifiedName; 63 import std.format : format; 64 65 string ret; 66 if(indent) { 67 ret = indentString(indent) ~ format("struct __Reduxed%s {\n", S.stringof); 68 } 69 70 static assert(is(S == struct)); 71 foreach(mem; __traits(allMembers, S)) { 72 alias Type = typeof(__traits(getMember, S, mem)); 73 enum isStruct = is(Type == struct); 74 enum isClass = is(Type == class); 75 static if((isStruct || isClass) && hasUDA!(Type,Reduxed)) { 76 ret ~= buildStructImpl!(Type, indent + 1)(); 77 ret ~= indentString(indent + 1) ~ format("__Reduxed%s %s;\n", 78 Type.stringof, mem 79 ); 80 } else { 81 import std.traits : moduleName; 82 static if(!isBasicType!(Type)) { 83 ret ~= indentString(indent + 1) 84 ~ format("import %s;\n", 85 moduleName!(Type) 86 ); 87 } 88 ret ~= indentString(indent + 1) ~ format("Observable!(%s) %s;\n", 89 Type.stringof, mem 90 ); 91 } 92 } 93 94 if(indent) { 95 ret ~= indentString(indent) ~ "}\n"; 96 } 97 98 return ret; 99 } 100 101 private string buildStruct(S)() { 102 string ret = "struct Store {\n"; 103 ret ~= buildStructImpl!(S, 0); 104 105 ret ~= "}\n"; 106 ret ~= "Store store;\n"; 107 ret ~= "alias store this;\n"; 108 return ret; 109 } 110 111 struct Store(T,Mutex = DummyMutex) { 112 import observable; 113 114 pragma(msg, buildStruct!T()); 115 mixin(buildStruct!T()); 116 117 void execute(alias Fun, Args...)(ref Args args) { 118 pragma(msg, buildMutexArray!Args()); 119 pragma(msg, buildExecuteCallString!Args()); 120 mixin(buildMutexArray!Args()); 121 foreach(Mutex* mu; mutexes[0 .. mutexIdx]) { 122 mu.lock(); 123 } 124 pragma(msg, buildExecuteCallString!Args()); 125 mixin(buildExecuteCallString!Args()); 126 foreach(Mutex* mu; mutexes[0 .. mutexIdx]) { 127 mu.unlock(); 128 } 129 } 130 131 } 132 133 unittest { 134 import observable; 135 struct Bar { 136 Observable!float a; 137 Observable!float b; 138 } 139 140 struct Foo { 141 Observable!int a; 142 Observable!int b; 143 144 Bar bar; 145 } 146 147 struct Flux { 148 Foo foo; 149 } 150 151 void fun(ref const(float) f) @safe { 152 int a = 10; 153 } 154 155 Flux f; 156 157 f.foo.bar.a.subscribe(&fun); 158 } 159 160 unittest { 161 // Define your store as a struct 162 struct Bar { 163 int b; 164 int a; 165 } 166 167 // Create the store 168 Store!Bar store; 169 // set store init values 170 store.a = 1; 171 store.b = 2; 172 173 int a = 1337; 174 175 // We use this function to observe Bar.a 176 void fun(ref const(int) f) @safe { 177 a = f; 178 } 179 180 // Here we subscribe to Bar.a 181 // Whenever Bar.a changes "fun" gets called 182 store.a.subscribe(&fun); 183 184 // This function is used to change the value 185 static int increment(int a, int b) pure { 186 return a + b; 187 } 188 189 // Here we call increment with the values of store.a and store.b 190 // the effect is equal to 191 // store.a = increment(store.a, store.b); 192 store.execute!(increment)(store.a, store.b); 193 194 assert(store.a.value == 3); 195 assert(a == 3); 196 } 197