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