From fd7337f8013529d2ea5d65eb80cff30fafce58dc Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 20 Sep 2009 18:37:20 +0200 Subject: [PATCH] WIP drastically simplify command-mutation. As we're using now a command frontend interface and and CommandImpl frame in the Registry, most of the functionaliry on Mutation's interface is superfluous. Additionally, this separates Mutation and Closure. --- src/proc/control/command-argument-holder.hpp | 7 +- src/proc/control/command-binding.hpp | 3 +- src/proc/control/command-closure.hpp | 29 ++++- src/proc/control/command-mutation.hpp | 105 +++--------------- .../proc/control/command-argument-test.cpp | 1 + .../proc/control/command-mutation-test.cpp | 52 ++++----- 6 files changed, 78 insertions(+), 119 deletions(-) diff --git a/src/proc/control/command-argument-holder.hpp b/src/proc/control/command-argument-holder.hpp index 189a8f84e..0d04a25df 100644 --- a/src/proc/control/command-argument-holder.hpp +++ b/src/proc/control/command-argument-holder.hpp @@ -107,7 +107,10 @@ namespace control { : public AcceptArgumentBinding< SIG // to derive the desired bind(..) signature , ArgumentHolder // target class providing the implementation , CmdClosure // base class to inherit from - > + > + +////////////////////////TODO: Ticket #266 +// : public CmdClosure { /** copy construction allowed(but no assignment)*/ ArgumentHolder& operator= (ArgumentHolder const&); @@ -202,7 +205,7 @@ namespace control { /** store a new argument tuple within this ArgumentHolder, - * discarding and previously stored arguments */ + * discarding any previously stored arguments */ void bindArg (ArgTuple const& argTup) { diff --git a/src/proc/control/command-binding.hpp b/src/proc/control/command-binding.hpp index 14532ecbb..2f278b33d 100644 --- a/src/proc/control/command-binding.hpp +++ b/src/proc/control/command-binding.hpp @@ -56,6 +56,7 @@ namespace com { ///< Proc-Layer command implementation details +////////////////////////TODO: Ticket #266 - can this be merged into argument-tuple-accept.hpp ?? /** @@ -63,7 +64,7 @@ namespace com { ///< Proc-Layer command implementation details * a set of \c bind(...) function for up to nine arbitrary arguments. * All these functions will package the argument values into a Tuple * (record) and forward the call to a \c bindArg(Tuple) function - * on the target class. The latter function is asumed to perform a + * on the target class. The latter function is assumed to perform a * run-time check to detect calls with invalid signature. */ template diff --git a/src/proc/control/command-closure.hpp b/src/proc/control/command-closure.hpp index fc3e70d0f..c1be0c4eb 100644 --- a/src/proc/control/command-closure.hpp +++ b/src/proc/control/command-closure.hpp @@ -34,10 +34,31 @@ ** internally contains an Closure instance (where SIG is the signature of the ** actual command operation function), which implements the invocation of the ** operation function with the stored argument tuple. - ** //TODO - ** + ** + ** \par Command Closure and Lifecycle + ** When defining a command, Mutation objects are to be created based on a concrete function. + ** These are stored embedded into a type erasure container, thus disposing the specific type + ** information of the function and function arguments. Each command needs an Mutation object + ** holding the command operation and an UndoMutation holding the undo functor. + ** + ** Later on, any command needs to be made ready for execution by binding it to a specific + ** execution environment, which especially includes the target objects to be mutated by the + ** command. Effectively, this means "closing" the Mutation (and UNDO) functor(s) with the + ** actual function arguments. These arguments are stored embedded within an ArgumentHolder, + ** which thereby acts as closure. Besides, the ArgumentHolder also has to accommodate for + ** storage holding the captured UNDO state (memento). Thus, internally the ArgumentHolder + ** has to keep track of the actual types, thus allowing to re-construct the concrete + ** function signature when closing the Mutation. + ** + ** Finally, when invoking the command, it passes a \c CmdClosure& to the Mutation object, + ** which allows the embedded function to be called with the concrete arguments. Besides + ** just invoking it, a command can also be used like a prototype object. To support this + ** use case it is possible to re-bind to a new set of command arguments, and to create + ** a clone copy of the argument (holder) without disclosing the actual types involved. + ** ** @see Command ** @see ProcDispatcher + ** @see command-argument-holder.hpp ** */ @@ -192,7 +213,7 @@ namespace control { typedef typename FunctionSignature< function >::Args Args; - typedef BuildTupleAccessor BuildAccessor; + typedef BuildTupleAccessor Accessor; typedef typename BuildAccessor::Accessor ParamStorageTuple; ParamStorageTuple params_; @@ -201,7 +222,7 @@ namespace control { typedef Tuple ArgTuple; Closure (ArgTuple const& args) - : params_(BuildAccessor(args)) + : params_(Accessor(args)) { } /** create a clone copy of this, without disclosing the exact type */ diff --git a/src/proc/control/command-mutation.hpp b/src/proc/control/command-mutation.hpp index 3238b44ce..163fbb6e1 100644 --- a/src/proc/control/command-mutation.hpp +++ b/src/proc/control/command-mutation.hpp @@ -28,22 +28,11 @@ ** and storing of a "memento" value behind the scenes. But towards Command, the ** Mutation acts as interface to conceal these details, as well as the actual ** type and parameters of the functions to be invoked. Thus, Mutation's - ** public interface just consists of a function call operator \c void() . + ** public interface just consists of a function call operator. ** - ** \par Lifecycle - ** Mutation objects are to be created based on a concrete function object, which then - ** gets embedded into a type erasure container, thus disposing the specific type information. - ** Moreover, building on this lib::OpaqueHolder yields a fixed size storage for Mutation objects, - ** allowing them to be embedded immediately within the CommandImpl frame. + ** Mutation objects are based on lib::OpaqueHolder, which yields a fixed size storage, + ** allowing them e.g. to be embedded immediately within the CommandImpl frame. ** - ** Later on, any command needs to be made ready for execution by binding it to a specific - ** execution environment, which especially includes the target objects to be mutated by the - ** command. This procedure includes "closing" the Mutation (and UNDO) functor(s) with the - ** actual function arguments. These arguments are stored embedded within an ArgumentHolder, - ** which thereby acts as closure. Besides, the ArgumentHolder also has to accommodate for - ** storage holding the captured UNDO state (memento). Thus, internally the ArgumentHolder - ** has to keep track of the actual types, thus allowing to re-construct the concrete - ** function signature when closing the Mutation. ** ** @see Command ** @see ProcDispatcher @@ -77,76 +66,35 @@ namespace control { /** * Unspecific command functor for implementing Proc-Layer Command. - * To be created from an tr1::function object, which later on - * can be \link #close closed \endlink with a set of actual - * function arguments. The concrete type of the function - * and the arguments is concealed (erased) on the interface, - * while the unclosed/closed - state of the functor can be - * checked by bool() conversion. + * To be created from an tr1::function object, which later on gets + * any necessary arguments from a closure passed in on invocation. + * The concrete type of the function and the arguments is + * concealed (erased) on the interface. */ class Mutation - : public lib::BoolCheckable { - CmdFunctor func_; - CmdClosure* clo_; + const CmdFunctor func_; public: template Mutation (function const& func) - : func_(func), - clo_(0) + : func_(func) { } - virtual ~Mutation() {} - - - virtual Mutation& - close (CmdClosure& cmdClosure) - { - ///////////////////////////////////////////////////////////////////////////////TODO we need a better approach; this breaks when re-binding - REQUIRE (!clo_, "Lifecycle error: already closed over the arguments"); - REQUIRE (func_, "Param error: not bound to a valid function"); - func_ = cmdClosure.closeArguments(func_); - clo_ = &cmdClosure; - return *this; - } void - operator() () + operator() (CmdClosure const& clo) { - if (!clo_) - throw lumiera::error::State ("Lifecycle error: function arguments not yet provided", + if (!clo) + throw lumiera::error::State ("Lifecycle error: function arguments not ready", LUMIERA_ERROR_UNBOUND_ARGUMENTS); - invoke (func_); + clo.invoke (func_); } - - /** diagnostics */ - operator string() const - { - return isValid()? "Mutation("+string (*clo_)+")" - : "Mutation(untied)"; - } - - virtual bool - isValid () const - { - return func_ && clo_; - } - - - private: - void - invoke (CmdFunctor & closedFunction) - { - closedFunction.getFun() (); - } }; - inline ostream& operator<< (ostream& os, Mutation const& muta) { return os << string(muta); } - @@ -164,7 +112,7 @@ namespace control { * and store the state (memento), which is implemented * by the specifically typed MementoTie object passed * in on construction. All these specific details - * are concealed on the interface + * are concealed (erased) on the interface */ class UndoMutation : public Mutation @@ -179,35 +127,18 @@ namespace control { { } - virtual Mutation& - close (CmdClosure& cmdClosure) - { - Mutation::close(cmdClosure); - captureMemento_.close(cmdClosure); - return *this; - } - - Mutation& - captureState () + captureState (CmdClosure const& clo) { - if (!Mutation::isValid()) - throw lumiera::error::State ("need to bind function arguments prior to capturing undo state", + if (!clo) + throw lumiera::error::State ("need additional function arguments to be able to capture UNDO state", LUMIERA_ERROR_UNBOUND_ARGUMENTS); - captureMemento_(); + captureMemento_(clo); return *this; } - private: - virtual bool - isValid () const - { - return Mutation::isValid() && captureMemento_; - } - - }; diff --git a/tests/components/proc/control/command-argument-test.cpp b/tests/components/proc/control/command-argument-test.cpp index ab7e8ab22..ec43dcb53 100644 --- a/tests/components/proc/control/command-argument-test.cpp +++ b/tests/components/proc/control/command-argument-test.cpp @@ -213,6 +213,7 @@ namespace test { for_each (tup, showIt); +////////////////////////TODO: Ticket #266 arg1->bind (); arg2->bind (rand() % 10); arg3->bind (rand() % 10, randTime()); diff --git a/tests/components/proc/control/command-mutation-test.cpp b/tests/components/proc/control/command-mutation-test.cpp index 39c88355c..f8f8877ad 100644 --- a/tests/components/proc/control/command-mutation-test.cpp +++ b/tests/components/proc/control/command-mutation-test.cpp @@ -24,6 +24,7 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" #include "proc/control/command-mutation.hpp" +#include "proc/control/command-argument-holder.hpp" #include "proc/control/memento-tie.hpp" #include "lib/meta/typelist.hpp" #include "lib/meta/tuple.hpp" @@ -91,9 +92,8 @@ namespace test { /** @test check the Mutation functor which is bound to our \c testFunc(int) . - * Then close this Mutation by providing a parameter tuple. - * Verify finally that by invoking the Mutation functor - * actually \c testFunc(param) is executed. + * Then create a argument closure and use this to invoke the Mutation + * and verify actually \c testFunc(param) is executed. */ void checkMutation () @@ -101,24 +101,25 @@ namespace test { function funky = testFunc; Mutation functor (funky); - ASSERT (!functor); - VERIFY_ERROR (UNBOUND_ARGUMENTS, functor() ); - cout << functor << endl; + MissingArguments nullClosure; + ASSERT (!nullClosure); + cout << "empty placeholder closure: " << nullClosure << endl; + VERIFY_ERROR (UNBOUND_ARGUMENTS, functor(nullClosure) ); + + // now create a real closure.... Tuple > param = tuple::make(23); Closure close_over (param); - CmdClosure& clo (close_over); - functor.close(clo); - ASSERT (functor); + CmdClosure& closure (close_over); + ASSERT (closure); - cout << "param values: " << clo << endl; - cout << functor << endl; + cout << "param values: " << closure << endl; testVal = 0; - functor(); + functor(closure); ASSERT (testVal == 23); - functor(); + functor(closure); ASSERT (testVal == 2*23); } @@ -148,48 +149,49 @@ namespace test { MemHolder mementoHolder (undo_func,cap_func); UndoMutation undoFunctor (mementoHolder); - ASSERT (!undoFunctor); ASSERT (!mementoHolder); - VERIFY_ERROR (UNBOUND_ARGUMENTS, undoFunctor() ); + + MissingArguments nullClosure; + VERIFY_ERROR (UNBOUND_ARGUMENTS, undoFunctor(nullClosure) ); + VERIFY_ERROR (UNBOUND_ARGUMENTS, undoFunctor.captureState(nullClosure) ); Tuple > param; Closure clo (param); undoFunctor.close(clo); - ASSERT ( undoFunctor); ASSERT (!mementoHolder); - VERIFY_ERROR (MISSING_MEMENTO, undoFunctor() ); + VERIFY_ERROR (MISSING_MEMENTO, undoFunctor (clo) ); VERIFY_ERROR (MISSING_MEMENTO, mementoHolder.getState() ); testVal = 11; - undoFunctor.captureState(); + undoFunctor.captureState(clo); ASSERT (mementoHolder); ASSERT (testVal == 11); int mem = mementoHolder.getState(); cout << "saved state: " << mem << endl; - undoFunctor(); + undoFunctor(clo); ASSERT (testVal == 11 + 11); - undoFunctor(); + undoFunctor(clo); ASSERT (testVal == 11 + 11 + 11); - undoFunctor.captureState(); + undoFunctor.captureState(clo); ASSERT (33 == mementoHolder.getState()); - undoFunctor(); + undoFunctor(clo); ASSERT (testVal == 33 + 33); testVal = 9; - undoFunctor(); + undoFunctor(clo); ASSERT (testVal == 42); UndoMutation clonedFunc (undoFunctor); // refers to the same state ASSERT (clonedFunc); ASSERT (33 == mementoHolder.getState()); - clonedFunc.captureState(); + clonedFunc.captureState(clo); ASSERT (42 == mementoHolder.getState()); // and captures into the same storage testVal = 0; - clonedFunc(); + clonedFunc(clo); ASSERT (testVal == 42); }