diff --git a/src/lib/replaceable-item.hpp b/src/lib/replaceable-item.hpp index aab736266..512abfbca 100644 --- a/src/lib/replaceable-item.hpp +++ b/src/lib/replaceable-item.hpp @@ -113,6 +113,13 @@ namespace wrapper { return *this; } + ReplaceableItem& + clear() noexcept + { + reAssign (NullValue::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(val)) { } - ReplaceableItem& operator= (X const& val) { val_=val; return *this; } - ReplaceableItem& operator= (X && val) { val_=forward(val); return *this; } + ReplaceableItem& operator= (X const& val) { val_=val; return *this; } + ReplaceableItem& operator= (X && val) { val_=forward(val); return *this; } + ReplaceableItem& clear() noexcept { val_=NullValue::get(); return *this; } operator X&() { return val_; } operator X const&() const { return val_; } diff --git a/src/proc/control/command-closure.hpp b/src/proc/control/command-closure.hpp index b5c520153..172ed5a4d 100644 --- a/src/proc/control/command-closure.hpp +++ b/src/proc/control/command-closure.hpp @@ -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 }; diff --git a/src/proc/control/command-impl.hpp b/src/proc/control/command-impl.hpp index d4499fefc..de1a13a98 100644 --- a/src/proc/control/command-impl.hpp +++ b/src/proc/control/command-impl.hpp @@ -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_); } diff --git a/src/proc/control/command-simple-closure.hpp b/src/proc/control/command-simple-closure.hpp index 764659d08..5b7e78d17 100644 --- a/src/proc/control/command-simple-closure.hpp +++ b/src/proc/control/command-simple-closure.hpp @@ -124,6 +124,13 @@ namespace control { storeTuple (buildTuple (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 (argTup); } + void + clearStorage () + { + arguments_.template create(); + } + bool equals (CmdClosure const& other) const override diff --git a/src/proc/control/command-storage-holder.hpp b/src/proc/control/command-storage-holder.hpp index d8528d0e1..7a3221bbf 100644 --- a/src/proc/control/command-storage-holder.hpp +++ b/src/proc/control/command-storage-holder.hpp @@ -131,6 +131,13 @@ namespace control { storeTuple (buildTuple (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 (argTup); } + void + clearStorage () + { + arguments_.template create(); + memento_->clear(); + } + typedef typename CommandSignature::OperateSig SIG_op; typedef typename CommandSignature::CaptureSig SIG_cap; diff --git a/src/proc/control/command.cpp b/src/proc/control/command.cpp index 7d826e82b..23bff8806 100644 --- a/src/proc/control/command.cpp +++ b/src/proc/control/command.cpp @@ -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() diff --git a/src/proc/control/command.hpp b/src/proc/control/command.hpp index 3f5134863..c888b27dc 100644 --- a/src/proc/control/command.hpp +++ b/src/proc/control/command.hpp @@ -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 () ; diff --git a/src/proc/control/memento-tie.hpp b/src/proc/control/memento-tie.hpp index bdbb8452a..91d619617 100644 --- a/src/proc/control/memento-tie.hpp +++ b/src/proc/control/memento-tie.hpp @@ -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.