/* COMMAND-STORAGE-HOLDER.hpp - specifically typed container for storage of command arguments 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 command-storage-holder.hpp ** A passive container record holding the actual command arguments & UNDO state. ** Effectively, this is the top level CmdClosure Implementation, which in turn ** delegates to sub-closures for the operation arguments and for UNDO management. ** While all command objects themselves have a common type (type erasure), ** the actual argument tuple and the state memento for UNDO can't. Especially, ** the size of arguments and memento will depend on their respective types. ** Thus, to manage somehow the storage of this data, we create a common holder, ** which can than be managed by a custom allocator / object pool. ** ** @see Command ** @see CmdClosure storage of command arguments ** @see MementoTie wiring of UNDO functions & memento ** @see UndoMutation execution of UNDO ** @see command-argument-test.cpp ** */ #ifndef CONTROL_COMMAND_STORAGE_HOLDER_H #define CONTROL_COMMAND_STORAGE_HOLDER_H #include "lib/typed-allocation-manager.hpp" #include "proc/control/command-op-closure.hpp" #include "proc/control/memento-tie.hpp" #include "proc/control/command-impl-clone-builder.hpp" #include "lib/opaque-holder.hpp" #include namespace proc { namespace control { using lib::InPlaceBuffer; using std::string; /** * This is "the" top level CmdClosure implementation. * It is a specifically typed CmdClosure, which serves for * actually allocating storage to hold the command arguments * and the UNDO state (memento) for Proc-Layer commands. * Both the contained components within StorageHolder * can be in \em empty state; there are no distinct * lifecycle limitations. StorageHolder is part * of Proc-Layer command's implementation * and should not be used standalone. */ template class StorageHolder : public CmdClosure { using ArgHolder = OpClosure; using MemHolder = MementoTie; using ArgumentBuff = InPlaceBuffer; using MementoBuff = InPlaceBuffer; using ArgTuple = typename ArgHolder::ArgTuple; using Args = typename lib::meta::RebindTySeq::Seq; // std::tuple to Types /* ====== in-place storage buffers ====== */ ArgumentBuff arguments_; MementoBuff memento_; /* ==== proxied CmdClosure interface ==== */ public: virtual bool isValid () const override { return arguments_->isValid(); } virtual bool isCaptured() const override { return memento_->isValid(); } /** assign a new parameter tuple to this */ virtual void bindArguments (Arguments& args) override { storeTuple (args.get()); } /** assign a new set of parameter values to this. * @note the values are passed packaged into a sequence * of GenNode elements. This is the usual way * arguments are passed from the UI-Bus */ virtual void bindArguments (lib::diff::Rec const& paramData) override { storeTuple (buildTuple (paramData)); } /** discard any argument data and return to _empty_ state */ virtual void unbindArguments() override { clearStorage(); } virtual void invoke (CmdFunctor const& func) override { if (!isValid()) throw lumiera::error::State ("Lifecycle error: can't bind functor, " "command arguments not yet provided", LUMIERA_ERROR_UNBOUND_ARGUMENTS); arguments_->invoke(func); } virtual operator string() const override { return "Command-State{ arguments=" + (arguments_->isValid()? string(*arguments_) : "unbound") + ", "+string(*memento_)+"}" ; } /** per default, all data within StorageHolder * is set up in \em empty state. Later on, the * command arguments are to be provided by #bind , * whereas the undo functions will be wired by #tie */ StorageHolder () : arguments_() , memento_() { } /** copy construction allowed(but no assignment). * @remarks rationale is to support immutable argument values, * which means default/copy construction is OK */ StorageHolder (StorageHolder const& oAh) : arguments_() , memento_() { if (oAh.arguments_->isValid()) // don't clone garbage from invalid arguments arguments_.template create (*oAh.arguments_); // memento can be cloned as-is, irrespective of activation state memento_.template create (*oAh.memento_); } /** copy construction allowed(but no assignment)*/ StorageHolder& operator= (StorageHolder const&) = delete; /** assist with creating a clone copy; * this results in invocation of the copy ctor */ void accept (CommandImplCloneBuilder& visitor) const override { visitor.buildCloneContext (*this); } /** has undo state capturing been invoked? */ bool canUndo () const { return memento_->isValid(); } bool empty () const { return !arguments_->isValid(); } /** store a new argument tuple within this StorageHolder, * discarding any previously stored arguments */ void storeTuple (ArgTuple const& argTup) { arguments_.template create (argTup); } void clearStorage () { arguments_.template create(); memento_->clear(); } typedef typename CommandSignature::OperateSig SIG_op; typedef typename CommandSignature::CaptureSig SIG_cap; typedef typename CommandSignature::UndoOp_Sig SIG_undo; /** create a new memento storage wiring, discarding existing memento state. * @note any bound undo/capture functions based on the previously held MementoTie * are silently invalidated; using them will likely cause memory corruption! */ MementoTie& tie (function const& undoFunc, function const& captureFunc) { return memento_.template create (undoFunc,captureFunc); } /** just re-access an existing memento storage wiring. * Used when cloning the closure */ MementoTie& getMementoWiring () { return *memento_; } /** direct "backdoor" access to stored memento value. * @throw LUMIERA_ERROR_MISSING_MEMENTO when invoked * prior to \c tie(..) and capturing any state */ MEM& memento () { return memento_->getState(); } bool equals (CmdClosure const& other) const { const StorageHolder* toCompare = dynamic_cast (&other); return (toCompare) and (*this == *toCompare); } /// Supporting equality comparisons... friend bool operator== (StorageHolder const& a1, StorageHolder const& a2) { return (a1.arguments_->isValid() == a2.arguments_->isValid()) && (*a1.arguments_ == *a2.arguments_) && (a1.memento_->isValid() == a2.memento_->isValid()) && (*a1.memento_ == *a2.memento_) ; } friend bool operator!= (StorageHolder const& a1, StorageHolder const& a2) { return not (a1 == a2); } }; }} // namespace proc::control #endif /*CONTROL_COMMAND_STORAGE_HOLDER_H*/