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:
parent
410c36d2c3
commit
bb7bba5dc2
8 changed files with 91 additions and 5 deletions
|
|
@ -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_; }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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_); }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 () ;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue