Commands: add API to unbind and discard command arguments

this seems like an obvious functionality and basically harmless,
since commands are designed to be inherently stateful, which is reflected
in all the internal storage holders to expos an assignment operator
(even while the actual implementation is based on placement new instead
of assigning values into the storage, and thus even supports immutable
values). The only possible ramification is that argument values must
be default constructible
This commit is contained in:
Fischlurch 2017-04-16 19:21:29 +02:00
parent 410c36d2c3
commit bb7bba5dc2
8 changed files with 91 additions and 5 deletions

View file

@ -113,6 +113,13 @@ namespace wrapper {
return *this;
}
ReplaceableItem&
clear() noexcept
{
reAssign (NullValue<X>::get());
return *this;
}
operator X&() { return access(); }
operator X const&() const { return unConst(this)->access(); }
@ -174,8 +181,9 @@ namespace wrapper {
ReplaceableItem(X const& val) : val_(val) { }
ReplaceableItem(X && val) : val_(forward<X>(val)) { }
ReplaceableItem& operator= (X const& val) { val_=val; return *this; }
ReplaceableItem& operator= (X && val) { val_=forward<X>(val); return *this; }
ReplaceableItem& operator= (X const& val) { val_=val; return *this; }
ReplaceableItem& operator= (X && val) { val_=forward<X>(val); return *this; }
ReplaceableItem& clear() noexcept { val_=NullValue<X>::get(); return *this; }
operator X&() { return val_; }
operator X const&() const { return val_; }

View file

@ -35,7 +35,7 @@
** actual command operation function), which implements the invocation of the
** operation function with the stored argument tuple.
**
** \par Command Closure and Lifecycle
** ## 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
@ -50,7 +50,7 @@
** 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,
** Finally, when invoking the command, it passes a `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
@ -115,6 +115,7 @@ namespace control {
virtual bool equals (CmdClosure const&) const =0; ///< is equivalent to the given other closure?
virtual void bindArguments (Arguments&) =0; ///< store a set of parameter values within this closure
virtual void bindArguments (lib::diff::Rec const&) =0; ///< store a set of parameter values, passed as GenNode sequence
virtual void unbindArguments() =0; ///< discard any parameters and return to _unbound state_
virtual void invoke (CmdFunctor const&) =0; ///< invoke functor using the stored parameter values
virtual void accept (CommandImplCloneBuilder&) const =0; ///< assist with creating clone closure without disclosing concrete type
};

View file

@ -177,6 +177,12 @@ namespace control {
pClo_->bindArguments (paramData);
}
void
discardArguments()
{
pClo_->unbindArguments();
}
void invokeOperation() { do_(*pClo_); }
void invokeCapture() { undo_.captureState(*pClo_); }
void invokeUndo() { undo_(*pClo_); }

View file

@ -124,6 +124,13 @@ namespace control {
storeTuple (buildTuple<Args> (paramData));
}
/** discard any argument data and return to _empty_ state */
virtual void
unbindArguments() override
{
clearStorage();
}
virtual void
invoke (CmdFunctor const& func) override
@ -192,6 +199,12 @@ namespace control {
arguments_.template create<ArgHolder> (argTup);
}
void
clearStorage ()
{
arguments_.template create<ArgHolder>();
}
bool
equals (CmdClosure const& other) const override

View file

@ -131,6 +131,13 @@ namespace control {
storeTuple (buildTuple<Args> (paramData));
}
/** discard any argument data and return to _empty_ state */
virtual void
unbindArguments() override
{
clearStorage();
}
virtual void
invoke (CmdFunctor const& func) override
@ -208,6 +215,13 @@ namespace control {
arguments_.template create<ArgHolder> (argTup);
}
void
clearStorage ()
{
arguments_.template create<ArgHolder>();
memento_->clear();
}
typedef typename CommandSignature<SIG,MEM>::OperateSig SIG_op;
typedef typename CommandSignature<SIG,MEM>::CaptureSig SIG_cap;

View file

@ -285,6 +285,22 @@ namespace control {
}
/** discard any argument data previously bound.
* @remarks this returns the command into pristine state,
* just holding the definition but not any further
* argument data or UNDO state. Contrast this to an
* defalut constructed Command, which is inactive
* and not bound to any definition at all.
*/
Command&
Command::unbind()
{
___check_notBottom (this, "Un-binding arguments of");
_Handle::impl().discardArguments();
return *this;
}
/** @return the number of command \em definitions currently registered */
size_t
Command::definition_count()

View file

@ -36,7 +36,23 @@
** Command object used by client code is a small, copyable and ref-counting handle to this
** stored definition backend.
**
** //TODO maybe summarise how UNDO works?
** # Command definition, argument types and UNDO operation
** For a command to be usable at all, a concrete [command definition](command-def.hpp) need to be supplied
** somewhere in the code base. Typically this is done through static [command-setup bindings](command-setup.hpp).
** Such a command definition links three functions with the name-ID of the command
** - the actual command operation
** - a function to capture state
** - a function to undo the effect of the operation
** These functions may take arbitrary arguments, but the signature of this operation is captured at
** compile time and embedded as invisible type information within the implementation storage. While this
** allows to verify the type of the actual command arguments, this type sanity check can happen only late,
** at invocation time (and result in raising an exception). In a similar vein, the state capturing and
** the undo function must match with the signature of the main operation, since these functions are
** provided with the same arguments as presented to the command function. The UNDO functionality
** in Lumiera is based on capturing a _state memento_, not on applying a reverse function.
** So it is the responsibility of the capture function to take a suitable state snapshot,
** such as to be able to revert to the situation before activating this command, but
** only as far as the effect of the actual command function is concerned.
**
** @see command-def.hpp
** @see command-use1-test.cpp
@ -146,6 +162,8 @@ namespace control {
Command& bindArg (lib::diff::Rec const&);
Command& unbind();
ExecResult operator() () ;
ExecResult exec () ;

View file

@ -128,6 +128,16 @@ namespace control {
{ }
/** reverses the effect of capturing state
* and returns this memento holder into pristine state
*/
void
clear()
{
isCaptured_ = false;
memento_.clear();
}
/** bind the undo function to the internal memento store within this object.
* @return a functor, which, when invoked with the remaining arguments, will
* automatically call #getState() to retrieve the current memento value.