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.
This commit is contained in:
Fischlurch 2009-09-20 18:37:20 +02:00
parent d15903f1db
commit fd7337f801
6 changed files with 78 additions and 119 deletions

View file

@ -107,7 +107,10 @@ namespace control {
: public AcceptArgumentBinding< SIG // to derive the desired bind(..) signature
, ArgumentHolder<SIG,MEM> // 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)
{

View file

@ -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<TYPES...>) 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<class TAR, class BA>

View file

@ -34,10 +34,31 @@
** internally contains an Closure<SIG> 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<SIG> >::Args Args;
typedef BuildTupleAccessor<Args,ParamAccessor> BuildAccessor;
typedef BuildTupleAccessor<Args,ParamAccessor> Accessor;
typedef typename BuildAccessor::Accessor ParamStorageTuple;
ParamStorageTuple params_;
@ -201,7 +222,7 @@ namespace control {
typedef Tuple<Args> ArgTuple;
Closure (ArgTuple const& args)
: params_(BuildAccessor(args))
: params_(Accessor(args))
{ }
/** create a clone copy of this, without disclosing the exact type */

View file

@ -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<Mutation>
{
CmdFunctor func_;
CmdClosure* clo_;
const CmdFunctor func_;
public:
template<typename SIG>
Mutation (function<SIG> 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<void()>() ();
}
};
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_;
}
};

View file

@ -213,6 +213,7 @@ namespace test {
for_each (tup, showIt);
////////////////////////TODO: Ticket #266
arg1->bind ();
arg2->bind (rand() % 10);
arg3->bind (rand() % 10, randTime());

View file

@ -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<void(int)> 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<Types<int> > param = tuple::make(23);
Closure<void(int)> 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<Types<> > param;
Closure<void()> 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);
}