module context; import std.traits; class context (elementType) { private static elementType[] stack; private static elementType current; static auto opCall (callbackType) (callbackType f) if (isCallable!callbackType) { return opCall(elementType.init, f); } static auto opCall (callbackType) (elementType initValue, callbackType f) if (isCallable!callbackType) { enter(initValue); scope(exit) exit(); return f(); } static void enter () { enter(elementType.init); } static void enter (elementType initValue) { if (stack.length > 0) stack[$ - 1] = current; stack ~= initValue; current = stack[$ - 1]; } static exit () { stack = stack[0 .. $ - 1]; if (stack.length > 0) current = stack[$ - 1]; } @property static bool isSet () { return stack.length > 0; } } unittest { alias context!int myCounterContext; alias myCounterContext.current myCounter; myCounterContext(1, { myCounterContext(2, { myCounterContext(3, { assert(myCounter == 3); }); assert(myCounter == 2); }); assert(myCounter == 1); }); } unittest { alias context!int myCounterContext; alias myCounterContext.current myCounter; void f1 () { assert(myCounter == 1); } myCounterContext(1, { assert(myCounter == 1); f1(); }); } unittest { alias context!int myCounterContext; alias myCounterContext.current myCounter; myCounterContext({ assert(myCounter == 0); myCounter = 1; myCounterContext({ assert(myCounter == 0); myCounter = 2; myCounterContext({ assert(myCounter == 0); myCounter = 3; assert(myCounter == 3); }); myCounterContext({ assert(myCounter == 0); myCounter = 4; assert(myCounter == 4); }); assert(myCounter == 2); }); assert(myCounter == 1); }); } unittest { alias context!(Object) myCounterContext; alias myCounterContext.current myCounter; myCounterContext({ assert(myCounter is null); myCounterContext({ assert(myCounter is null); myCounter = new Object(); assert(myCounter !is null); }); assert(myCounter is null); myCounter = new Object(); myCounterContext({ assert(myCounter is null); myCounter = new Object(); assert(myCounter !is null); }); assert(myCounter !is null); }); } unittest { struct counterA { int value; alias value this; } struct counterB { int value; alias value this; } alias context!(counterA) myCounterAContext; alias myCounterAContext.current myCounterA; alias context!(counterB) myCounterBContext; alias myCounterBContext.current myCounterB; myCounterAContext({ myCounterA = 1; myCounterBContext({ myCounterB = 2; assert(myCounterA == 1); myCounterA = 3; }); assert(myCounterA == 3); }); }