/* MEMENTO-TIE.hpp - capturing and providing state for undoing commands Copyright (C) Lumiera.org 2009, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file memento-tie.hpp ** A special binding used by Proc-Layer commands for capturing UNDO state information. ** The UndoMutation, which is the functor object created and configured by the Command ** for handling UNDO, utilises a MementoTie (as ctor parameter) for binding together ** the "undo capture function" and the actual "undo function", by retrieving the ** memento data or memento object from the former and feeding it to the latter ** as an additional parameter, when the undo operation is invoked. ** ** @see CmdClosure ** @see UndoMutation ** @see memento-tie-test.cpp ** */ #ifndef CONTROL_MEMENTO_TIE_H #define CONTROL_MEMENTO_TIE_H #include "lib/bool-checkable.hpp" #include "lib/meta/maybe-compare.hpp" #include "lib/meta/function-closure.hpp" #include "proc/control/command-signature.hpp" #include "lib/functor-util.hpp" #include "lib/format-obj.hpp" #include "lib/util.hpp" #include #include #include namespace proc { namespace control { using boost::equality_comparable; using lib::meta::func::bindLast; using lib::meta::func::chained; using lib::meta::equals_safeInvoke; LUMIERA_ERROR_DECLARE (MISSING_MEMENTO); ///< Undo functor not yet usable, because no undo state has been captured /** * Binding together state capturing and execution of the undo operation. * MementoTie itself is a passive container object with a very specific type, * depending on the type of the operation arguments and the type of the memento. * It is to be allocated within the StorageHolder of the command, thereby wrapping * the undo and capture function, setting up the necessary bindings and closures for * allowing them to cooperate behind the scenes to carry out the UNDO functionality. * On construction, the UndoMutation functor retrieves the wired up functions, * storing them into generic containers (type erasure) for later invocation. * * More specifically, the \c captureFunction, which is expected to run immediately prior * to the actual command operation, returns a \b memento value object (of unspecific type), * which needs to be stored within the MementoTie. On UNDO, the undo-operation functor needs * to be provided with a reference to this stored memento value through an additional * parameter (which by convention is always the last argument of the undo function). * @warning take care of the MementoTie storage location, as the bound functions * returned by #tieCaptureFunc and #tieUndoFunc refer to \c this internally. * * @param SIG signature of the command operation * @param MEM type of the memento state to capture. Needs to be default constructible and copyable */ template class MementoTie : public lib::BoolCheckable, equality_comparable > > { typedef typename CommandSignature::CaptureSig SIG_cap; typedef typename CommandSignature::UndoOp_Sig SIG_undo; MEM memento_; ///< storage holding the captured state for undo bool isCaptured_; function undo_; function capture_; /** to be chained behind the capture function */ void capture (MEM const& mementoVal) { memento_ = mementoVal; isCaptured_ = true; } public: MementoTie() : MementoTie (function(), function()) { } /** creates an execution context tying together the provided functions. * Bound copies of these functors may be pulled from the MementoTie, * in order to build the closures (with the concrete operation arguments) * to be invoked later on command execution. */ MementoTie (function const& undoFunc, function const& captureFunc) : memento_() , isCaptured_(false) , undo_(undoFunc) , capture_(captureFunc) { } /** 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. * @note similar to #getState(), the returned functor will throw * when the state capturing wasn't yet invoked */ function tieUndoFunc() { using std::bind; return bindLast( undo_ // getState() bound to last argument of undo(...) , bind (&MementoTie::getState, this) ); } /** bind the capturing function to the internal memento store within this object. * @return a functor, which on invocation will automatically store the return value * of the capturing function (= the current memento value) into the field * #memento_ within this object */ function tieCaptureFunc() { using std::placeholders::_1; function doCaptureMemento = bind (&MementoTie::capture, this, _1 ); return chained(capture_, doCaptureMemento); } /** access the currently captured memento state value * @throw when the capturing function wasn't yet invoked */ MEM& getState () { if (!isCaptured_) throw lumiera::error::State ("need to invoke memento state capturing beforehand", LUMIERA_ERROR_MISSING_MEMENTO); return memento_; } /** conversion to bool() yields true * if all functions are usable and * memento state has been captured */ bool isValid () const { return undo_ && capture_ && isCaptured_; } /** for diagnostics: include format-util.hpp */ operator std::string() const; /// Supporting equality comparisons... friend bool operator== (MementoTie const& m1, MementoTie const& m2) { return ((!m1.undo_ && !m2.undo_ && !m1.capture_ && !m2.capture_) // either no valid functions || ( util::rawComparison(m1.undo_, m2.undo_ ) // or identical functions && util::rawComparison(m1.capture_,m2.capture_ ) ) ) && (m1.isCaptured_ == m2.isCaptured_) // either both not captured or identical state && (!m1.isCaptured_ || equals_safeInvoke (m1.memento_, m2.memento_)); } }; template MementoTie::operator std::string() const { if (!undo_ || !capture_) return "·noUNDO·"; if (!isCaptured_) return ""; return ""; } }} // namespace proc::control #endif