diff --git a/src/proc/control/command-mutation.hpp b/src/proc/control/command-mutation.hpp index 63a5019ec..6afe61137 100644 --- a/src/proc/control/command-mutation.hpp +++ b/src/proc/control/command-mutation.hpp @@ -168,6 +168,24 @@ namespace control { } + virtual Mutation& + close (Closure& cmdClosure) + { + REQUIRE (!memento_, "Lifecycle error: already closed over the arguments"); + REQUIRE (captureFunc_, "Param error: not bound to a valid function"); + + // create a special state closure, which can later on store the captured undo state (memento) + scoped_ptr stateClosure (new MementoClosure (captureFunc_)); + CmdFunctor closedCaptureFunc = stateClosure->bindArguments(captureFunc_); + + // the undoFunc (within parent class) will retrieve an argument tuple extended by the memento + Mutation::close (stateClosure->decorate (cmdClosure)); + + captureFunc_ = closedCaptureFunc; + memento_.swap(stateClosure); + return *this; + } + Mutation& captureState () { @@ -175,18 +193,14 @@ namespace control { throw lumiera::error::State ("need to bind function arguments prior to capturing undo state", LUMIERA_ERROR_UNBOUND_ARGUMENTS); - if (!memento_) // on first invocation we have to close the capture function - captureFunc_ = Mutation::getClosure().bindArguments(func_); - - memento_.reset (new MementoClosure (Mutation::getClosure(), invoke(captureFunc_) )) //////TODO verify exception safety! - - UNIMPLEMENTED ("how to make the inherited operator() use the extended Parameters (including the memento)???"); + invoke(captureFunc_); return *this; } CmdClosure& getMemento() { + ASSERT (memento_, "Lifecycle error: need to close first"); return *memento_; } diff --git a/tests/components/proc/control/command-mutation-test.cpp b/tests/components/proc/control/command-mutation-test.cpp index 79b54d723..ddfd59047 100644 --- a/tests/components/proc/control/command-mutation-test.cpp +++ b/tests/components/proc/control/command-mutation-test.cpp @@ -41,6 +41,7 @@ #include //#include #include +#include #include using std::tr1::bind; @@ -51,6 +52,7 @@ using std::tr1::function; //using lumiera::Time; //using util::contains; using std::string; +using std::rand; using std::cout; using std::endl; @@ -135,6 +137,8 @@ namespace test { functor.close(clo); ASSERT (functor); + cout << "param values: " << clo << endl; + testVal = 0; functor(); ASSERT (testVal == 23); @@ -202,6 +206,50 @@ namespace test { clonedFunc(); ASSERT (testVal == 42); } + + + void + checkStateCapturingClosure () + { + function undo_func = bind (&testFunc,_1); + function cap_func = bind (&capture ); + + MementoClosure memClo (cap_func); + CmdFunctor closed_cap_func = memClo.bindArguments (cap_func); + Tuple > param; + Closure clo (param); + cout << "plain param values: " << clo << endl; + + Closure extendedClo = memClo.decorate (clo); + cout << "params including memento storage: " << extendedClo << endl; + + CmdFunctor closed_undo_func = extendedClo.bindArguments (undo_func); + + VERIFY_ERROR (MISSING_MEMENTO, closed_undo_func() ); // invalid, because no state was captured + + int rr (rand() %100); + + testVal = rr; + closed_cap_func(); // invoke state capturing + + cout << "params including memento: " << memClo << endl; + cout << "captured memento state : " << extendedClo << endl; + + testVal = -10; // meanwhile "somehow" mutate the state + + closed_undo_func(); // invoking the undo() feeds back the memento + ASSERT (rr == testVal); // which is then restored into the state + + // this cycle can be repeated with different state values + rr = (rand() %100); + testVal = rr; + closed_cap_func(); // capture new state + cout << "params including memento: " << memClo << endl; + // ....note the changed memento! + testVal = -20; + closed_undo_func(); + ASSERT (rr == testVal); + } };