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:
parent
d15903f1db
commit
fd7337f801
6 changed files with 78 additions and 119 deletions
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -213,6 +213,7 @@ namespace test {
|
|||
|
||||
for_each (tup, showIt);
|
||||
|
||||
////////////////////////TODO: Ticket #266
|
||||
arg1->bind ();
|
||||
arg2->bind (rand() % 10);
|
||||
arg3->bind (rand() % 10, randTime());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue